Add TNG writing and reading support
authorMagnus Lundborg <lundborg.magnus@gmail.com>
Tue, 1 Oct 2013 08:11:00 +0000 (10:11 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Mon, 20 Jan 2014 14:26:39 +0000 (15:26 +0100)
TNG is never read or written by default, but is available as
a trajectory format. The TNG repo is bundled in the GROMACS
source (commit 35827d2c19db8ad86690be32f33da7470abddf3d).

Tools such as trjconv, trjcat, gmxcheck and gmxdump have partial
support for TNG available. Full support for these tools (even only for
the subset of TNG features that GROMACS might want to use right now)
is currently difficult, because the code and data for existing
trajectory writing is a mess of excess functionality.  This will need
to be revisited when (at least) trjconv is broken up into more parts.

Writing with mdrun -x works (tests included)
Reading with mdrun -rerun works (tests included)
Reading and writing with trjconv works (tests included)
trjconv -n subsets work (tests included)

Reading works from e.g. dump, check, trjconv trjcat.
Writing works from e.g. mdrun trjconv, trjcat.
Some other tools seem to work as well, e.g., g_rms.

Reworked some integration test machinery to do more value-paramterized
tests.

Renamed status variable in gmx_trjconv.c to better reflect its usage
as an opaque trajectory-reading-management object.

Encapsulated gmx_mdoutf_t

Moved content of trajectory_writing_low_level.c to mdoutf.c

The Windows build with GMX_USE_TNG=ON is known to be broken with TNG
files.

Changed .mdp field and code variable names that were specific to XTC
to be more generic, now that there is more than one way to write a
trajectory file with lossy compression. Other associated documentation
updates also.

Change-Id: Ied156d8ad728711e08a0c61db5ee3472b0df39d5

205 files changed:
CMakeLists.txt
manual/algorithms.tex
share/html/links.dat
share/html/online/files.html
share/html/online/mdp.html
share/html/online/mdp_opt.html
share/html/online/tng.html [new file with mode: 0644]
src/config.h.cmakein
src/external/tng_io/AUTHORS [new file with mode: 0644]
src/external/tng_io/CMakeLists.txt [new file with mode: 0644]
src/external/tng_io/COPYING [new file with mode: 0644]
src/external/tng_io/Doxyfile.in [new file with mode: 0644]
src/external/tng_io/INSTALL [new file with mode: 0644]
src/external/tng_io/Trajectoryformatspecification.mk [new file with mode: 0644]
src/external/tng_io/example_files/tng_example.tng [new file with mode: 0644]
src/external/tng_io/include/compression/bwlzh.h [new file with mode: 0644]
src/external/tng_io/include/compression/bwt.h [new file with mode: 0644]
src/external/tng_io/include/compression/coder.h [new file with mode: 0644]
src/external/tng_io/include/compression/dict.h [new file with mode: 0644]
src/external/tng_io/include/compression/fixpoint.h [new file with mode: 0644]
src/external/tng_io/include/compression/huffman.h [new file with mode: 0644]
src/external/tng_io/include/compression/lz77.h [new file with mode: 0644]
src/external/tng_io/include/compression/merge_sort.h [new file with mode: 0644]
src/external/tng_io/include/compression/mtf.h [new file with mode: 0644]
src/external/tng_io/include/compression/my64bit.h [new file with mode: 0644]
src/external/tng_io/include/compression/rle.h [new file with mode: 0644]
src/external/tng_io/include/compression/tng_compress.h [new file with mode: 0644]
src/external/tng_io/include/compression/vals16.h [new file with mode: 0644]
src/external/tng_io/include/compression/warnmalloc.h [new file with mode: 0644]
src/external/tng_io/include/compression/widemuldiv.h [new file with mode: 0644]
src/external/tng_io/include/md5.h [new file with mode: 0644]
src/external/tng_io/include/tng_io.h [new file with mode: 0644]
src/external/tng_io/include/tng_io.hpp [new file with mode: 0644]
src/external/tng_io/include/tng_io_fwd.h [new file with mode: 0644]
src/external/tng_io/src/CMakeLists.txt [new file with mode: 0644]
src/external/tng_io/src/compression/CMakeLists.txt [new file with mode: 0644]
src/external/tng_io/src/compression/bwlzh.c [new file with mode: 0644]
src/external/tng_io/src/compression/bwt.c [new file with mode: 0644]
src/external/tng_io/src/compression/coder.c [new file with mode: 0644]
src/external/tng_io/src/compression/dict.c [new file with mode: 0644]
src/external/tng_io/src/compression/fixpoint.c [new file with mode: 0644]
src/external/tng_io/src/compression/huffman.c [new file with mode: 0644]
src/external/tng_io/src/compression/huffmem.c [new file with mode: 0644]
src/external/tng_io/src/compression/lz77.c [new file with mode: 0644]
src/external/tng_io/src/compression/merge_sort.c [new file with mode: 0644]
src/external/tng_io/src/compression/mtf.c [new file with mode: 0644]
src/external/tng_io/src/compression/rle.c [new file with mode: 0644]
src/external/tng_io/src/compression/tng_compress.c [new file with mode: 0644]
src/external/tng_io/src/compression/vals16.c [new file with mode: 0644]
src/external/tng_io/src/compression/warnmalloc.c [new file with mode: 0644]
src/external/tng_io/src/compression/widemuldiv.c [new file with mode: 0644]
src/external/tng_io/src/compression/xtc2.c [new file with mode: 0644]
src/external/tng_io/src/compression/xtc3.c [new file with mode: 0644]
src/external/tng_io/src/lib/CMakeLists.txt [new file with mode: 0644]
src/external/tng_io/src/lib/md5.c [new file with mode: 0644]
src/external/tng_io/src/lib/tng_io.c [new file with mode: 0644]
src/external/tng_io/src/lib/tng_io_fortran.c [new file with mode: 0644]
src/external/tng_io/src/tests/CMakeLists.txt [new file with mode: 0644]
src/external/tng_io/src/tests/compression/CMakeLists.txt [new file with mode: 0644]
src/external/tng_io/src/tests/compression/getfilesize.sh [new file with mode: 0755]
src/external/tng_io/src/tests/compression/test1.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test10.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test11.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test12.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test13.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test14.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test15.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test16.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test17.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test18.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test19.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test2.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test20.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test21.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test22.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test23.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test24.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test25.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test26.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test27.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test28.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test29.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test3.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test30.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test31.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test32.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test33.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test34.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test35.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test36.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test37.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test38.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test39.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test4.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test40.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test41.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test42.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test43.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test44.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test45.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test46.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test47.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test48.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test49.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test5.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test50.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test51.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test52.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test53.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test54.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test55.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test56.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test57.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test58.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test59.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test6.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test60.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test61.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test62.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test63.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test64.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test65.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test66.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test67.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test68.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test69.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test7.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test70.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test71.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test72.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test73.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test74.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test75.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test76.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test77.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test78.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test8.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test9.h [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test_tng_compress_read.bat [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test_tng_compress_read.sh [new file with mode: 0755]
src/external/tng_io/src/tests/compression/test_tng_compress_write.bat [new file with mode: 0644]
src/external/tng_io/src/tests/compression/test_tng_compress_write.sh [new file with mode: 0755]
src/external/tng_io/src/tests/compression/testsuite.c [new file with mode: 0644]
src/external/tng_io/src/tests/md_openmp.c [new file with mode: 0644]
src/external/tng_io/src/tests/md_openmp.f [new file with mode: 0644]
src/external/tng_io/src/tests/md_openmp_util.c [new file with mode: 0644]
src/external/tng_io/src/tests/tng_io_read_pos.c [new file with mode: 0644]
src/external/tng_io/src/tests/tng_io_read_pos_util.c [new file with mode: 0644]
src/external/tng_io/src/tests/tng_io_testing.c [new file with mode: 0644]
src/external/tng_io/src/tests/tng_parallel_read.c [new file with mode: 0644]
src/gromacs/CMakeLists.txt
src/gromacs/fileio/CMakeLists.txt
src/gromacs/fileio/filenm.c
src/gromacs/fileio/filenm.h
src/gromacs/fileio/gmxfio.c
src/gromacs/fileio/mdoutf.c
src/gromacs/fileio/mdoutf.h
src/gromacs/fileio/tests/CMakeLists.txt [new file with mode: 0644]
src/gromacs/fileio/tests/spc2-traj.tng [new file with mode: 0644]
src/gromacs/fileio/tests/tngio.cpp [new file with mode: 0644]
src/gromacs/fileio/tngio.cpp [new file with mode: 0644]
src/gromacs/fileio/tngio.h [new file with mode: 0644]
src/gromacs/fileio/tngio_for_tools.cpp [new file with mode: 0644]
src/gromacs/fileio/tngio_for_tools.h [new file with mode: 0644]
src/gromacs/fileio/tpxio.c
src/gromacs/fileio/trajectory_writing.c
src/gromacs/fileio/trajectory_writing.h
src/gromacs/fileio/trajectory_writing_low_level.c [deleted file]
src/gromacs/fileio/trxio.c
src/gromacs/fileio/trxio.h
src/gromacs/fileio/xtcio.c
src/gromacs/gmxana/gmx_clustsize.c
src/gromacs/gmxana/gmx_mindist.c
src/gromacs/gmxana/gmx_spatial.c
src/gromacs/gmxana/gmx_trjcat.c
src/gromacs/gmxana/gmx_trjconv.c
src/gromacs/gmxana/gmx_tune_pme.c
src/gromacs/gmxana/legacytests/gmx_traj_tests.cpp
src/gromacs/gmxana/legacytests/spc2-traj.tng [new file with mode: 0644]
src/gromacs/gmxana/legacytests/spc2.ndx [new file with mode: 0644]
src/gromacs/gmxlib/names.c
src/gromacs/gmxlib/txtdump.c
src/gromacs/gmxpreprocess/compute_io.c
src/gromacs/gmxpreprocess/readir.c
src/gromacs/legacyheaders/pme.h
src/gromacs/legacyheaders/sim_util.h
src/gromacs/legacyheaders/txtdump.h
src/gromacs/legacyheaders/types/inputrec.h
src/gromacs/legacyheaders/types/topology.h
src/gromacs/mdlib/minimize.c
src/gromacs/mdlib/sim_util.c
src/gromacs/tools/compare.c
src/gromacs/tools/dump.c
src/gromacs/tools/dump.h
src/programs/mdrun/md.c
src/programs/mdrun/mdrun.cpp
src/programs/mdrun/tests/CMakeLists.txt
src/programs/mdrun/tests/compressed_x_output.cpp [moved from src/programs/mdrun/tests/xtc_output.cpp with 81% similarity]
src/programs/mdrun/tests/moduletest.cpp
src/programs/mdrun/tests/moduletest.h
src/programs/mdrun/tests/rerun.cpp
src/programs/mdrun/tests/spc-and-methanol.gro [new file with mode: 0644]
src/programs/mdrun/tests/spc-and-methanol.ndx [new file with mode: 0644]
src/programs/mdrun/tests/spc-and-methanol.top [new file with mode: 0644]
src/programs/mdrun/tests/trajectory_writing.cpp [new file with mode: 0644]

index 87bab31b58c93721d0c96105844f31b653df1495..8613189318d87fa8c9a94b1cbe41592feb1c2a2e 100644 (file)
@@ -73,7 +73,7 @@ SET(CPACK_PACKAGE_VERSION_MINOR "0")
 
 # The numerical gromacs version. It is 40600 for 4.6.0.
 # The #define GMX_VERSION in gromacs/version.h is set to this value.
-math(EXPR NUM_VERSION 
+math(EXPR NUM_VERSION
     "${CPACK_PACKAGE_VERSION_MAJOR}*10000 + ${CPACK_PACKAGE_VERSION_MINOR}*100")
 if(CPACK_PACKAGE_VERSION_PATCH)
     math(EXPR NUM_VERSION
@@ -231,6 +231,8 @@ mark_as_advanced(GMX_LOAD_PLUGINS)
 option(GMX_GPU  "Enable GPU acceleration" ON)
 option(GMX_OPENMP "Enable OpenMP-based multithreading" ON)
 
+option(GMX_USE_TNG "Use the TNG library for trajectory I/O" ON)
+
 option(GMX_GIT_VERSION_INFO "Generate git version information" ${SOURCE_IS_GIT_REPOSITORY})
 mark_as_advanced(GMX_GIT_VERSION_INFO)
 
@@ -881,7 +883,7 @@ endif()
 if(NOT GMX_OPENMP)
     #Unset all OpenMP flags in case OpenMP was disabled either by the user
     #or because it was only partially detected (e.g. only for C but not C++ compiler)
-    unset(OpenMP_C_FLAGS CACHE) 
+    unset(OpenMP_C_FLAGS CACHE)
     unset(OpenMP_CXX_FLAGS CACHE)
 else()
     set(GMX_EXE_LINKER_FLAGS ${GMX_EXE_LINKER_FLAGS} ${OpenMP_LINKER_FLAGS})
@@ -948,6 +950,33 @@ else()
     endif ()
 endif()
 
+# TODO The Windows builds are still broken (we are avoiding some
+# unknown bug, perhaps with checkpointing?), because defaulting to
+# .tng output leads to calling null-op implementation functions, so
+# regressiontests will fail on Windows at the post-mdrun stages. We
+# can fix this during the 5.0 beta phase.
+gmx_add_cache_dependency(GMX_USE_TNG BOOL "NOT GMX_NATIVE_WINDOWS" OFF)
+
+if(GMX_USE_TNG)
+    find_package(ZLIB)
+    set(TNG_BUILD_FORTRAN OFF CACHE BOOL "Build Fortran compatible TNG library and examples for testing")
+    set(TNG_BUILD_EXAMPLES OFF CACHE BOOL "Build examples showing usage of the TNG API")
+    set(TNG_BUILD_COMPRESSION_TESTS OFF CACHE BOOL "Build tests of the TNG compression library")
+    # TODO reconsider use of OpenMP in TNG if/when trajectory writing is not serial in mdrun
+    set(TNG_USE_OPENMP ${GMX_OPENMP} CACHE BOOL "Try to use the OpenMP library (if available) with TNG")
+    set(TNG_BUILD_DOCUMENTATION OFF CACHE BOOL "Use Doxygen to create the HTML based TNG API documentation")
+    set(TNG_BUILD_TEST OFF CACHE BOOL "Build TNG testing binary.")
+    add_subdirectory(${CMAKE_SOURCE_DIR}/src/external/tng_io)
+    set(GMX_TNG_LIBRARIES tng_io)
+endif()
+mark_as_advanced(TNG_BUILD_FORTRAN)
+mark_as_advanced(TNG_BUILD_EXAMPLES)
+mark_as_advanced(TNG_BUILD_COMPRESSION_TESTS)
+mark_as_advanced(TNG_USE_OPENMP)
+mark_as_advanced(TNG_BUILD_DOCUMENTATION)
+mark_as_advanced(TNG_BUILD_TEST)
+mark_as_advanced(TNG_EXAMPLE_FILES_DIR)
+
 if (GMX_BUILD_FOR_COVERAGE)
     # Code heavy with asserts makes conditional coverage close to useless metric,
     # as by design most of the false branches are impossible to trigger in
index 36d59f7501671ea95af08b5745a13f9e61227333..6867876309e2a9701620ed0a82bb9da1bbc40627 100644 (file)
@@ -1,7 +1,7 @@
 %
 % This file is part of the GROMACS molecular simulation package.
 %
-% Copyright (c) 2013, by the GROMACS development team, led by
+% Copyright (c) 2013,2014, by the GROMACS development team, led by
 % Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 % and including many others, as listed in the AUTHORS file in the
 % top-level source directory and at http://www.gromacs.org.
@@ -326,11 +326,13 @@ systems) to prevent center of mass motion to occur. It makes sense to
 use the same groups for temperature coupling and center of mass motion
 removal.
 
-\item[\swapindex{XTC output}{group}]
-In order to reduce the size of the {\tt .xtc{\index{XTC}}} trajectory file, only a subset
-of all particles can be stored. All XTC groups that are specified
-are saved, the rest is not. If no XTC groups are specified, than all
-atoms are saved to the {\tt .xtc} file.
+\item[\swapindex{Compressed position output}{group}]
+
+In order to further reduce the size of the compressed trajectory file
+({\tt .xtc{\index{XTC}}} or {\tt .tng{\index{TNG}}}), it is possible
+to store only a subset of all particles. All x-compression groups that
+are specified are saved, the rest are not. If no such groups are
+specified, than all atoms are saved to the compressed trajectory file.
 
 \end{description}
 The use of groups in {\gromacs} tools is described in
@@ -3182,7 +3184,7 @@ but differing slightly between the three Born radii models.
 % LocalWords:  GROningen MAchine BIOSON Groningen GROMACS Berendsen der Spoel
 % LocalWords:  Drunen Comp Phys Comm ROck NS FFT pbc EM ifthenelse gmxlite ff
 % LocalWords:  octahedra triclinic Ewald PME PPPM trjconv xy solvated
-% LocalWords:  boxtypes boxshapes editconf Lennard mdpopt COM XTC kT defunits
+% LocalWords:  boxtypes boxshapes editconf Lennard mdpopt COM XTC TNG kT defunits
 % LocalWords:  Boltzmann's Mueller nb int mdrun chargegroup simplerc prefactor
 % LocalWords:  pme waterloops CH NH CO df com virial integrator Verlet vverlet
 % LocalWords:  integrators ref timepoint timestep timesteps mdp md vv avek NVE
@@ -3192,7 +3194,7 @@ but differing slightly between the three Born radii models.
 % LocalWords:  zy zz se barostat compressibilities MTTK NPT Martyna al isobaric
 % LocalWords:  Tuckerman vir PV fkT iLt iL Liouville NHC Eq baro mu trj mol bc
 % LocalWords:  freezegroup Shannon's polarizability Overhauser barostats iLn KE
-% LocalWords:  negligibly thermostatted Tobias  rhombic maxwell et xtc TC rlist
+% LocalWords:  negligibly thermostatted Tobias  rhombic maxwell et xtc tng TC rlist
 % LocalWords:  waals LINCS holonomic plincs lincs unc ang SA Langevin SD amu BD
 % LocalWords:  bfgs Broyden Goldfarb Shanno mkT kJ DFLEXIBLE Nocedal diag nmeig
 % LocalWords:  diagonalization anaeig nmens covanal ddg feia BT dp dq pV dV dA
index b70980b494456539f8a508c15ac8c37c4d808277..3b35abe5889efcbf55a66dca3c27e3e7fda6cb05 100644 (file)
@@ -19,6 +19,7 @@ pdb
 rtp
 tex
 top
+tng
 tpa
 tpb
 tpr
index 6a494aad822fff6c01aa0bb0494361e438d1e143..9be08f4729354894f249e69a547794bebb42af6c 100644 (file)
@@ -58,6 +58,7 @@ and velocities (binary)
 
 <br><dt><h3>Trajectory files</h3>
 <dd><dl compact>
+<dt><a href="tng.html">tng</a> <dd>Any kind of data (compressed, portable, any precision)
 <dt><a href="trj.html">trj</a> <dd>x, v and f (binary, full precision)
 <dt><a href="trr.html">trr</a> <dd>x, v and f (binary, full precision, portable)
 <dt><a href="xtc.html">xtc</a> <dd>x only (compressed, portable, any precision)
@@ -65,10 +66,12 @@ and velocities (binary)
 <dt><a href="gro.html">gro</a> <dd>x and v (ascii, any precision)
 <dt><a href="g96.html">g96</a> <dd>x only (ascii, fixed high precision)
 <dt><a href="pdb.html">pdb</a> <dd>x only (ascii, reduced precision)
-<dt><b>Full precision formats:</b>
+<dt><b>Formats for full-precision data:</b>
+<a href="tng.html">tng</a>,
 <a href="trr.html">trr</a> or
 <a href="trj.html">trj</a>
 <dt><b>Generic trajectory formats:</b>
+<a href="tng.html">tng</a>,
 <a href="xtc.html">xtc</a>,
 <a href="trr.html">trr</a>,
 <a href="trj.html">trj</a>,
index 4737a50a295842524011ab2f16519dd688c1bc26..ebbec487aefac7c710335f96193c9cee8a7813a2 100644 (file)
@@ -23,8 +23,8 @@ nstxout                  = 5000
 nstvout                  = 5000
 nstlog                   = 5000
 nstenergy                = 250
-nstxtcout                = 250
-xtc-grps                 = Protein
+nstxout-compressed       = 250
+compressed-x-grps        = Protein
 energygrps               = Protein  SOL
 nstlist                  = 10
 ns-type                  = grid
@@ -94,11 +94,11 @@ nstfout                  = 0
 nstlog                   = 5000
 nstenergy                = 250
 ; Output frequency and precision for xtc file = 
-nstxtcout                = 250
-xtc-precision            = 1000
+nstxout-compressed       = 250
+compressed-x-precision   = 1000
 ; This selects the subset of atoms for the xtc file. You can = 
 ; select multiple groups. By default all atoms will be written. = 
-xtc-grps                 = Protein
+compressed-x-grps        = Protein
 ; Selection of energy groups = 
 energygrps               = Protein  SOL
 
index 0678ad1976861e459f596803b5dc58368fb3ec7f..e63311dd48cf6c577bfa7ba57f72da3a02baa584 100644 (file)
@@ -20,7 +20,7 @@ IF YOU'RE NOT SURE ABOUT WHAT YOU'RE DOING, DON'T DO IT!
 <li><A HREF="#em"><b>energy minimization</b></A> (emtol, emstep, nstcgsteep)
 <li><a HREF="#shellmd"><b>shell molecular dynamics</b></a>(emtol,niter,fcstep)
 <li><a HREF="#tpi"><b>test particle insertion</b></a>(rtpi)
-<li><A HREF="#out"><b>output control</b></A> (nstxout, nstvout, nstfout, nstlog, nstcalcenergy, nstenergy, nstxtcout, xtc-precision, xtc-grps, energygrps)
+<li><A HREF="#out"><b>output control</b></A> (nstxout, nstvout, nstfout, nstlog, nstcalcenergy, nstenergy, nstxout-compressed, compressed-x-precision, compressed-x-grps, energygrps)
 <li><A HREF="#nl"><b>neighbor searching</b></A> (cutoff-scheme, nstlist, nstcalclr, ns-type, pbc, periodic-molecules, verlet-buffer-tolerance, rlist, rlistlong)
 <li><A HREF="#el"><b>electrostatics</b></A> (coulombtype, coulomb-modifier, rcoulomb-switch, rcoulomb, epsilon-r, epsilon-rf)
 <li><A HREF="#vdw"><b>VdW</b></A> (vdwtype, vdw-modifier, rvdw-switch, rvdw, DispCorr)
@@ -326,18 +326,18 @@ Try several values!</dd>
 <h3>Output control</h3>
 <dl>
 <dt><b>nstxout: (0) [steps]</b></dt>
-<dd>frequency to write coordinates to output 
+<dd>number of steps that elapse between writing coordinates to output
 <!--Idx-->trajectory file<!--EIdx-->, the last coordinates are always written</dd>
 <dt><b>nstvout: (0) [steps]</b></dt>
-<dd>frequency  to write velocities to output trajectory,
+<dd>number of steps that elapse between writing velocities to output trajectory,
 the last velocities are always written</dd>
 <dt><b>nstfout: (0) [steps]</b></dt>
-<dd>frequency to write forces to output trajectory.</dd>
+<dd>number of steps that elapse between writing forces to output trajectory.</dd>
 <dt><b>nstlog: (1000) [steps]</b></dt>
-<dd>frequency to write energies to <!--Idx-->log file<!--EIdx-->,
+<dd>number of steps that elapse between writing energies to the <!--Idx-->log file<!--EIdx-->,
 the last energies are always written</dd>
 <dt><b>nstcalcenergy: (100)</b></dt>
-<dd>frequency for calculating the energies, 0 is never.
+<dd>number of steps that elapse between calculating the energies, 0 is never.
 This option is only relevant with dynamics.
 With a twin-range cut-off setup <b>nstcalcenergy</b> should be equal to
 or a multiple of <b>nstlist</b>.
@@ -346,20 +346,20 @@ because calculating energies requires global communication between all
 processes which can become a bottleneck at high parallelization.
 </dd>
 <dt><b>nstenergy: (1000) [steps]</b></dt>
-<dd>frequency to write energies to energy file,
+<dd>number of steps that else between writing energies to energy file,
 the last energies are always written,
 should be a multiple of <b>nstcalcenergy</b>.
 Note that the exact sums and fluctuations over all MD steps
 modulo <b>nstcalcenergy</b> are stored in the energy file,
 so <tt>g_energy</tt> can report exact
 energy averages and fluctuations also when <b>nstenergy</b><tt>&gt;1</tt></dd>
-<dt><b>nstxtcout: (0) [steps]</b></dt>
-<dd>frequency to write coordinates to xtc trajectory</dd>
-<dt><b>xtc-precision: (1000) [real]</b></dt>
-<dd>precision to write to xtc trajectory</dd>
-<dt><b>xtc-grps:</b></dt>
-<dd>group(s) to write to xtc trajectory, default the whole system is written
-(if <b>nstxtcout</b> &gt; 0)</dd>
+<dt><b>nstxout-compressed: (0) [steps]</b></dt>
+<dd>number of steps that elapse between writing position coordinates using lossy compression</dd>
+<dt><b>compressed-x-precision: (1000) [real]</b></dt>
+<dd>precision with which to write to the compressed trajectory file</dd>
+<dt><b>compressed-x-grps:</b></dt>
+<dd>group(s) to write to the compressed trajectory file, by default the whole system is written
+(if <b>nstxout-compressed</b> &gt; 0)</dd>
 <dt><b>energygrps:</b></dt>
 <dd>group(s) to write to energy file</dd>
 </dl>
@@ -2118,7 +2118,7 @@ reals to your subroutine. Check the inputrec definition in
 <A HREF="#tc">nsttcouple</A><br>
 <A HREF="#out">nstvout</A><br>
 <A HREF="#out">nstxout</A><br>
-<A HREF="#out">nstxtcout</A><br>
+<A HREF="#out">nstxout-compressed</A><br>
 <A HREF="#expanded">nst-transition-matrix</A><br>
 <A HREF="#nl">ns-type</A><br>
 <A HREF="#wall">nwall</A><br>
@@ -2173,8 +2173,8 @@ reals to your subroutine. Check the inputrec definition in
 <A HREF="#vdw">vdwtype</A><br>
 <A HREF="#vdw">vdw-modifier</A><br>
 <A HREF="#nl">verlet-buffer-tolerance</A><br>
-<A HREF="#out">xtc-grps</A><br>
-<A HREF="#out">xtc-precision</A><br>
+<A HREF="#out">compressed-x-grps</A><br>
+<A HREF="#out">compressed-x-precision</A><br>
 <A HREF="#sa">zero-temp-time</A><br>
 <A HREF="#walls">wall-atomtype</A><br>
 <A HREF="#walls">wall-density</A><br>
diff --git a/share/html/online/tng.html b/share/html/online/tng.html
new file mode 100644 (file)
index 0000000..a6b6c48
--- /dev/null
@@ -0,0 +1,23 @@
+<title>tng file format</title>
+<h3>Description</h3>
+Files with the .tng file extension can contain all kinds of data
+related to the trajectory of a simulation. For example, it might
+contain coordinates, velocities, forces and/or energies. Various .mdp
+file options control which of these are written by mdrun, whether data
+is written with compression, and how lossy that compression can be.
+This file is in portable binary format an can be read
+with <a href="../programs/gmx-dump.html">gmx dump</a>.
+<PRE>
+% <a href="../programs/gmx-dump.html">gmx dump</a> -f traj.tng
+</PRE>
+or if you're not such a fast reader:
+<PRE>
+% gmxdump -f traj.tng | more
+</PRE>
+
+<p>
+You can also get a quick look in the contents of the file (number of 
+frames etc.) using:
+<PRE>
+% <a href="../programs/gmx-check.html">gmx check</a> -f traj.tng
+</PRE>
index d6ff665f8e6de213be4028482a681529d18d36ff..1804b63879e2956e0d6d22e95b061e278aa19534 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 /* Use our own instead of system XDR libraries */
 #cmakedefine GMX_INTERNAL_XDR
 
+/* Compile to use TNG library */
+#cmakedefine GMX_USE_TNG
+
 /* Use MPI (with mpicc) for parallelization */
 #cmakedefine GMX_LIB_MPI
 
diff --git a/src/external/tng_io/AUTHORS b/src/external/tng_io/AUTHORS
new file mode 100644 (file)
index 0000000..ff6aa32
--- /dev/null
@@ -0,0 +1,11 @@
+The TNG trajectory format is developed by:
+
+Magnus Lundborg <magnus.lundborg@scilifelab.se>
+Daniel Spångberg <daniels@mkem.uu.se>
+Rossen Apostolov <rossen@kth.se>
+
+The API is implemented mainly by:
+
+Magnus Lundborg
+Daniel Spångberg (TNG-MF1 compression)
+Anders Gärdenäs (C++ wrapper)
\ No newline at end of file
diff --git a/src/external/tng_io/CMakeLists.txt b/src/external/tng_io/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a0f917d
--- /dev/null
@@ -0,0 +1,57 @@
+cmake_minimum_required(VERSION 2.8)
+
+if(CMAKE_BUILD_TOOL MATCHES "(msdev|devenv|nmake)")
+    set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /W2")
+else()
+    set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
+endif()
+
+project(TNG_IO)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+
+option(BUILD_SHARED_LIBS "Enable shared libraries" ON)
+
+option(TNG_BUILD_FORTRAN "Build Fortran compatible library and examples for testing" OFF)
+
+option(TNG_BUILD_EXAMPLES "Build examples showing usage of the TNG API" ON)
+option(TNG_BUILD_TEST "Build TNG testing binary." ON)
+option(TNG_BUILD_COMPRESSION_TESTS "Build tests of the TNG compression library" OFF)
+
+option(TNG_USE_OPENMP "Try to use the OpenMP library (if available)" OFF)
+if(TNG_USE_OPENMP)
+  find_package(OpenMP)
+  if(OPENMP_FOUND)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
+  endif()
+endif()
+
+find_package(ZLIB)
+
+include(CheckIncludeFile)
+check_include_file(inttypes.h   HAVE_INTTYPES_H)
+
+add_subdirectory(src)
+
+install(FILES include/tng_io.h DESTINATION include/)
+
+#-- Add an Option to toggle the generation of the API documentation
+option(TNG_BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" OFF)
+if(TNG_BUILD_DOCUMENTATION)
+  find_package(Doxygen)
+  if (NOT DOXYGEN_FOUND)
+    message(FATAL_ERROR
+      "Doxygen is needed to build the documentation. Please install it correctly")
+  endif()
+  #-- Configure the Template Doxyfile for our specific project
+  configure_file(Doxyfile.in
+                 ${PROJECT_BINARY_DIR}/Doxyfile  @ONLY IMMEDIATE)
+  #-- Add a custom target to run Doxygen when ever the project is built
+  add_custom_target (Docs ALL
+                                        COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile
+                                        SOURCES ${PROJECT_BINARY_DIR}/Doxyfile)
+  # IF you do NOT want the documentation to be generated EVERY time you build the project
+  # then leave out the 'ALL' keyword from the above command.
+endif()
+
diff --git a/src/external/tng_io/COPYING b/src/external/tng_io/COPYING
new file mode 100644 (file)
index 0000000..4824ad9
--- /dev/null
@@ -0,0 +1,25 @@
+Copyright (c) 2012-2013, The GROMACS development team,
+check out http://www.gromacs.org for more information.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the GROMACS development team nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/src/external/tng_io/Doxyfile.in b/src/external/tng_io/Doxyfile.in
new file mode 100644 (file)
index 0000000..3d6f0f7
--- /dev/null
@@ -0,0 +1,1748 @@
+# Doxyfile 1.7.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = "TNG API"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = "1.4"
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "A flexible binary trajectory format"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @PROJECT_BINARY_DIR@/Documentation
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        = @PROJECT_SOURCE_DIR@/src/lib
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = @PROJECT_SOURCE_DIR@/src/lib @PROJECT_SOURCE_DIR@/include
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                = @PROJECT_SOURCE_DIR@/src/lib/md5.c @PROJECT_SOURCE_DIR@/include/md5.h @PROJECT_SOURCE_DIR@/include/tng_io.hpp @PROJECT_SOURCE_DIR@/src/lib/tng_io.c
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = *_
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# style sheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/src/external/tng_io/INSTALL b/src/external/tng_io/INSTALL
new file mode 100644 (file)
index 0000000..c7c3611
--- /dev/null
@@ -0,0 +1,21 @@
+mkdir build
+cd build
+cmake ..
+make
+
+
+Test by running:
+bin/tng_testing
+
+Make a system-wide installation by:
+make install
+(this will require administrative privileges on most systems, i.e. 'sudo make install'
+on a Unix like system with a sudoers file)
+
+
+Useful cmake flags:
+-DTNG_BUILD_DOCUMENTATION=ON to build the API documentation (requires doxygen)
+-DCMAKE_BUILD_TYPE=Debug to compile with debug flags (recommended for
+feedback during development)
+-DTNG_BUILD_FORTRAN=ON to build the Fortran MD simulations example, saving results
+in the TNG format (requires a Fortran compiler allowing cray-pointers).
\ No newline at end of file
diff --git a/src/external/tng_io/Trajectoryformatspecification.mk b/src/external/tng_io/Trajectoryformatspecification.mk
new file mode 100644 (file)
index 0000000..96059cf
--- /dev/null
@@ -0,0 +1,609 @@
+Trajectory format specification
+
+1.  Release notes
+    =============
+
+General notes:
+--------------
+
+\* Currently the API is lacking many data retrieval and getter
+functions. This will be fixed soon.
+
+\* It might be problematic searching for a specific time if frames are
+not consecutive. This problem is somewhat hypothetical, but might be
+good to point out.
+
+\* Currently we cannot track a specific molecule in a grand canonical
+ensemble.
+
+\* We should request comments on the specs from other groups - after v1.
+
+Erik's comments:
+
+\* Keep the number of calls small
+
+\* Use a prefix for the calls, e.g. tng\_open, tng\_close, etc.; use
+e.g. adv\_ prefix for advanced API
+
+\* Help routine to return the full list of atom types
+
+\* In general info:
+
+- number of stride pointers
+
+- for each pointer, the number of frame sets that it skips
+
+=\> have 3 stride pointers with defaults 1, 100 and 10,000 frame sets
+
+\* include zlib as a supported compressor
+
+To do:
+------
+
+\* Make a drawing
+
+\* Include API function for signing of the trajectory
+
+\* Sanity checklist:
+
+1.  Block header size
+2.  Block contents size
+3.  Compare hashes (currently doesn’t abort only prints warning)
+
+1.  maybe add a flag to choose between abort/warning
+2.  empty hash is always accepted
+
+4.  
+
+Version 0.98
+------------
+
+\* Use signed int instead of unsigned.
+
+\* Changed names from ‘trg’ to ‘tng’.
+
+Version 0.97
+------------
+
+\* Added chains and residues to the molecule description.
+
+Version 0.96
+------------
+
+\*Renamed “Trajectory Box Shape”, “Trajectory Positions” etc.
+
+\*Renamed “Trajectory Index Block” to “Particle Mapping Block”
+
+Version 0.95
+------------
+
+\* Changed name of ‘Atom name block’ to ‘Molecule block’ and included
+connectivity in that block. The number of each molecule is also
+specified there, unless using variable number of particles (grand
+canonical ensemble).
+
+\* Removed ‘variable number of particles’ and ‘variable number of
+values’ flags from data blocks.
+
+\* Updated the API and headers
+
+Version 0.9
+-----------
+
+\* Removed endianness/string block
+
+\* Added PGP signature to General Info block
+
+\* Removed reserved ‘user data blocks’. Those are already handled as
+normal ‘data blocks’. Reserved are only box, positions, velocities and
+forces.
+
+Version 0.8
+
+\* Use only MD5 hashes
+
+\* Changed time (in general info block) to 64 bit int.
+
+Version 0.7
+-----------
+
+\* New hierarchy rules
+
+\* Changed "Trajectory Info" to "General Info" block
+
+\* Moved "BOX SHAPE" before the index and trajectory frame blocks
+
+\* general, int., bin32, bin64 user data is explicitly not frame
+dependent
+
+\* Variable number of atoms will be supported in version 1
+
+\* Made more clear the nesting in the "trajectory group blocks"
+
+\* Allow header only files, i.e. no trajectory blocks
+
+\* Removed number of frames from the "index block" description
+
+\* Removed ASCII recommendation from the string description, i.e. [1]
+
+\* Added molecule ID to “atom name block”
+
+Version 0.6
+-----------
+
+Changed endianness block to endianness and string length block.
+
+Changed order of hash specifications in the header block.
+
+Added initial API specifications
+
+Version 0.5
+-----------
+
+Included an additional header block that specifies the endianness. It
+comes before all other blocks.
+
+2.  Specifications
+    ==============
+
+General specifications are given below. Others are described in the
+relevant block sections.
+
+1.  The file contains a number of blocks.
+2.  The order of the blocks follows the order specified in section 4
+    “Description of blocks”
+3.  All integers and floating point (floats and doubles) values are
+    stored using big endian byte ordering. Conversions to and from the
+    native format of the computer is performed during reading and
+    writing of numerical fields.
+4.  MD5 hashes are used to verify the integrity of the data.
+5.  Strings are limited to a max length of 1023 characters and are
+    terminated by a null character (‘\\000’). If longer text data must
+    be saved a data block containing multiple entries of general
+    (character/string) data can be used.
+6.  If a trajectory converter program encounters errors during reading a
+    block which format is not recognized, the block is to be written out
+    as binary object without modification.
+7.  Each group of blocks to contain a Table of Contents which lists the
+    included blocks within the group.^[[a]](#cmnt1)^
+8.  No compression (in ver.1) for general data streams such as integers,
+    floating point numbers, particle indices etc.
+
+3.  Each block contains the following fields (header):
+    ==================================================
+
+1.  64 bit size of the header
+2.  64 bit size of the block contents (except header)
+3.  64 bit block type identifier
+4.  16 characters MD5 Hash (or 16 “\\0” characters)
+5.  name[1]
+6.  64 bit version of the block ^[[b]](#cmnt2)^(allows addition of more
+    fields in the future to existing blocks, although old fields should
+    never be removed, to allow older readers read new files)
+
+4.  Description of blocks (each with a unique 64 bit identifier and a matching "name"):
+    ===================================================================================
+
+1.  info block (1) "GENERAL INFO" (required)
+2.  molecules block (2) "MOLECULES" (optional)
+3.  trajectory ids and names (4) “TRAJECTORY IDS AND NAMES” (optional)
+4.  trajectory frames, box shape block (10000) "BOX SHAPE" (optional,
+    can be present before the frame sets if it does not change or inside
+    the frame sets if it varies)
+5.  trajectory frame set block (5) "TRAJECTORY FRAME SET" (required)
+    (multiple “trajectory frame sets” are allowed)
+
+1.  trajectory table of contents block (6) “BLOCK TABLE OF CONTENTS”
+    (required)
+2.  trajectory frames, box shape block (10000) "BOX SHAPE" (optional,
+    can be present before the frame sets if it does not change or inside
+    the frame sets if it varies)
+3.  trajectory particle mapping block (7) "PARTICLE MAPPING" (required
+    if there are trajectory frames blocks) (multiple particle mapping
+    blocks with corresponding trajectory frames blocks are allowed, e.g.
+    to allow parallel writes of different atom sets)
+
+1.  trajectory frames, positions, block (10001) "POSITIONS" (optional)
+2.  trajectory frames, velocities, block (10002) "VELOCITIES" (optional)
+3.  trajectory frames, forces, block (10003) "FORCES" (optional)
+
+6.  ...other specified blocks, both non-trajectory and trajectory
+    blocks, each with unique id & name
+
+Data blocks can be used to store whatever data is needed. Data blocks
+with IDs in the range 10000 to 10999 are reserved for standard data
+(such as box shape, positions, velocities etc.), whereas IDs from 11000
+and above can be used for any kind of user data.
+
+5.  NOTES ABOUT BLOCK KINDS:
+    ========================
+
+There can be only one block at the beginning of the file in the standard
+case with fixed charges throughout the simulation (that’s the case for
+version 1 of the format). For simulations where charges vary each frame
+will include a block with the values.
+
+The trajectory frame blocks must be collected into frame sets, each
+
+such frame set has as its first block the "trajectory frame set block".
+Each frame set will contain (multiple) particle mapping blocks,
+positions, velocities, forces etc.^[[c]](#cmnt3)^
+
+6.  Requirements on block order:
+    ============================
+
+1.  The order follows section 4. “Description of blocks” with the
+    corresponding nesting of multiple “trajectory frame sets” and
+    “particle mapping blocks”.
+2.  All non-trajectory frame blocks (e.g. user ones) must appear before
+    the trajectory blocks
+3.  Trajectory particle mapping blocks are optional. If they are
+    present, they must appear before the corresponding trajectory data
+    blocks. If there are multiple trajectory data blocks, the
+    corresponding particle mapping blocks come right before them. I.e.
+    ParticleMappingBlock1-\>DataBlock1-\>P.MappingBlock2-\>DataBlock2.
+4.  Blocks within a group of blocks are ordered by their ID.
+
+7.  Other requirements:
+    ===================
+
+1.  Most blocks are optional except for the “general info” blocks
+2.  No limit on the number of times that trajectory related blocks are
+    allowed to appear
+
+8.  Specification of the block contents (all blocks have the same header as described above) for version 1 of each block type.
+    ==========================================================================================================================
+
+BLOCK: general info block
+-------------------------
+
+1.  name and version of the program used to perform the simulation (upon
+    file creation)[1]
+2.  name and version of the program used when finishing the file[1]
+3.  name of the force field used to perform the simulation
+    [1]^[[d]](#cmnt4)^
+4.  name of the person who created the file [1]
+5.  name of the person who last modified the file [1]
+6.  64 bit time of initial file creation, seconds since 1970
+7.  64 bit time of completing the simulation, seconds since 1970
+8.  name of computer/other info where the file was created [1]
+9.  name of computer/other info where the file was completed [1]
+10. PGP signature (optional and 0 terminated string)
+11. 8 bit flag Use variable number of atoms.
+12. 64 bit number of frames in each frame set (this is the expected
+    number of frames in each set, but it does not have to be constant,
+    it is OK to have frame sets with fewer or more frames, e.g. after
+    concatenating multiple trajectory files. This avoids the need to
+    recompress all data after a concatenation, but it means that
+    searching for a specific frame might need a few more steps between
+    frame sets.). For simulations using a grand canonical ensemble it is
+    best to set this to 1 so that the number of atoms in the frame sets
+    can be updated regularly.
+13. 64 bit pointer from the beginning of the info block to the beginning
+    of the first trajectory frame set [2]
+14. 64 bit pointer from the beginning of the info block to the beginning
+    of the last trajectory frame set [2] (updated when finishing writing
+    the trajectory file - otherwise set to -1)
+15. 64 bit length of steps (number of “trajectory frame set blocks”) for
+    long stride pointers (default 100 “trajectory frame set blocks”).
+
+BLOCK: molecules block (optional)
+---------------------------------
+
+1.  64 bit number of molecules
+2.  For each molecule:
+
+1.  64 bit Molecule ID
+2.  Molecule name [1]
+3.  64 bit quaternary structure, e.g. 1 means monomeric, 4 means
+    tetrameric etc.
+4.  64 bit number of molecules of this kind - only if not using
+    “variable number of atoms” in the “general info block”.
+5.  64 bit number of chains in the molecule
+6.  64 bit number of residues in the molecule
+7.  64 bit number of atoms in the molecule^[[e]](#cmnt5)^
+8.  For each chain:
+
+1.  64 bit Chain ID (unique in molecule)
+2.  Chain name [1]
+3.  64 bit number of residues in the chain
+4.  For each residue:
+
+1.  64 bit Residue ID (unique in the chain)
+2.  Residue name [1]
+3.  64 bit number of atoms in the residue
+4.  For each atom:
+
+1.  64 bit Atom ID (unique in the molecule)
+2.  Atom name [1]
+3.  Atom type [1]
+
+9.  64 bit number of bonds in the molecule
+10. For each bond:
+
+5.  64 bit integer From Atom ID.
+6.  64 bit integer To Atom ID.
+
+BLOCK: trajectory frame set block
+---------------------------------
+
+1.  64 bit number of first frame (zero based numbering)
+2.  64 bit number of frames (NF)
+3.  Array of 64 bit integers specifying the count of each molecule type.
+    The molecule types are listed in the “Atom names block” and should
+    be listed in the same order here. This should only be present when
+    the variable number of atoms flag in the “General info block” is set
+    to TRUE. This is used for e.g. simulations using a grand canonical
+    ensemble (in which case the number of frames in each frame set
+    should be 1).
+4.  64 bit pointer to the next “trajectory frame set block”.
+5.  64 bit pointer the previous “trajectory frame set block”.
+6.  64 bit long stride pointer to the next e.g. 100th “trajectory frame
+    set block”. (Stride length specified in “general info” block.)
+7.  64 bit long stride pointer to the previous e.g. 100th “trajectory
+    frame set block”.
+
+BLOCK: trajectory table of contents
+-----------------------------------
+
+1.  64 bit number of blocks
+
+Contains a listing of all data blocks \_present\_ in the frame set. It
+is possible to have multiple blocks with the same ID, but the ID is only
+listed once in the “trajectory table of contents” block.
+
+It includes for each block type:
+
+1.  Block name [1]
+
+BLOCK: data blocks
+------------------
+
+Frame dependent data blocks should come after the frame set block to
+which it belongs. Frame and particle dependent data blocks should come
+after the relevant particle mapping block (if using any particle mapping
+block).
+
+1.  Char data type flag. 0 = character/string data, 1 = 64 bit integer
+    data, 2 = float data (32 bit), 3 = double data (64 bit)
+2.  Char dependency flag. 1 = frame dependent, 2 = particle dependent.
+    Can be combined, i.e. 3 = frame and particle dependent.
+3.  Char sparse data flag to signify if not all frames in the frame sets
+    have data entries in this data block, e.g. energies and positions
+    might be saved at different intervals meaning that at least one of
+    them would be saved as sparse data. Only present if the data is
+    frame dependent.
+4.  64 bit number of values.
+5.  64 bit id of the CODEC used to store the positions
+6.  Double (64 bit) multiplier for integers to obtain the appropriate
+    floating point number, for compressed frames [3] [\*\*] (only
+    present if the above CODEC id is \> 0 and if the data type is double
+    or float)
+
+If using sparse data the following fields are required:
+
+1.  64 bit number of first frame containing data.
+2.  64 bit number of frames between data points
+
+Particle dependent data blocks contain the following fields:
+
+1.  64 bit number of first particle as stored in the trajectory, zero
+    based numbering) (J), this must be the same as in the preceding
+    trajectory particle mapping block, if present.
+
+1.  64 bit number of particles in block, this must be the same as in the
+    preceding trajectory particle mapping block, if present.
+
+Example 1:
+
+Box shape block (10000) in a frame set with frames 0-99:
+
+1.  Data type: 3 (double)
+2.  Dependency: 1 (frame dependent)
+3.  Sparse data: 1
+4.  Number of values: 9
+5.  Codec ID: 0
+6.  First frame containing data: 0
+7.  Number of frames between data points: 50
+8.  For each frame (2 frames with data in this block):
+
+1.  9 double (64 bit) values describing the shape of the block
+
+Example 2:
+
+Positions block (ID 10001) in a frame set with frames 1000-1099:
+
+1.  Data type: 2 (float)
+2.  Dependency: 3 (frame and particle dependent)
+3.  Sparse data: 1
+4.  Number of values: 3 (x, y and z)
+5.  Coded ID: 0
+6.  First frame containing data: 100
+7.  Number of frames between data points: 10
+8.  Number of first particle: 0
+9.  Number of particles in block: 1000
+10. For each frame (10 frames with data in this block):
+
+1.  For each particle (1000 particles):
+
+1.  32 bit float x coordinate
+2.  32 bit float y coordinate
+3.  32 bit float z coordinate
+
+Example 3:
+
+Forces block (ID 10003) in a frame set with frames 0-99:
+
+11. Data type: 2 (float)
+12. Dependency: 3 (frame and particle dependent)
+13. Sparse data: 0
+14. Number of values: 3 (x, y and z)
+15. Coded ID: 0
+16. Number of first particle: 0
+17. Number of particles in block: 100
+18. For each frame (100 frames with data in this block):
+
+2.  For each particle (100 particles):
+
+1.  32 bit float x coordinate
+2.  32 bit float y coordinate
+3.  32 bit float z coordinate
+
+BLOCK: particle mapping block
+-----------------------------
+
+1.  64 bit number of first particle (particle number as stored in the
+    trajectory, zero based numbering) (J)
+2.  64 bit number of particles in this particle mapping block (M)
+3.  64 bit array of particle numbers^[[f]](#cmnt6)^ (M values):
+
+1.  Each value is the number of the real particle corresponding to the
+    particle number as stored in the trajectory.
+
+Should no particle mapping block be present, the mapping is the number
+
+of the real particle == the particle number as stored in the trajectory.
+
+It is possible to have several trajectory/velocities etc. frame blocks
+within a frame set, e.g. when faster parallel writes or memory
+considerations are needed. In that case a separate particle mapping
+block is needed for each of the trajectory/velocities etc. blocks.
+
+Relation between trajectory blocks:
+===================================
+
+Particle mapping blocks contain the remapping of actual particle index
+and the particle index as appearing in the trajectory file. They are
+optional. If they are not given, there is no remapping. All trajectory
+blocks for the same set of particles must follow each other, i.e.
+positions for particle 0-99, then velocities for particle 0-99, then
+positions for particle 100-199, then velocities for particle 100-199.
+All non-particle trajectory blocks must appear before any particle
+containing trajectory blocks.
+
+Limitations on the number of particles in trajectory frame blocks: In
+order to be able to read and uncompress data there must be a limit on
+the number of particles in each trajectory frame block, therefore most
+trajectory frame sets will contain multiple particle mapping / positions
+/ velocities / ... blocks. The limit on the number of particles per
+trajectory frame blocks should be XXXX. This should be a good value, and
+not allowed to be set by the user, since this may prevent reading of the
+files on smaller memory machines.
+
+CODEC specifications (id) "name"
+================================
+
+1.  uncompressed (0) "UNCOMPRESSED"
+2.  XTC positions (1) "XTC"
+3.  TNG (2) "TNG"
+4.  …
+
+[](#)
+
+[\*\*] Storage of compressed positions / velocities / ...: These are now
+all converted to integers before stored. In order to facilitate
+recompression without loss of precision it is essential that these are
+visible as integers. Therefore the compression blocks all must contain
+somewhere a conversion factor from integer to float.
+
+Notes
+=====
+
+[1] UTF-8 text string. Make all text strings zero terminated.
+
+[2] 64 bit pointer format: -1UL (all ones), means "not set", which is
+what should be written whenever a pointer needs to be written when the
+appropriate value is not yet known, while 0 (all zeros), typically means
+the end of the list.
+
+[3] Floating point format is big-endian IEEE-754, float (32 bit) or
+double (64 bit).
+
+API
+===
+
+(The API should be separated into one high- and one low-level API, using
+e.g. a tng\_low tag for the low-level functions.)
+
+API documentation is generated using the -DTNG_BUILD_DOCUMENTATION=ON option
+when running cmake. Requires a doxygen installation.
+
+
+^[[g]](#cmnt7)^
+
+[[a]](#cmnt_ref1)magnus.lundborg:
+
+Currently sizes and offsets are not in the TOC block. I think it needs
+further testing to decide if it is good or not.
+
+* * * * *
+
+Sander Pronk:
+
+So how can you find out where the block is?
+
+* * * * *
+
+magnus.lundborg:
+
+If the offsets are not listed in the TOC block you would have to read
+the whole frame set, or at least all the block headers in the frame set,
+which shouldn't be too bad.
+
+[[b]](#cmnt_ref2)Roland Schulz:
+
+I suggest not to use a version number. This is already a problem with
+the tpx version and branches. Instead I suggest to have a bitvector
+where each bit says whether a certain feature is present in this file.
+Given a central registry of meaning of the bits, this allows different
+groups/branches/software to add features. Which would be difficult with
+a version number approach which has an inherently linear ordering. The
+last bit probably should be reserved to signify whether the 64bit
+bitvector is extended by a another 64bit. The data should be stored in
+the order of the bitvectors. A reader which doesn't support a certain
+bit, cannot read any of the following data if the bit is on.
+
+[[c]](#cmnt_ref3)magnus.lundborg:
+
+We will have a problem if we want to add data to a frame set in a file.
+All subsequent frame sets will need to be rewritten. One alternative
+would be to have a list of pointers to each block of each block type in
+the frame set table of contents block. But we will have a problem adding
+rows to that block as well, which in turn could be fixed by having a
+pointer from the frame set block to the "current" table of contents
+block and just let the old one remain. We could actually have a flag in
+block headers to show if the block is "up-to-date". But there is a risk
+that these pointers will be slow - especially when it comes to writing.
+
+[[d]](#cmnt_ref4)Rossen Apostolov:
+
+somehow the name of the FF doesn't fit naturally with the rest of the
+info here :)
+
+How about including the simulation setup in the file in a separate
+block? That will be needed if the file can be used for restarts too.
+
+[[e]](#cmnt_ref5)magnus.lundborg:
+
+This introduces a bit of redundancy, but helps keeping track of the
+data.
+
+[[f]](#cmnt_ref6)Roland Schulz:
+
+might be good to make this optional. And if it isn't given then the
+numbering is consecutive. The would still give the flexibility that one
+can specify the first and no of particles which isn't possible without
+index block.
+
+* * * * *
+
+Daniel Spångberg:
+
+if this is made optional, the comment below the section can be removed.
+and particle mapping blocks required, since it will not cost much extra
+to have it.
+
+[[g]](#cmnt_ref7)Rossen Apostolov:
+
+We should think of a different name for the traj. group blcok, it's
+confusing
diff --git a/src/external/tng_io/example_files/tng_example.tng b/src/external/tng_io/example_files/tng_example.tng
new file mode 100644 (file)
index 0000000..82c64a4
Binary files /dev/null and b/src/external/tng_io/example_files/tng_example.tng differ
diff --git a/src/external/tng_io/include/compression/bwlzh.h b/src/external/tng_io/include/compression/bwlzh.h
new file mode 100644 (file)
index 0000000..70d586a
--- /dev/null
@@ -0,0 +1,72 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef BWLZH_H
+#define BWLZH_H
+
+/* Compress the integers (positive, small integers are preferable)
+   using bwlzh compression.  The unsigned char *output should be
+   allocated to be able to hold worst case. You can obtain this length
+   conveniently by calling comp_get_buflen()
+*/
+void DECLSPECDLLEXPORT bwlzh_compress(unsigned int *vals, int nvals,
+                 unsigned char *output, int *output_len);
+
+void DECLSPECDLLEXPORT bwlzh_compress_no_lz77(unsigned int *vals, int nvals,
+                 unsigned char *output, int *output_len);
+
+int DECLSPECDLLEXPORT bwlzh_get_buflen(int nvals);
+
+void DECLSPECDLLEXPORT bwlzh_decompress(unsigned char *input, int nvals,
+                   unsigned int *vals);
+
+
+/* The routines below are mostly useful for testing, and for internal
+   use by the library. */
+
+void DECLSPECDLLEXPORT bwlzh_compress_verbose(unsigned int *vals, int nvals,
+                         unsigned char *output, int *output_len);
+
+void DECLSPECDLLEXPORT bwlzh_compress_no_lz77_verbose(unsigned int *vals, int nvals,
+                 unsigned char *output, int *output_len);
+
+void DECLSPECDLLEXPORT bwlzh_decompress_verbose(unsigned char *input, int nvals,
+                           unsigned int *vals);
+
+/* Compress the integers (positive, small integers are preferable)
+   using huffman coding, with automatic selection of how to handle the
+   huffman dictionary.  The unsigned char *huffman should be allocated
+   to be able to hold worst case. You can obtain this length
+   conveniently by calling comp_huff_buflen()
+*/
+void Ptngc_comp_huff_compress(unsigned int *vals, int nvals,
+                       unsigned char *huffman, int *huffman_len);
+
+int Ptngc_comp_huff_buflen(int nvals);
+
+void Ptngc_comp_huff_decompress(unsigned char *huffman, int huffman_len,
+                         unsigned int *vals);
+
+
+/* the value pointed to by chosen_algo should be
+   sent as -1 for autodetect. */
+void Ptngc_comp_huff_compress_verbose(unsigned int *vals, int nvals,
+                               unsigned char *huffman, int *huffman_len,
+                               int *huffdatalen,
+                               int *huffman_lengths,int *chosen_algo,
+                               int isvals16);
+
+#define N_HUFFMAN_ALGO 3
+char *Ptngc_comp_get_huff_algo_name(int algo);
+char *Ptngc_comp_get_algo_name(int algo);
+
+
+#endif
diff --git a/src/external/tng_io/include/compression/bwt.h b/src/external/tng_io/include/compression/bwt.h
new file mode 100644 (file)
index 0000000..9f927f8
--- /dev/null
@@ -0,0 +1,26 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef BWT_H
+#define BWT_H
+
+void Ptngc_comp_to_bwt(unsigned int *vals, int nvals,
+                unsigned int *output, int *index);
+
+void Ptngc_comp_from_bwt(unsigned int *input, int nvals, int index,
+                  unsigned int *vals);
+
+void Ptngc_bwt_merge_sort_inner(int *indices, int nvals,unsigned int *vals,
+                         int start, int end,
+                         unsigned int *nrepeat,
+                         int *workarray);
+
+#endif
diff --git a/src/external/tng_io/include/compression/coder.h b/src/external/tng_io/include/compression/coder.h
new file mode 100644 (file)
index 0000000..34b56c1
--- /dev/null
@@ -0,0 +1,48 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef CODER_H
+#define CODER_H
+
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+struct coder
+{
+    unsigned int pack_temporary;
+    int pack_temporary_bits;
+    int stat_overflow;
+    int stat_numval;
+};
+
+struct coder DECLSPECDLLEXPORT *Ptngc_coder_init(void);
+void DECLSPECDLLEXPORT Ptngc_coder_deinit(struct coder *coder);
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array(struct coder *coder,int *input, int *length, int coding, int coding_parameter, int natoms, int speed);
+int DECLSPECDLLEXPORT Ptngc_unpack_array(struct coder *coder,unsigned char *packed,int *output, int length, int coding, int coding_parameter, int natoms);
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array_xtc2(struct coder *coder,int *input, int *length);
+int DECLSPECDLLEXPORT Ptngc_unpack_array_xtc2(struct coder *coder,unsigned char *packed,int *output, int length);
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array_xtc3(int *input, int *length, int natoms, int speed);
+int DECLSPECDLLEXPORT Ptngc_unpack_array_xtc3(unsigned char *packed,int *output, int length, int natoms);
+
+void DECLSPECDLLEXPORT Ptngc_out8bits(struct coder *coder, unsigned char **output);
+void DECLSPECDLLEXPORT Ptngc_pack_flush(struct coder *coder,unsigned char **output);
+void DECLSPECDLLEXPORT Ptngc_write_pattern(struct coder *coder,unsigned int pattern, int nbits, unsigned char **output);
+
+void DECLSPECDLLEXPORT Ptngc_writebits(struct coder *coder,unsigned int value,int nbits, unsigned char **output_ptr);
+void DECLSPECDLLEXPORT Ptngc_write32bits(struct coder *coder,unsigned int value,int nbits, unsigned char **output_ptr);
+void DECLSPECDLLEXPORT Ptngc_writemanybits(struct coder *coder,unsigned char *value,int nbits, unsigned char **output_ptr);
+
+
+#endif
diff --git a/src/external/tng_io/include/compression/dict.h b/src/external/tng_io/include/compression/dict.h
new file mode 100644 (file)
index 0000000..d66dd23
--- /dev/null
@@ -0,0 +1,21 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef DICT_H
+#define DICT_H
+
+void Ptngc_comp_canonical_dict(unsigned int *dict, int *ndict);
+
+void Ptngc_comp_make_dict_hist(unsigned int *vals, int nvals,
+                        unsigned int *dict, int *ndict,
+                        unsigned int *hist);
+
+#endif
diff --git a/src/external/tng_io/include/compression/fixpoint.h b/src/external/tng_io/include/compression/fixpoint.h
new file mode 100644 (file)
index 0000000..6be7482
--- /dev/null
@@ -0,0 +1,39 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef FIXPOINT_H
+#define FIXPOINT_H
+
+#include "../compression/my64bit.h"
+
+/* There are at least 32 bits available in a long. */
+typedef unsigned long fix_t;
+
+/* Positive double to 32 bit fixed point value */
+fix_t Ptngc_ud_to_fix_t(double d,double max);
+
+/* double to signed 32 bit fixed point value */
+fix_t Ptngc_d_to_fix_t(double d,double max);
+
+/* 32 bit fixed point value to positive double */
+double Ptngc_fix_t_to_ud(fix_t f, double max);
+
+/* signed 32 bit fixed point value to double */
+double Ptngc_fix_t_to_d(fix_t f, double max);
+
+/* Convert a floating point variable to two 32 bit integers with range
+   -2.1e9 to 2.1e9 and precision to somewhere around 1e-9. */
+void Ptngc_d_to_i32x2(double d, fix_t *hi, fix_t *lo);
+
+/* Convert two 32 bit integers to a floating point variable
+   -2.1e9 to 2.1e9 and precision to somewhere around 1e-9. */
+double Ptngc_i32x2_to_d(fix_t hi, fix_t lo);
+
+#endif
diff --git a/src/external/tng_io/include/compression/huffman.h b/src/external/tng_io/include/compression/huffman.h
new file mode 100644 (file)
index 0000000..49347de
--- /dev/null
@@ -0,0 +1,33 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef HUFFMAN_H
+#define HUFFMAN_H
+
+void Ptngc_comp_conv_to_huffman(unsigned int *vals, int nvals,
+                         unsigned int *dict, int ndict,
+                         unsigned int *prob,
+                         unsigned char *huffman,
+                         int *huffman_len,
+                         unsigned char *huffman_dict,
+                         int *huffman_dictlen,
+                         unsigned int *huffman_dict_unpacked,
+                         int *huffman_dict_unpackedlen);
+
+void Ptngc_comp_conv_from_huffman(unsigned char *huffman,
+                           unsigned int *vals, int nvals,
+                           int ndict,
+                           unsigned char *huffman_dict,
+                           int huffman_dictlen,
+                           unsigned int *huffman_dict_unpacked,
+                           int huffman_dict_unpackedlen);
+
+#endif
diff --git a/src/external/tng_io/include/compression/lz77.h b/src/external/tng_io/include/compression/lz77.h
new file mode 100644 (file)
index 0000000..ad37e5b
--- /dev/null
@@ -0,0 +1,25 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef LZ77_H
+#define LZ77_H
+
+void Ptngc_comp_to_lz77(unsigned int *vals, int nvals,
+                 unsigned int *data, int *ndata,
+                 unsigned int *len, int *nlens,
+                 unsigned int *offsets, int *noffsets);
+
+void Ptngc_comp_from_lz77(unsigned int *data, int ndata,
+                   unsigned int *len, int nlens,
+                   unsigned int *offsets, int noffsets,
+                   unsigned int *vals, int nvals);
+
+#endif
diff --git a/src/external/tng_io/include/compression/merge_sort.h b/src/external/tng_io/include/compression/merge_sort.h
new file mode 100644 (file)
index 0000000..48ab410
--- /dev/null
@@ -0,0 +1,20 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef MERGE_SORT_H
+#define MERGE_SORT_H
+
+void Ptngc_merge_sort(void *base, size_t nmemb, size_t size,
+               int (*compar)(const void *v1,const void *v2,const void *private),
+               void *private);
+
+
+#endif
diff --git a/src/external/tng_io/include/compression/mtf.h b/src/external/tng_io/include/compression/mtf.h
new file mode 100644 (file)
index 0000000..bc4b2c8
--- /dev/null
@@ -0,0 +1,35 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef MTF_H
+#define MTF_H
+
+void Ptngc_comp_conv_to_mtf(unsigned int *vals, int nvals,
+                     unsigned int *dict, int ndict,
+                     unsigned int *valsmtf);
+
+void Ptngc_comp_conv_from_mtf(unsigned int *valsmtf, int nvals,
+                       unsigned int *dict, int ndict,
+                       unsigned int *vals);
+
+void Ptngc_comp_conv_to_mtf_partial(unsigned int *vals, int nvals,
+                             unsigned int *valsmtf);
+
+void Ptngc_comp_conv_from_mtf_partial(unsigned int *valsmtf, int nvals,
+                               unsigned int *vals);
+
+void Ptngc_comp_conv_to_mtf_partial3(unsigned int *vals, int nvals,
+                              unsigned char *valsmtf);
+
+void Ptngc_comp_conv_from_mtf_partial3(unsigned char *valsmtf, int nvals,
+                                unsigned int *vals);
+
+#endif
diff --git a/src/external/tng_io/include/compression/my64bit.h b/src/external/tng_io/include/compression/my64bit.h
new file mode 100644 (file)
index 0000000..c17a4a9
--- /dev/null
@@ -0,0 +1,33 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef MY64BIT_H
+#define MY64BIT_H
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+typedef int64_t my_int64_t;
+typedef uint64_t my_uint64_t;
+#define HAVE64BIT
+#else /* USE_STD_INTTYPES */
+/* The USE_WINDOWS symbol should be automatically defined in tng_compress.h */
+#include "../compression/tng_compress.h"
+#ifdef USE_WINDOWS
+typedef __int64 my_int64_t;
+typedef unsigned __int64 my_uint64_t;
+#define HAVE64BIT
+#else  /* USE_WINDOWS */
+/* Fall back to assume that we have unsigned long long */
+typedef unsigned long long my_uint64_t;
+#define HAVE64BIT
+#endif /* USE_WINDOWS */
+#endif /* USE_STD_INTTYPES */
+
+#endif
diff --git a/src/external/tng_io/include/compression/rle.h b/src/external/tng_io/include/compression/rle.h
new file mode 100644 (file)
index 0000000..c6d4706
--- /dev/null
@@ -0,0 +1,22 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef RLE_H
+#define RLE_H
+
+void Ptngc_comp_conv_to_rle(unsigned int *vals, int nvals,
+                     unsigned int *rle, int *nrle,
+                     int min_rle);
+
+void Ptngc_comp_conv_from_rle(unsigned int *rle,
+                       unsigned int *vals, int nvals);
+
+#endif
diff --git a/src/external/tng_io/include/compression/tng_compress.h b/src/external/tng_io/include/compression/tng_compress.h
new file mode 100644 (file)
index 0000000..c8b8db1
--- /dev/null
@@ -0,0 +1,223 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef TNG_COMPRESS_H
+#define TNG_COMPRESS_H
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* tng_compress_pos expects positions to have the order:
+   first xyz, then sorted in atom order
+   then all the frames repeated, i.e.:
+   nframes * [
+    natoms* [
+      x, y, z
+    ]
+   ]
+   desired_precision what to round the numbers to, i.e. integers will be created as:
+   round(pos[]/desired_precision).
+
+   algo should first be determined by calling
+   tng_compress_pos_find_algo
+
+   The compressed data is returned in a malloced pointer (so free can
+   be called to free the memory), the number of chars in the compressed
+   data is put into *nitems.
+
+   If too large values are input (compared to the precision), NULL is returned.
+*/
+
+char DECLSPECDLLEXPORT *tng_compress_pos(double *pos, int natoms, int nframes,
+                                        double desired_precision,
+                                        int speed, int *algo,
+                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_float(float *pos, int natoms, int nframes,
+                                              float desired_precision,
+                                              int speed, int *algo,
+                                              int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_int(int *pos, int natoms, int nframes,
+                                            unsigned long prec_hi, unsigned long prec_lo,
+                                            int speed,int *algo,
+                                            int *nitems);
+
+/* The tng_compress_pos_find_algo works the same as tng_compress_pos, but
+   it performs benchmarking to find the algorithms with the best
+   compression ratio.
+   The search is controlled by giving speed:
+   speed=1:  Fast algorithms only. This excludes all BWLZH algorithms and
+             the XTC3 algorithm.
+   speed=2:  Same as 1 and also includes the XTC3 algorithm using base compression
+             only.
+   speed=3:  Same as 2 and also includes the XTC3 algorithm which will use BWLZH
+             compression when it seems likely to give better
+             compression. Also includes the interframe BWLZH algorithm for
+             coordinates and velocities.
+   speed=4:  Enable the inter frame BWLZH algorithm for the coordinates.
+             The one-to-one BWLZH algorithm is enabled for velocities.
+   speed=5:  Enable the LZ77 part of the BWLZH algorithm.
+   speed=6:  Enable the intra frame BWLZH algorithm for the coordinates. Always try
+             the BWLZH compression in the XTC3 algorithm.
+
+   Set speed=0 to allow tng_compression to set the default speed (which is currently 2).
+   For very good compression it makes sense to choose speed=4 or speed=5
+
+   The number of items required in the algorithm array can be found
+   by calling tng_compress_nalgo
+*/
+
+char DECLSPECDLLEXPORT *tng_compress_pos_find_algo(double *pos, int natoms, int nframes,
+                                                  double desired_precision,
+                                                  int speed,
+                                                  int *algo,
+                                                  int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_float_find_algo(float *pos, int natoms, int nframes,
+                                                        float desired_precision,
+                                                        int speed,
+                                                        int *algo,
+                                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_int_find_algo(int *pos, int natoms, int nframes,
+                                                      unsigned long prec_hi, unsigned long prec_lo,
+                                                      int speed,int *algo,
+                                                      int *nitems);
+
+/* This returns the number of integers required for the storage of the algorithm
+   with the best compression ratio. */
+int DECLSPECDLLEXPORT tng_compress_nalgo(void);
+
+/* The following two routines does the same as the compression of the
+   positions, but compresses velocities instead. The algorithm
+   selection for velocities is different, so the position and
+   velocities routines should not be mixed. */
+
+char DECLSPECDLLEXPORT *tng_compress_vel(double *vel, int natoms, int nframes,
+                                        double desired_precision,
+                                        int speed, int *algo,
+                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_float(float *vel, int natoms, int nframes,
+                                              float desired_precision,
+                                              int speed, int *algo,
+                                              int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_int(int *vel, int natoms, int nframes,
+                                            unsigned long prec_hi, unsigned long prec_lo,
+                                            int speed, int *algo,
+                                            int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_find_algo(double *vel, int natoms, int nframes,
+                                                  double desired_precision,
+                                                  int speed,
+                                                  int *algo,
+                                                  int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_float_find_algo(float *vel, int natoms, int nframes,
+                                                        float desired_precision,
+                                                        int speed,
+                                                        int *algo,
+                                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_int_find_algo(int *vel, int natoms, int nframes,
+                                                      unsigned long prec_hi, unsigned long prec_lo,
+                                                      int speed,
+                                                      int *algo,
+                                                      int *nitems);
+
+/* From a compressed block, obtain information about
+   whether it is a position or velocity block:
+   *vel=1 means velocity block, *vel=0 means position block.
+   It also gives info about the number of atoms,
+   frames, and the precision used to compress the block, and the algorithms used to
+   compress the block. The return value=0 if the block looks like a tng compressed block,
+   and 1 otherwise. If the return value is 1 no information is returned. */
+int DECLSPECDLLEXPORT tng_compress_inquire(char *data,int *vel, int *natoms,
+                                          int *nframes, double *precision,
+                                          int *algo);
+
+/* Uncompresses any tng compress block, positions or velocities. It determines whether it is positions or velocities from the data buffer. The return value is 0 if ok, and 1 if not.
+*/
+int DECLSPECDLLEXPORT tng_compress_uncompress(char *data,double *posvel);
+
+int DECLSPECDLLEXPORT tng_compress_uncompress_float(char *data,float *posvel);
+
+int DECLSPECDLLEXPORT tng_compress_uncompress_int(char *data,int *posvel, unsigned long *prec_hi, unsigned long *prec_lo);
+
+/* This converts a block of integers, as obtained from tng_compress_uncompress_int, to floating point values
+   either double precision or single precision. */
+void DECLSPECDLLEXPORT tng_compress_int_to_double(int *posvel_int,unsigned long prec_hi, unsigned long prec_lo,
+                                                 int natoms,int nframes,
+                                                 double *posvel_double);
+
+void DECLSPECDLLEXPORT tng_compress_int_to_float(int *posvel_int,unsigned long prec_hi, unsigned long prec_lo,
+                                                int natoms,int nframes,
+                                                float *posvel_float);
+
+
+/* Compression algorithms (matching the original trajng
+   assignments) The compression backends require that some of the
+   algorithms must have the same value. */
+
+#define TNG_COMPRESS_ALGO_STOPBIT 1
+#define TNG_COMPRESS_ALGO_TRIPLET 2
+#define TNG_COMPRESS_ALGO_BWLZH1  8
+#define TNG_COMPRESS_ALGO_BWLZH2  9
+
+#define TNG_COMPRESS_ALGO_POS_STOPBIT_INTER     TNG_COMPRESS_ALGO_STOPBIT
+#define TNG_COMPRESS_ALGO_POS_TRIPLET_INTER     TNG_COMPRESS_ALGO_TRIPLET
+#define TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA     3
+#define TNG_COMPRESS_ALGO_POS_XTC2              5
+#define TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE  7
+#define TNG_COMPRESS_ALGO_POS_BWLZH_INTER       TNG_COMPRESS_ALGO_BWLZH1
+#define TNG_COMPRESS_ALGO_POS_BWLZH_INTRA       TNG_COMPRESS_ALGO_BWLZH2
+#define TNG_COMPRESS_ALGO_POS_XTC3              10
+#define TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE  TNG_COMPRESS_ALGO_STOPBIT
+#define TNG_COMPRESS_ALGO_VEL_TRIPLET_INTER     TNG_COMPRESS_ALGO_TRIPLET
+#define TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE  3
+#define TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER     6
+#define TNG_COMPRESS_ALGO_VEL_BWLZH_INTER       TNG_COMPRESS_ALGO_BWLZH1
+#define TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE    TNG_COMPRESS_ALGO_BWLZH2
+#define TNG_COMPRESS_ALGO_MAX 11
+
+
+
+/* Obtain strings describing the actual algorithms. These point to static memory, so should
+   not be freed. */
+char DECLSPECDLLEXPORT *tng_compress_initial_pos_algo(int *algo);
+char DECLSPECDLLEXPORT *tng_compress_pos_algo(int *algo);
+char DECLSPECDLLEXPORT *tng_compress_initial_vel_algo(int *algo);
+char DECLSPECDLLEXPORT *tng_compress_vel_algo(int *algo);
+
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+
+#endif
diff --git a/src/external/tng_io/include/compression/vals16.h b/src/external/tng_io/include/compression/vals16.h
new file mode 100644 (file)
index 0000000..4585755
--- /dev/null
@@ -0,0 +1,21 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef VALS16_H
+#define VALS16_H
+
+void Ptngc_comp_conv_to_vals16(unsigned int *vals,int nvals,
+                        unsigned int *vals16, int *nvals16);
+
+void Ptngc_comp_conv_from_vals16(unsigned int *vals16,int nvals16,
+                          unsigned int *vals, int *nvals);
+
+#endif
diff --git a/src/external/tng_io/include/compression/warnmalloc.h b/src/external/tng_io/include/compression/warnmalloc.h
new file mode 100644 (file)
index 0000000..4444271
--- /dev/null
@@ -0,0 +1,26 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef WARNMALLOC_H
+#define WARNMALLOC_H
+
+#include "../compression/tng_compress.h"
+
+void DECLSPECDLLEXPORT *Ptngc_warnmalloc_x(size_t size, char *file, int line);
+
+#define warnmalloc(size) Ptngc_warnmalloc_x(size,__FILE__,__LINE__)
+
+void DECLSPECDLLEXPORT *Ptngc_warnrealloc_x(void *old, size_t size, char *file, int line);
+
+#define warnrealloc(old,size) Ptngc_warnrealloc_x(old,size,__FILE__,__LINE__)
+
+
+#endif
diff --git a/src/external/tng_io/include/compression/widemuldiv.h b/src/external/tng_io/include/compression/widemuldiv.h
new file mode 100644 (file)
index 0000000..8ca24ee
--- /dev/null
@@ -0,0 +1,31 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef WIDEMULDIV_H
+#define WIDEMULDIV_H
+
+/* Multiply two 32 bit unsigned integers returning a 64 bit unsigned value (in two integers) */
+void Ptngc_widemul(unsigned int i1, unsigned int i2, unsigned int *ohi, unsigned int *olo);
+
+/* Divide a 64 bit unsigned value in hi:lo with the 32 bit value i and
+   return the result in result and the remainder in remainder */
+void Ptngc_widediv(unsigned int hi, unsigned int lo, unsigned int i, unsigned int *result, unsigned int *remainder);
+
+/* Add a unsigned int to a largeint. */
+void Ptngc_largeint_add(unsigned int v1, unsigned int *largeint, int n);
+
+/* Multiply v1 with largeint_in and return result in largeint_out */
+void Ptngc_largeint_mul(unsigned int v1, unsigned int *largeint_in, unsigned int *largeint_out, int n);
+
+/* Return the remainder from dividing largeint_in with v1. Result of the division is returned in largeint_out */
+unsigned int Ptngc_largeint_div(unsigned int v1, unsigned int *largeint_in, unsigned int *largeint_out, int n);
+
+#endif
diff --git a/src/external/tng_io/include/md5.h b/src/external/tng_io/include/md5.h
new file mode 100644 (file)
index 0000000..80fbf6c
--- /dev/null
@@ -0,0 +1,113 @@
+
+/* This file has been modified in the TNG library distribution. Modifications
+ * are marked below. */
+
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+       references to Ghostscript; clarified derivation from RFC 1321;
+       now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+       added conditionalization for C++ compilation from Martin
+       Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];       /* message length in bits, lsw first */
+    md5_word_t abcd[4];                /* digest buffer */
+    md5_byte_t buf[64];                /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* The USE_WINDOWS define below was added in TNG library distribution of this
+ * file in order to compile properly in MSVC */
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+/* The DECLSPECDLLEXPORT define below was added in the TNG library distribution
+ * of this file. It is also used in the function declarations. */
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+/* Initialize the algorithm. */
+void DECLSPECDLLEXPORT md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void DECLSPECDLLEXPORT md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void DECLSPECDLLEXPORT md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/src/external/tng_io/include/tng_io.h b/src/external/tng_io/include/tng_io.h
new file mode 100644 (file)
index 0000000..a240d32
--- /dev/null
@@ -0,0 +1,4809 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.4
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+/** @file tng_io.h
+ *  @brief API for input and output of tng trajectory files
+ *  @mainpage TNG: A flexible binary trajectory format
+ *  @section intro_sec Introduction
+ *
+ * The TNG format is developed as part of the ScalaLife EU project.
+ * It is flexible by design to allow parallel writing, custom data blocks,
+ * different output frequencies and different compression algorithms.
+ *
+ * Each block can contain MD5 hashes to verify data integrity and the file
+ * can be signed by the user to ensure that the origin is correct.
+ *
+ * This is version 1.4 of the TNG API. The intention is that this version of
+ * the API and ABI should be stable, but it is still possible that future
+ * changes might make that impossible, in which case that will be clarified.
+ *
+ * The API and all examples are released without any warranties. Use them at
+ * your own risk.
+ *
+ * @section authors_sec Authors
+ *
+ * The TNG trajectory format is developed by:
+ *
+ * Magnus Lundborg magnus.lundborg@scilifelab.se
+ *
+ * Daniel Spångberg daniels@mkem.uu.se
+ *
+ * Rossen Apostolov rossen@kth.se
+ *
+ * The API is implemented mainly by:
+ *
+ * Magnus Lundborg
+ *
+ * @section License
+ *
+ * Copyright (c) 2012, The GROMACS development team.
+ * check out http://www.gromacs.org for more information.
+ *
+ * The TNG API is released under the Revised BSD License and is free to
+ * redistribute according to that license.
+ *
+ * A license file (named COPYING) should be included with each copy of the API.
+ *
+ * @section install_sec Installation
+ *
+ * \code
+ * mkdir build
+ *
+ * cd build
+ *
+ * cmake ..
+ *
+ * make
+ *
+ * make install
+ * \endcode
+ * Test by running:
+ * \code
+ * bin/tests/tng_testing
+ * \endcode
+ *
+ * @section change_sec Change Log
+ *
+ * See git log for full revision history.
+ *
+ * Revisions
+ *
+ * v. 1.4 - Changed from LGPL to the Revised BSD License.
+ *
+ *        - More flexible support for digital signatures in header.
+ *        - Block ID numbers changed.
+ *
+ * v. 1.3 - Second stable release of the API.
+ *
+ *      - Added multiplication factor for coordinate units to general info.
+ *      - Added time stamps and time per frame in frame sets.
+ *      - High-level API functions added (not for managing molecules yet)
+ *      - Added functions for reading data blocks into 1D arrays.
+ *      - TNG compression added.
+ *      - C++ interface added.
+ *      - Avoid memory allocation if no data is submitted when adding data
+ *        blocks.
+ *      - Added function tng_num_frames_per_frame_set_set
+ *      - Added data block IDs for charges, b-factors and occupancy.
+ *      - GZIP compression added.
+ *      - Fixed bug when updating MD5 hashes of data blocks.
+ *      - Fixed bug in chain_name_of_particle_get(...)
+ *      - Update frame set pointers properly.
+ *      - Moved fortran wrapper from header file to source file.
+ *      - Write sparse data in mdrun examples.
+ *      - Fixed bugs related to reading and writing sparse data.
+ *      - Fixed memory leak for non-trajectory particle data blocks.
+ *      - Fixed bug when writing data blocks.
+ *      - Fixed wrong values in dependency constants
+ *      - Write box shape, partial charges and annotation data in tng_testing
+ *      - Bug fixes in tng_testing (frame sets not written before)
+ *
+ * v. 1.0 - First stable release of the API.
+ *
+ *
+ * @section examples_sec Examples
+ *
+ * There are some examples of how to use the library located in src/tests/
+ *
+ * @subsection tng_subsec TNG files
+ *
+ * The build directory contains an example_files directory, which in turn
+ * contains a very short example of a TNG file containing a few water molecules,
+ * a box shape description and positions in 10 frames.
+ *
+ * It is also possible to run the bin/examples/md_openmp_util
+ * (see src/tests/md_openmp_util.c)
+ * testing program, which will save MD simulations output to a new file
+ * (saved in the example_files directory).
+ *
+ * These files can be read using the bin/examples/tng_io_read_pos_util
+ * program.
+ *
+ * @subsection c_subsec C
+ *
+ * Example writing data to a TNG file (just an excerpt):
+ * \code
+ *     for ( step = 1; step < step_num; step++ )
+ *     {
+ *         compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
+ *
+ *         if(step % step_save == 0)
+ *         {
+ *             // Write positions, velocities and forces
+ *             if(tng_util_pos_write(traj, step, pos) != TNG_SUCCESS)
+ *             {
+ *                 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+ *                 break;
+ *             }
+ *             if(tng_util_vel_write(traj, step, vel) != TNG_SUCCESS)
+ *             {
+ *                 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+ *                 break;
+ *             }
+ *             if(tng_util_force_write(traj, step, force) != TNG_SUCCESS)
+ *             {
+ *                 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+ *                 break;
+ *             }
+ *         }
+ *         update ( np, nd, pos, vel, force, acc, mass, dt );
+ *     }
+ * \endcode
+ *
+ * Example reading positions from a TNG file:
+ * \code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ * #include "tng_io.h"
+ *
+ * int main(int argc, char **argv)
+ * {
+ *     tng_trajectory_t traj;
+ *     // Assume that the data is stored as floats. The data is placed in 1-D
+ *     // arrays
+ *     float *positions = 0, *box_shape = 0;
+ *     int64_t n_particles, n_frames, tot_n_frames, stride_length, i, j;
+ *     // Set a default frame range
+ *     int64_t first_frame = 0, last_frame = 5000;
+ *     int k;
+ *
+ *     // A reference must be passed to allocate memory
+ *     tng_util_trajectory_open(argv[1], 'r', &traj);
+ *
+ *     if(tng_num_frames_get(traj, &tot_n_frames) != TNG_SUCCESS)
+ *     {
+ *         printf("Cannot determine the number of frames in the file\n");
+ *         tng_util_trajectory_close(&traj);
+ *         exit(1);
+ *     }
+ *
+ *     if(tng_num_particles_get(traj, &n_particles) != TNG_SUCCESS)
+ *     {
+ *         printf("Cannot determine the number of particles in the file\n");
+ *         tng_util_trajectory_close(&traj);
+ *         exit(1);
+ *     }
+ *
+ *     printf("%"PRId64" frames in file\n", tot_n_frames);
+ *
+ *     if(last_frame > tot_n_frames - 1)
+ *     {
+ *         last_frame = tot_n_frames - 1;
+ *     }
+ *
+ *     if(tng_util_box_shape_read(traj, &box_shape, &stride_length) ==
+ *         TNG_SUCCESS)
+ *     {
+ *         printf("Simulation box shape: ");
+ *         for(i=0; i < 9; i++)
+ *         {
+ *             printf("%f ", box_shape[i]);
+ *         }
+ *         printf("\n");
+ *     }
+ *     else
+ *     {
+ *         printf("Simulation box shape not set in the file (or could not be read)\n");
+ *     }
+ *
+ *     n_frames = last_frame - first_frame + 1;
+ *
+ *
+ *     // Get the positions of all particles in the requested frame range.
+ *     // The positions are stored in the positions array.
+ *     // N.B. No proper error checks.
+ *     if(tng_util_pos_read_range(traj, 0, last_frame, &positions, &stride_length)
+ *        == TNG_SUCCESS)
+ *     {
+ *         // Print the positions of the wanted particle (zero based)
+ *         for(i=0; i < n_frames; i += stride_length)
+ *         {
+ *             printf("\nFrame %"PRId64":\n", first_frame + i);
+ *             for(j=0; j < n_particles; j++)
+ *             {
+ *                 printf("Atom nr: %"PRId64"", j);
+ *                 for(k=0; k < 3; k++)
+ *                 {
+ *                     printf("\t%f", positions[i/stride_length*n_particles*
+ *                                              3+j*3+k]);
+ *                 }
+ *                 printf("\n");
+ *             }
+ *         }
+ *     }
+ *     else
+ *     {
+ *         printf("Cannot read positions\n");
+ *     }
+ *
+ *     // Free memory
+ *     if(positions)
+ *     {
+ *         free(positions);
+ *     }
+ *     tng_util_trajectory_close(&traj);
+ *
+ *     return(0);
+ * }
+ *
+ * \endcode
+ *
+ * @subsection fortran_subsec Fortran
+ *
+ * The TNG library can be used from Fortran. It requires cray pointers, which
+ * are not part of the Fortran 77 standard, but available in most compilers.
+ *
+ * To compile the fortran example -DTNG_BUILD_FORTRAN=ON needs to be specified when
+ * running cmake.
+ *
+ */
+
+#ifndef TNG_IO_H
+#define TNG_IO_H     1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "tng_io_fwd.h"
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+#else
+/* Visual Studio does not contain inttypes.h and stdint.h. Some defines and
+ * typedefs are used from the GNU C Library */
+#ifdef _MSC_VER
+
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+#else
+#include <stdint.h>
+#endif /* _MSC_VER */
+
+/* This is from inttypes.h  (GNU C Library) */
+/* The ISO C99 standard specifies that these macros must only be
+   defined if explicitly requested.  */
+#if !defined __cplusplus || defined __STDC_FORMAT_MACROS
+
+# if __WORDSIZE == 64
+#  define __PRI64_PREFIX        "l"
+#  define __PRIPTR_PREFIX       "l"
+# else
+#  define __PRI64_PREFIX        "ll"
+#  define __PRIPTR_PREFIX
+# endif
+
+/* From stdint.h (GNU C Library) */
+/* Macros for printing format specifiers. */
+/* Decimal notation.  */
+#ifndef PRId64
+# define PRId64         __PRI64_PREFIX "d"
+#endif
+
+#endif
+
+#endif /* USE_STD_INTTYPES_H */
+
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+
+/** The version of this TNG build */
+#define TNG_VERSION 4 /* TNG_VERSION 4 => Api version 1.4 */
+
+/** Flag to indicate frame dependent data. */
+#define TNG_FRAME_DEPENDENT 1
+/** Flag to indicate particle dependent data. */
+#define TNG_PARTICLE_DEPENDENT 2
+
+/** The maximum length of a date string */
+#define TNG_MAX_DATE_STR_LEN 24
+/** The length of an MD5 hash */
+#define TNG_MD5_HASH_LEN 16
+/** The maximum allowed length of a string */
+#define TNG_MAX_STR_LEN 1024
+
+#ifndef NDEBUG
+#define TNG_ASSERT(cnd, msg) if(!(cnd)) {printf("%s\n", msg); assert(cnd);}
+#else
+#define TNG_ASSERT(cnd, msg) (void)0;
+#endif
+
+/** Flag to specify the endianness of a TNG file */
+typedef enum {TNG_BIG_ENDIAN,
+              TNG_LITTLE_ENDIAN} tng_file_endianness;
+
+/** Flag to specify the endianness of 32 bit values of the current architecture. */
+typedef enum {TNG_BIG_ENDIAN_32,
+              TNG_LITTLE_ENDIAN_32,
+              TNG_BYTE_PAIR_SWAP_32} tng_endianness_32;
+
+/** Flag to specify the endianness of 64 bit values of the current architecture. */
+typedef enum {TNG_BIG_ENDIAN_64,
+              TNG_LITTLE_ENDIAN_64,
+              TNG_QUAD_SWAP_64,
+              TNG_BYTE_PAIR_SWAP_64,
+              TNG_BYTE_SWAP_64} tng_endianness_64;
+
+/** Compression mode is specified in each data block */
+typedef enum {TNG_UNCOMPRESSED,
+              TNG_XTC_COMPRESSION,
+              TNG_TNG_COMPRESSION,
+              TNG_GZIP_COMPRESSION} tng_compression;
+
+/** Hash types */
+typedef enum {TNG_NO_HASH,
+              TNG_MD5,
+              TNG_SHA256} tng_hash_type;
+
+/** Non trajectory blocks come before the first frame set block */
+typedef enum {TNG_NON_TRAJECTORY_BLOCK, TNG_TRAJECTORY_BLOCK} tng_block_type;
+
+/** @defgroup def1 Standard non-trajectory blocks
+ *  Block IDs of standard non-trajectory blocks.
+ * @{
+ */
+#define TNG_GENERAL_INFO                0x0000000000000000LL
+#define TNG_MOLECULES                   0x0000000000000001LL
+#define TNG_TRAJECTORY_FRAME_SET        0x0000000000000002LL
+#define TNG_PARTICLE_MAPPING            0x0000000000000003LL
+/** @} */
+
+/** @defgroup def2 Standard trajectory blocks
+ * Block IDs of standard trajectory blocks. Box shape and partial charges can
+ * be either trajectory blocks or non-trajectory blocks
+ * @{
+ */
+#define TNG_TRAJ_BOX_SHAPE              0x0000000010000000LL
+#define TNG_TRAJ_POSITIONS              0x0000000010000001LL
+#define TNG_TRAJ_VELOCITIES             0x0000000010000002LL
+#define TNG_TRAJ_FORCES                 0x0000000010000003LL
+#define TNG_TRAJ_PARTIAL_CHARGES        0x0000000010000004LL
+#define TNG_TRAJ_FORMAL_CHARGES         0x0000000010000005LL
+#define TNG_TRAJ_B_FACTORS              0x0000000010000006LL
+#define TNG_TRAJ_ANISOTROPIC_B_FACTORS  0x0000000010000007LL
+#define TNG_TRAJ_OCCUPANCY              0x0000000010000008LL
+/** @} */
+
+
+/** @defgroup def3 GROMACS data block IDs
+ *  Block IDs of data blocks specific to GROMACS.
+ * @{
+ */
+#define TNG_GMX_LAMBDA                  0x1000000010000000LL
+#define TNG_GMX_ENERGY_ANGLE            0x1000000010000001LL
+#define TNG_GMX_ENERGY_RYCKAERT_BELL    0x1000000010000002LL
+#define TNG_GMX_ENERGY_LJ_14            0x1000000010000003LL
+#define TNG_GMX_ENERGY_COULOMB_14       0x1000000010000004LL
+#define TNG_GMX_ENERGY_LJ_(SR)          0x1000000010000005LL
+#define TNG_GMX_ENERGY_COULOMB_(SR)     0x1000000010000006LL
+#define TNG_GMX_ENERGY_COUL_RECIP       0x1000000010000007LL
+#define TNG_GMX_ENERGY_POTENTIAL        0x1000000010000008LL
+#define TNG_GMX_ENERGY_KINETIC_EN       0x1000000010000009LL
+#define TNG_GMX_ENERGY_TOTAL_ENERGY     0x1000000010000010LL
+#define TNG_GMX_ENERGY_TEMPERATURE      0x1000000010000011LL
+#define TNG_GMX_ENERGY_PRESSURE         0x1000000010000012LL
+#define TNG_GMX_ENERGY_CONSTR_RMSD      0x1000000010000013LL
+#define TNG_GMX_ENERGY_BOX_X            0x1000000010000014LL
+#define TNG_GMX_ENERGY_BOX_Y            0x1000000010000015LL
+#define TNG_GMX_ENERGY_BOX_Z            0x1000000010000016LL
+#define TNG_GMX_ENERGY_VOLUME           0x1000000010000017LL
+#define TNG_GMX_ENERGY_DENSITY          0x1000000010000018LL
+#define TNG_GMX_ENERGY_PV               0x1000000010000019LL
+#define TNG_GMX_ENERGY_ENTHALPY         0x1000000010000020LL
+#define TNG_GMX_ENERGY_VIR_XX           0x1000000010000021LL
+#define TNG_GMX_ENERGY_VIR_XY           0x1000000010000022LL
+#define TNG_GMX_ENERGY_VIR_XZ           0x1000000010000023LL
+#define TNG_GMX_ENERGY_VIR_YX           0x1000000010000024LL
+#define TNG_GMX_ENERGY_VIR_YY           0x1000000010000025LL
+#define TNG_GMX_ENERGY_VIR_YZ           0x1000000010000026LL
+#define TNG_GMX_ENERGY_VIR_ZX           0x1000000010000027LL
+#define TNG_GMX_ENERGY_VIR_ZY           0x1000000010000028LL
+#define TNG_GMX_ENERGY_VIR_ZZ           0x1000000010000029LL
+#define TNG_GMX_ENERGY_PRES_XX          0x1000000010000030LL
+#define TNG_GMX_ENERGY_PRES_XY          0x1000000010000031LL
+#define TNG_GMX_ENERGY_PRES_XZ          0x1000000010000032LL
+#define TNG_GMX_ENERGY_PRES_YX          0x1000000010000033LL
+#define TNG_GMX_ENERGY_PRES_YY          0x1000000010000034LL
+#define TNG_GMX_ENERGY_PRES_YZ          0x1000000010000035LL
+#define TNG_GMX_ENERGY_PRES_ZX          0x1000000010000036LL
+#define TNG_GMX_ENERGY_PRES_ZY          0x1000000010000037LL
+#define TNG_GMX_ENERGY_PRES_ZZ          0x1000000010000038LL
+#define TNG_GMX_ENERGY_SURFXSURFTEN     0x1000000010000039LL
+#define TNG_GMX_ENERGY_T_SYSTEM         0x1000000010000040LL
+#define TNG_GMX_ENERGY_LAMB_SYSTEM      0x1000000010000041LL
+#define TNG_GMX_SELECTION_GROUP_NAMES   0x1000000010000042LL
+#define TNG_GMX_ATOM_SELECTION_GROUP    0x1000000010000043LL
+/** @} */
+
+/** Flag to specify if a data block contains data related to particles or not.*/
+typedef enum {TNG_NON_PARTICLE_BLOCK_DATA,
+              TNG_PARTICLE_BLOCK_DATA} tng_particle_dependency;
+
+
+typedef enum {TNG_FALSE, TNG_TRUE} tng_bool;
+
+/** Flag to specify if the number of atoms change throughout the trajectory or
+ *  if it is constant. */
+typedef enum {TNG_CONSTANT_N_ATOMS, TNG_VARIABLE_N_ATOMS}
+             tng_variable_n_atoms_flag;
+
+/** Return values of API functions. TNG_SUCCESS means that the operation
+ *  was successful. TNG_FAILURE means that the operation failed for some
+ *  reason, but it is possible to try to continue anyhow. TNG_CRITICAL
+ *  means that the error is irrecoverable. */
+typedef enum {TNG_SUCCESS, TNG_FAILURE, TNG_CRITICAL} tng_function_status;
+
+/** If tng_hash_mode == TNG_USE_HASH md5 hashes will be written to output files
+ *  and when reading a file the md5 hashes of the contents will be compared to
+ *  those in the file (for each block) in order to ensure data integrity */
+typedef enum {TNG_SKIP_HASH, TNG_USE_HASH} tng_hash_mode;
+
+/** Possible formats of data block contents */
+typedef enum {TNG_CHAR_DATA,
+              TNG_INT_DATA,
+              TNG_FLOAT_DATA,
+              TNG_DOUBLE_DATA} tng_data_type;
+
+
+struct tng_trajectory;
+struct tng_molecule;
+struct tng_chain;
+struct tng_residue;
+struct tng_atom;
+struct tng_bond;
+struct tng_gen_block;
+struct tng_particle_mapping;
+struct tng_trajectory_frame_set;
+struct tng_particle_data;
+struct tng_non_particle_data;
+
+/** Data can be either double, float, int or a string */
+union data_values {
+    double d;
+    float f;
+    int64_t i;
+    char *c;
+};
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** @defgroup group1 Low-level API
+ *  These functions give detailed control of the TNG data management. Most
+ *  things can be done using the more convenient high-level API functions
+ *  instead.
+ *  @{
+ */
+
+/**
+ * @brief Setup a trajectory data container.
+ * @param tng_data_p a pointer to memory to initialise as a trajectory.
+ * @pre tng_data_p must not be pointing at a reserved memory block.
+ * @details Memory is allocated during initialisation.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init
+                (tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief Clean up a trajectory data container.
+ * @param tng_data_p a pointer to the trajectory data to destroy.
+ * @details All allocated memory in the data structure is freed, as well as
+ * tng_data_p itself.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_destroy
+                (tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief Copy a trajectory data container (dest is setup as well).
+ * @details This initialises dest and copies only what is absolute necessary for
+ * parallel i/o. This can be used inside pragma omp for setting up a thread
+ * local copy of src. It can be freed (using tng_trajectory_destroy) at the
+ * end of the parallel block.
+ * @param src the original trajectory.
+ * @param dest_p a pointer to memory to initialise as a trajectory.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre tng_data_p must not be pointing at a reserved memory block.
+ * @details Memory is allocated during initialisation.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init_from_src
+                (tng_trajectory_t src, tng_trajectory_t *dest_p);
+
+/**
+ * @brief Get the name of the input file.
+ * @param tng_data the trajectory of which to get the input file name.
+ * @param file_name the string to fill with the name of the input file,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for file_name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_input_file_get
+                (const tng_trajectory_t tng_data,
+                 char *file_name, const int max_len);
+
+/**
+ * @brief Set the name of the input file.
+ * @param tng_data the trajectory of which to set the input file name.
+ * @param file_name the name of the input file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_input_file_set
+                (tng_trajectory_t tng_data,
+                 const char *file_name);
+
+/**
+ * @brief Get the name of the output file.
+ * @param tng_data the trajectory of which to get the input file name.
+ * @param file_name the string to fill with the name of the output file,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for file_name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_get
+                (const tng_trajectory_t tng_data,
+                 char *file_name, const int max_len);
+
+/**
+ * @brief Set the name of the output file.
+ * @param tng_data the trajectory of which to set the output file name.
+ * @param file_name the name of the output file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_set
+                (tng_trajectory_t tng_data,
+                 const char *file_name);
+
+/**
+ * @brief Set the name of the output file for appending. The output file
+ * will not be overwritten.
+ * @param tng_data the trajectory of which to set the output file name.
+ * @param file_name the name of the output file to append to.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_append_file_set
+                (tng_trajectory_t tng_data,
+                 const char *file_name);
+
+/**
+ * @brief Get the endianness of the output file.
+ * @param tng_data the trajectory of which to get the endianness of the current
+ * output file.
+ * @param endianness will contain the enumeration of the endianness.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code endianness != 0 \endcode The pointer to the endianness container
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+ * could not be retrieved.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_get
+                (tng_trajectory_t tng_data, tng_file_endianness *endianness);
+
+/**
+ * @brief Set the endianness of the output file.
+ * @param tng_data the trajectory of which to set the endianness of the current
+ * output file.
+ * @param endianness the enumeration of the endianness, can be either
+ * TNG_BIG_ENDIAN (0) or TNG_LITTLE_ENDIAN (1).
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details The endianness cannot be changed after file output has started.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+ * could not be set.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_set
+                (tng_trajectory_t tng_data,
+                 const tng_file_endianness endianness);
+
+/**
+ * @brief Get the name of the program used when creating the trajectory.
+ * @param tng_data the trajectory of which to get the program name.
+ * @param name the string to fill with the name of the program,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the program used when creating the trajectory.
+ * @param tng_data the trajectory of which to set the program name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the program used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to get the program name.
+ * @param name the string to fill with the name of the program,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the program used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to set the program name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the user who created the trajectory.
+ * @param tng_data the trajectory of which to get the user name.
+ * @param name the string to fill with the name of the user,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the user who created the trajectory.
+ * @param tng_data the trajectory of which to set the user name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the user who last modified the trajectory.
+ * @param tng_data the trajectory of which to get the user name.
+ * @param name the string to fill with the name of the user,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the user who last modified the trajectory.
+ * @param tng_data the trajectory of which to set the user name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the computer used when creating the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param name the string to fill with the name of the computer,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the computer used when creating the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the computer used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param name the string to fill with the name of the computer,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the computer used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the pgp_signature of the user creating the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param signature the string to fill with the signature,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_get
+                (const tng_trajectory_t tng_data,
+                 char *signature, const int max_len);
+
+/**
+ * @brief Set the pgp_signature of the user creating the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param signature is a string containing the pgp_signature.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_set
+                (tng_trajectory_t tng_data,
+                 const char *signature);
+
+/**
+ * @brief Get the pgp_signature of the user last modifying the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param signature the string to fill with the signature,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_get
+                (const tng_trajectory_t tng_data,
+                 char *signature, const int max_len);
+
+/**
+ * @brief Set the pgp_signature of the user last modifying the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param signature is a string containing the pgp_signature.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_set
+                (tng_trajectory_t tng_data,
+                 const char *signature);
+
+/**
+ * @brief Get the name of the forcefield used in the trajectory.
+ * @param tng_data the trajectory of which to get the forcefield name.
+ * @param name the string to fill with the name of the forcefield,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the forcefield used in the trajectory.
+ * @param tng_data the trajectory of which to set the forcefield name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_set
+                (tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the medium stride length of the trajectory.
+ * @param tng_data is the trajectory from which to get the stride length.
+ * @param len is pointing to a value set to the stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code len != 0 \endcode The pointer to len must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len);
+
+/**
+ * @brief Set the medium stride length of the trajectory.
+ * @param tng_data is the trajectory of which to set the stride length.
+ * @param len is the wanted medium stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_set
+                (tng_trajectory_t tng_data,
+                 const int64_t len);
+
+/**
+ * @brief Get the long stride length of the trajectory.
+ * @param tng_data is the trajectory from which to get the stride length.
+ * @param len is pointing to a value set to the stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code len != 0 \endcode The pointer to len must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len);
+
+/**
+ * @brief Set the long stride length of the trajectory.
+ * @param tng_data is the trajectory of which to set the stride length.
+ * @param len is the wanted long stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_set
+                (tng_trajectory_t tng_data,
+                 const int64_t len);
+
+/**
+ * @brief Get the current time per frame of the trajectory.
+ * @param tng_data is the trajectory from which to get the time per frame.
+ * @param time is pointing to a value set to the time per frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time != 0 \endcode The pointer to time must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_get
+                (const tng_trajectory_t tng_data,
+                 double *time);
+
+/**
+ * @brief Set the time per frame of the trajectory.
+ * @param tng_data is the trajectory of which to set the time per frame.
+ * @param time is the new time per frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time > 0 \endcode The time per frame must be >= 0.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_set
+                (tng_trajectory_t tng_data,
+                 const double time);
+
+/**
+ * @brief Get the length of the input file.
+ * @param tng_data is the trajectory from which to get the input file length.
+ * @param len is pointing to a value set to the file length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code len != 0 \endcode The pointer to len must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_input_file_len_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len);
+
+/**
+ * @brief Get the number of frames in the trajectory
+ * @param tng_data is the trajectory of which to get the number of frames.
+ * @param n is pointing to a value set to the number of frames.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code tng_data->input_file != 0 \endcode An input file must be open
+ * to find the next frame set.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (could not find last frame set).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get the precision of lossy compression.
+ * @param tng_data is the trajectory of which to get the compression precision.
+ * @param precision will be pointing to the retrieved compression precision.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details A compression precision of 0.001 (the default) means that the
+ * compressed values are accurate to the third decimal. This function does
+ * not check actual precision of compressed data, but just returns what has
+ * previously been set using tng_compression_precision_set().
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_compression_precision_get
+                (const tng_trajectory_t tng_data,
+                 double *precision);
+
+/**
+ * @brief Set the precision of lossy compression.
+ * @param tng_data is the trajectory of which to set the compression precision.
+ * @param precision is the new compression precision.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details A compression precision of 0.001 (the default) means that the
+ * compressed values are accurate to the third decimal.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_compression_precision_set
+                (tng_trajectory_t tng_data,
+                 const double precision);
+
+/**
+ * @brief Set the number of particles, in the case no molecular system is used.
+ * @param tng_data is the trajectory of which to get the number of particles.
+ * @param n is the number of particles to use.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details When creating a molecular system the number of particles are set
+ * automatically. This should only be used when there is no molecular system
+ * specified or if the number of atoms needs to be overridden for some reason.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_implicit_num_particles_set
+                (tng_trajectory_t tng_data,
+                 const int64_t n);
+
+/**
+ * @brief Get the current number of particles.
+ * @param tng_data is the trajectory from which to get the number of particles.
+ * @param n is pointing to a value set to the number of particles.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @details If variable number of particles are used this function will return
+ * the number of particles in the current frame set.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get if the number of particle can be varied during the simulation.
+ * @param tng_data is the trajectory from which to get the number of particles.
+ * @param variable is pointing to a value set to TNG_CONSTANT_N_ATOMS if the
+ * number of particles cannot change or TNG_VARIABLE_N_ATOMS if the number of
+ * particles can change.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code variable != 0 \endcode The pointer to variable must not be
+ * a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_variable_get
+                (const tng_trajectory_t tng_data,
+                 char *variable);
+
+/**
+ * @brief Get the number of molecule types (length of tng_data->molecules).
+ * @param tng_data is the trajectory from which to get the number of molecules.
+ * @param n is pointing to a value set to the number of molecule types.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_molecule_types_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get the current total number of molecules.
+ * @param tng_data is the trajectory from which to get the number of molecules.
+ * @param n is pointing to a value set to the number of molecules.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @details If variable number of particles are used this function will return
+ * the total number of molecules in the current frame set.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_molecules_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/** @brief Get the list of the count of each molecule.
+ * @param tng_data is the trajectory from which to get the molecule count list.
+ * @param mol_cnt_list is a list of the count of each molecule in the
+ * mol system. This is a pointer to the list in the TNG container, which
+ * means that it should be handled carefully, e.g. not freed.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE(1) if the list of
+ * molecule counts was not valid.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_list_get
+                (const tng_trajectory_t tng_data,
+                 int64_t **mol_cnt_list);
+
+/**
+ * @brief Get the exponent used for distances in the trajectory.
+ * @param tng_data is the trajectory from which to get the information.
+ * @param exp is pointing to a value set to the distance unit exponent.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code exp != 0 \endcode The pointer to exp must not be a NULL pointer.
+ * @details Example: If the distances are specified in nm (default) exp is -9.
+ * If the distances are specified in Å exp is -10.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *exp);
+
+/**
+ * @brief Set the exponent used for distances in the trajectory.
+ * @param tng_data is the trajectory of which to set the unit exponent.
+ * @param exp is the distance unit exponent to use.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details Example: If the distances are specified in nm (default) exp is -9.
+ * If the distances are specified in Å exp is -10.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t exp);
+
+/**
+ * @brief Get the number of frames per frame set.
+ * @param tng_data is the trajectory from which to get the number of frames
+ * per frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @param n is pointing to a value set to the number of frames per frame set.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Set the number of frames per frame set.
+ * @param tng_data is the trajectory of which to set the number of frames
+ * per frame set.
+ * @param n is the number of frames per frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details This does not affect already existing frame sets. For
+ * consistency the number of frames per frame set should be set
+ * betfore creating any frame sets.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t n);
+
+/**
+ * @brief Get the number of frame sets.
+ * @details This updates tng_data->n_trajectory_frame_sets before returning it.
+ * @param tng_data is the trajectory from which to get the number of frame sets.
+ * @param n is pointing to a value set to the number of frame sets.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frame_sets_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get the current trajectory frame set.
+ * @param tng_data is the trajectory from which to get the frame set.
+ * @param frame_set_p will be set to point at the memory position of
+ * the found frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_current_frame_set_get
+                (tng_trajectory_t tng_data,
+                 tng_trajectory_frame_set_t *frame_set_p);
+
+/**
+ * @brief Find the requested frame set number.
+ * @param tng_data is the trajectory from which to get the frame set.
+ * @param nr is the frame set number to search for.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code nr >= 0 \endcode The frame set number (nr) must be >= 0.
+ * @details tng_data->current_trajectory_frame_set will contain the
+ * found trajectory if successful.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_nr_find
+                (tng_trajectory_t tng_data,
+                 const int64_t nr);
+
+/**
+ * @brief Find the frame set containing a specific frame.
+ * @param tng_data is the trajectory from which to get the frame set.
+ * @param frame is the frame number to search for.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame >= 0 \endcode The frame number must be >= 0.
+ * @details tng_data->current_trajectory_frame_set will contain the
+ * found trajectory if successful.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_of_frame_find
+                (tng_trajectory_t tng_data,
+                 const int64_t frame);
+
+/**
+ * @brief Get the file position of the next frame set in the input file.
+ * @param tng_data is a trajectory data container.
+ * @param frame_set is the frame set of which to get the position of the
+ * following frame set.
+ * @param pos is pointing to a value set to the file position.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code pos != 0 \endcode The pointer to pos must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_next_frame_set_file_pos_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos);
+
+/**
+ * @brief Get the file position of the previous frame set in the input file.
+ * @param tng_data is a trajectory data container.
+ * @param frame_set is the frame set of which to get the position of the
+ * previous frame set.
+ * @param pos is pointing to a value set to the file position.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code pos != 0 \endcode The pointer to pos must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_prev_frame_set_file_pos_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos);
+
+/**
+ * @brief Get the first and last frames of the frame set.
+ * @param tng_data is a trajectory data container.
+ * @param frame_set is the frame set of which to get the frame range.
+ * @param first_frame is set to the first frame of the frame set.
+ * @param last_frame is set to the last frame of the frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame != 0 \endcode The pointer to first_frame must
+ * not be a NULL pointer.
+ * @pre \code last_frame != 0 \endcode The pointer to last_frame must
+ * not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_frame_range_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *first_frame,
+                 int64_t *last_frame);
+
+/**
+ * @brief Allocate memory for and setup a molecule container.
+ * @param tng_data is a trajectory data container.
+ * @param molecule_p is a pointer to molecule to allocate and initialise.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_alloc(const tng_trajectory_t tng_data,
+                                                         tng_molecule_t *molecule_p);
+
+/**
+ * @brief Clean up a molecule container and free its allocated memory.
+ * @param tng_data is a trajectory data container.
+ * @param molecule_p is the molecule to destroy.
+ * @details All allocated memory in the data structure is freed and also the memory
+ * of the molecule itself.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_free(const tng_trajectory_t tng_data,
+                                                        tng_molecule_t *molecule_p);
+
+/**
+ * @brief Setup a molecule container.
+ * @param tng_data is a trajectory data container.
+ * @param molecule is the molecule to initialise. Memory must be preallocated.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_init
+                (const tng_trajectory_t tng_data,
+                 tng_molecule_t molecule);
+
+/**
+ * @brief Clean up a molecule container.
+ * @param tng_data is a trajectory data container.
+ * @param molecule is the molecule to destroy.
+ * @details All allocated memory in the data structure is freed, but not the
+ * memory of molecule itself.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_destroy
+                (const tng_trajectory_t tng_data,
+                 tng_molecule_t molecule);
+
+/**
+ * @brief Add a molecule to the trajectory.
+ * @param tng_data is the trajectory data container containing the block..
+ * @param name is a pointer to the string containing the name of the new molecule.
+ * @param molecule is a pointer to the newly created molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_add
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Add a molecule with a specific ID to the trajectory.
+ * @param tng_data is the trajectory data container containing the block..
+ * @param name is a pointer to the string containing the name of the new molecule.
+ * @param id is the ID of the created molecule.
+ * @param molecule is a pointer to the newly created molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_w_id_add
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t id,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Add an existing molecule (from a molecule container) to the trajectory.
+ * @param tng_data is the trajectory data container containing the block..
+ * @param molecule is a pointer to the molecule to add to the trajectory and will
+ * afterwards point to the molecule in the trajectory.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_existing_add
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Get the name of a molecule.
+ * @param tng_data the trajectory containing the molecule.
+ * @param molecule the molecule of which to get the name.
+ * @param name the string to fill with the name of the molecule,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_set
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *new_name);
+
+/**
+ * @brief Get the count of a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule of which to get the count.
+ * @param cnt is a pointer to the variable to be populated with the count.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code cnt != 0 \endcode The pointer to the molecule count
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t *cnt);
+
+/**
+ * @brief Set the count of a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule of which to set the count.
+ * @param cnt is the number of instances of this molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_set
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const int64_t cnt);
+
+/**
+ * @brief Find a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param name is a string containing the name of the molecule. If name is empty
+ * only id will be used for finding the molecule.
+ * @param id is the id of the molecule to look for. If id is -1 only the name of
+ * the molecule will be used for finding the molecule.
+ * @param molecule is a pointer to the molecule if it was found - otherwise 0.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the molecule is found or TNG_FAILURE (1) if the
+ * molecule is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_find
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 int64_t id,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Retrieve the molecule with specified index in the list of molecules.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in tng_data->molecules) of the molecule to return
+ * @param molecule is a pointer to the molecule if it was found - otherwise 0.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the molecule is found or TNG_FAILURE (1) if the
+ * molecule is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_of_index_get
+                (tng_trajectory_t tng_data,
+                 int64_t index,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Copy all molecules and the molecule counts from one TNG trajectory
+ * to another.
+ * @param tng_data_src is the source trajectory containing the molecular
+ * system to copy.
+ * @param tng_data_dest is the destination trajectory.
+ * @pre \code tng_data_src != 0 \endcode The trajectory container (tng_data_src)
+ * must be initialised before using it.
+ * @pre \code tng_data_dest != 0 \endcode The trajectory container (tng_data_dest)
+ * must be initialised before using it.
+ * @details The molecular system in tng_data_dest will be overwritten.
+ * @return TNG_SUCCESS(0) if the copying is successful, TNG_FAILURE if a minor
+ * error has occured or TNG_CRITICAL(2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_system_copy(tng_trajectory_t tng_data_src,
+                                                               tng_trajectory_t tng_data_dest);
+
+/**
+ * @brief Get the number of chains in a molecule.
+ * @param tng_data is the trajectory containing the molecule.
+ * @param molecule is the molecule of which to get the number of chains.
+ * @param n is pointing to a value set to the number of chains.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_chains_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the chain of a molecule with specified index in the list
+ * of chains.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in molecule->chains) of the chain to return
+ * @param molecule is the molecule from which to get the chain.
+ * @param chain is a pointer to the chain if it was found - otherwise 0.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @pre \code chain != 0 \endcode chain must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the chain is found or TNG_FAILURE (1) if the
+ * chain is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t index,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Get the number of residues in a molecule.
+ * @param tng_data is the trajectory containing the molecule.
+ * @param molecule is the molecule of which to get the number residues.
+ * @param n is pointing to a value set to the number of residues.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_residues_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the residue of a molecule with specified index in the list
+ * of chains.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in molecule->residues) of the residue to return
+ * @param molecule is the molecule from which to get the residue.
+ * @param residue is a pointer to the residue if it was found - otherwise 0.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @pre \code residue != 0 \endcode residue must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+ * residue is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_residue_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t index,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Get the number of atoms in a molecule.
+ * @param tng_data is the trajectory containing the molecule.
+ * @param molecule is the molecule of which to get the number of atoms.
+ * @param n is pointing to a value set to the number of atoms.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_atoms_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the atom of a molecule with specified index in the list
+ * of atoms.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in molecule->atoms) of the atom to return
+ * @param molecule is the molecule from which to get the atom.
+ * @param atom is a pointer to the atom if it was found - otherwise 0.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @pre \code atom != 0 \endcode atom must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the atom is found or TNG_FAILURE (1) if the
+ * atom is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_atom_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t index,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Find a chain in a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param molecule is the molecule in which to search for the chain.
+ * @param name is a string containing the name of the chain. If name is empty
+ * only id will be used for finding the chain.
+ * @param id is the id of the chain to look for. If id is -1 only the name of
+ * the chain will be used for finding the chain.
+ * @param chain is a pointer to the chain if it was found - otherwise 0.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the chain is found or TNG_FAILURE (1) if the
+ * chain is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_find
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 int64_t id,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Add a chain to a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule to add a chain to.
+ * @param name is a string containing the name of the chain.
+ * @param chain is a pointer to the newly created chain.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_add
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Add a chain with a specific id to a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule to add a chain to.
+ * @param name is a string containing the name of the chain.
+ * @param id is the ID of the created chain.
+ * @param chain is a pointer to the newly created chain.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_w_id_add
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 const int64_t id,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Add a bond between two atoms to a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param molecule is the molecule containing the atoms to connect.
+ * @param from_atom_id is the id of one of the two atoms in the bond.
+ * @param to_atom_id is the id of the other atom in the bond.
+ * @param bond is a pointer to the newly created bond.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (!) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_bond_add
+                (const tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const int64_t from_atom_id,
+                 const int64_t to_atom_id,
+                 tng_bond_t *bond);
+
+/**
+ * @brief Find an atom in a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param molecule is the molecule in which to search for the atom.
+ * @param name is a string containing the name of the atom. If name is an
+ * empty string only id will be used for searching.
+ * @param id is the id of the atom to find. If id == -1 the first atom
+ * that matches the specified name will be found.
+ * @param atom is a pointer to the atom if it was found - otherwise 0.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the atom is found or TNG_FAILURE (1) if the
+ * atom is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_atom_find
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 int64_t id,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Get the name of a chain.
+ * @param tng_data the trajectory containing the chain.
+ * @param chain the chain of which to get the name.
+ * @param name the string to fill with the name of the chain,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code chain != 0 \endcode The chain must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of a chain.
+ * @param tng_data is the trajectory data container containing the atom..
+ * @param chain is the chain to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code new_name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_set
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *new_name);
+
+/**
+ * @brief Get the number of residues in a molecule chain.
+ * @param tng_data is the trajectory containing the chain.
+ * @param chain is the chain of which to get the number of residues.
+ * @param n is pointing to a value set to the number of residues.
+ * @pre \code chain != 0 \endcode The chain must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_num_residues_get
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the residue of a chain with specified index in the list
+ * of residues.
+ * @param tng_data is the trajectory data container containing the chain.
+ * @param index is the index (in chain->residues) of the residue to return
+ * @param chain is the chain from which to get the residue.
+ * @param residue is a pointer to the residue if it was found - otherwise 0.
+ * @pre \code chain != 0 \endcode chain must not be a NULL pointer.
+ * @pre \code residue != 0 \endcode residue must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+ * residue is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 int64_t index,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Find a residue in a chain.
+ * @param tng_data is the trajectory data container containing the chain.
+ * @param chain is the chain in which to search for the residue.
+ * @param name is a string containing the name of the residue.  If name is an
+ * empty string only id will be used for searching.
+ * @param id is the id of the residue to find. If id == -1 the first residue
+ * that matches the specified name will be found.
+ * @param residue is a pointer to the residue if it was found - otherwise 0.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+ * residue is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_find
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *name,
+                 int64_t id,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Add a residue to a chain.
+ * @param tng_data is the trajectory data container containing the chain..
+ * @param chain is the chain to add a residue to.
+ * @param name is a string containing the name of the residue.
+ * @param residue is a pointer to the newly created residue.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_add
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *name,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Add a residue with a specific ID to a chain.
+ * @param tng_data is the trajectory data container containing the chain..
+ * @param chain is the chain to add a residue to.
+ * @param name is a string containing the name of the residue.
+ * @param id is the ID of the created residue.
+ * @param residue is a pointer to the newly created residue.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_w_id_add
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *name,
+                 const int64_t id,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Get the name of a residue.
+ * @param tng_data the trajectory containing the residue.
+ * @param residue the residue of which to get the name.
+ * @param name the string to fill with the name of the residue,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code residue != 0 \endcode The residue must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of a residue.
+ * @param tng_data is the trajectory data container containing the residue.
+ * @param residue is the residue to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The new name to set (new_name) must
+ * not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_set
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 const char *new_name);
+
+/**
+ * @brief Get the number of atoms in a residue.
+ * @param tng_data is the trajectory containing the residue.
+ * @param residue is the residue of which to get the number atoms.
+ * @param n is pointing to a value set to the number of atoms.
+ * @pre \code residue != 0 \endcode The residue must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_num_atoms_get
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the atom of a residue with specified index in the list
+ * of atoms.
+ * @param tng_data is the trajectory data container containing the residue.
+ * @param index is the index (in residue->atoms) of the atom to return
+ * @param residue is the residue from which to get the atom.
+ * @param atom is a pointer to the atom if it was found - otherwise 0.
+ * @pre \code residue != 0 \endcode residue must not be a NULL pointer.
+ * @pre \code atom != 0 \endcode atom must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the atom is found or TNG_FAILURE (1) if the
+ * atom is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 int64_t index,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Add an atom to a residue.
+ * @param tng_data is the trajectory containing the residue.
+ * @param residue is the residue to add an atom to.
+ * @param atom_name is a string containing the name of the atom.
+ * @param atom_type is a string containing the atom type of the atom.
+ * @param atom is a pointer to the newly created atom.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code atom_name != 0 \endcode The pointer to the atom name string
+ * must not be a NULL pointer.
+ * @pre \code atom_type != 0 \endcode The pointer to the atom_type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_add
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 const char *atom_name,
+                 const char *atom_type,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Add an atom with a specific ID to a residue.
+ * @param tng_data is the trajectory containing the residue.
+ * @param residue is the residue to add an atom to.
+ * @param atom_name is a string containing the name of the atom.
+ * @param atom_type is a string containing the atom type of the atom.
+ * @param id is the ID of the created atom.
+ * @param atom is a pointer to the newly created atom.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code atom_name != 0 \endcode The pointer to the atom name string
+ * must not be a NULL pointer.
+ * @pre \code atom_type != 0 \endcode The pointer to the atom_type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_w_id_add
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 const char *atom_name,
+                 const char *atom_type,
+                 const int64_t id,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Get the residue of an atom.
+ * @param tng_data the trajectory containing the atom.
+ * @param atom the atom of which to get the name.
+ * @param residue is set to the residue of the atom.
+ * @pre \code atom != 0 \endcode The atom must not be NULL.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status tng_atom_residue_get(tng_trajectory_t tng_data,
+                                         const tng_atom_t atom,
+                                         tng_residue_t *residue);
+
+/**
+ * @brief Get the name of an atom.
+ * @param tng_data the trajectory containing the atom.
+ * @param atom the atom of which to get the name.
+ * @param name the string to fill with the name of the atom,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code atom != 0 \endcode The atom must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of an atom.
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param atom is the atom to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_set
+                (tng_trajectory_t tng_data,
+                 tng_atom_t atom,
+                 const char *new_name);
+
+/**
+ * @brief Get the type of an atom.
+ * @param tng_data the trajectory containing the atom.
+ * @param atom the atom of which to get the type.
+ * @param type the string to fill with the type of the atom,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for type. This includes \0 terminating character.
+ * @pre \code atom != 0 \endcode The atom must not be NULL.
+ * @pre \code type != 0 \endcode The pointer to the type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_get
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 char *type,
+                 const int max_len);
+
+/**
+ * @brief Set the atom type of an atom.
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param atom is the atom to change.
+ * @param new_type is a string containing the atom type.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_type != 0 \endcode The pointer to the atom type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_set
+                (tng_trajectory_t tng_data,
+                 tng_atom_t atom,
+                 const char *new_type);
+
+/**
+ * @brief Get the molecule name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the molecule. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len);
+
+/**
+ * @brief Get the molecule id of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param id is will be set to the id of the molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code id != 0 \endcode The pointer to id must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id);
+
+/**
+ * @brief Get the bonds of the current molecular system.
+ * @param tng_data is the trajectory data container containing the molecular
+ * system.
+ * @param n_bonds is set to the number of bonds in the molecular system and
+ * thereby also the lengths of the two lists: from_atoms and to_atoms.
+ * @param from_atoms is a list (memory reserved by this function) of atoms
+ * (number of atom in mol system) in bonds.
+ * @param to_atoms is a list (memory reserved by this function) of atoms
+ * (number of atom in mol system) in bonds.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_bonds != 0 \endcode The pointer to n_bonds must not be a
+ * NULL pointer.
+ * @pre \code from_atoms != 0 \endcode The pointer to from_atoms must not
+ * be a NULL pointer.
+ * @pre \code to_atoms != 0 \endcode The pointer to to_atoms must not
+ * be a NULL pointer.
+ * @details The two lists of atoms use the same index, i.e. from_atoms[0]
+ * and to_atoms[0] are linked with a bond. Since memory is reserved in
+ * this function it must be freed afterwards.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molsystem_bonds_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n_bonds,
+                 int64_t **from_atoms,
+                 int64_t **to_atoms);
+
+/**
+ * @brief Get the chain name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the chain. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len);
+
+/**
+ * @brief Get the residue name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the residue. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len);
+
+/**
+ * @brief Get the residue id (local to molecule) of real particle number
+ * (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param id is a pointer to the variable, which will be set to the ID.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code id != 0 \endcode The pointer to id must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id);
+
+/**
+ * @brief Get the residue id (based on other molecules and molecule counts)
+ * of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param id is a pointer to the variable, which will be set to the ID.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code id != 0 \endcode The pointer to id must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_global_residue_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id);
+
+/**
+ * @brief Get the atom name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the atom. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len);
+
+/**
+ * @brief Get the atom type of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param type is a string, which is set to the type of the atom. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of type.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code type != 0 \endcode The pointer to the type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *type,
+                 int max_len);
+
+/**
+ * @brief Add a particle mapping table.
+ * @details Each particle mapping table will be written as a separate block,
+ * followed by the data blocks for the corresponding particles. In most cases
+ * there is one particle mapping block for each thread writing the trajectory.
+ * @param tng_data is the trajectory, with the frame set to which to add
+ * the mapping block.
+ * @details The mapping information is added to the currently active frame set
+ * of tng_data
+ * @param num_first_particle is the first particle number of this mapping
+ * block.
+ * @param n_particles is the number of particles in this mapping block.
+ * @param mapping_table is a list of the real particle numbers (i.e. the numbers
+ * used in the molecular system). The list is n_particles long.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details mapping_table[0] is the real particle number of the first particle
+ * in the following data blocks.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_mapping_add
+                (tng_trajectory_t tng_data,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t *mapping_table);
+
+/**
+ * @brief Remove all particle mappings (in memory) from the current frame set.
+ * @details Clears the currently setup particle mappings of the current frame
+ * set.
+ * @param tng_data is the trajectory, with the frame set of which to clear
+ * all particle mappings.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_particle_mapping_free
+                (tng_trajectory_t tng_data);
+
+/**
+ * @brief Read the header blocks from the input_file of tng_data.
+ * @details The trajectory blocks must be read separately and iteratively in chunks
+ * to fit in memory.
+ * @param tng_data is a trajectory data container.
+ * @details tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_read
+                (tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Write the header blocks to the output_file of tng_data.
+ * @details The trajectory blocks must be written separately and iteratively in chunks
+ * to fit in memory.
+ * @param tng_data is a trajectory data container.
+ * @details tng_data->output_file_path
+ * specifies which file to write to. If the file (output_file) is not open it
+ * will be opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read one (the next) block (of any kind) from the input_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_data is a pointer to the struct which will be populated with the
+ * data.
+ * @details If block_data->input_file_pos > 0 it is the position from where the
+ * reading starts otherwise it starts from the current position.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block != 0 \endcode The block container (block) must be
+ * initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_block_read_next
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read one frame set, including all particle mapping blocks and data
+ * blocks, starting from the current file position.
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read
+                (tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read data from the current frame set from the input_file. Only read
+ * particle mapping and data blocks matching the specified block_id.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param block_id is the ID of the data block to read from file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_current_only_data_from_block_id
+                (tng_trajectory_t tng_data,
+                 const char hash_mode,
+                 const int64_t block_id);
+
+/**
+ * @brief Read one (the next) frame set, including particle mapping and related data blocks
+ * from the input_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next
+                (tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read one (the next) frame set, including particle mapping and data blocks with a
+ * specific block id from the input_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param block_id is the ID number of the blocks that should be read from file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next_only_data_from_block_id
+                (tng_trajectory_t tng_data,
+                 const char hash_mode,
+                 const int64_t block_id);
+
+/**
+ * @brief Write one frame set, including mapping and related data blocks
+ * to the output_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->output_file_path specifies
+ * which file to write to. If the file (output_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Write one frame set even if it does not have as many frames as
+ * expected. The function also writes mapping and related data blocks
+ * to the output_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->output_file_path specifies
+ * which file to write to. If the file (output_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details The number of frames in the frame set is set to the number of
+ * frames of the data blocks before writing it to disk.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_premature_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Create and initialise a frame set.
+ * @details Particle mappings are retained from previous frame set (if any).
+ * To explicitly clear particle mappings use tng_frame_set_particle_mapping_free().
+ * @param tng_data is the trajectory data container in which to add the frame
+ * set.
+ * @param first_frame is the first frame of the frame set.
+ * @param n_frames is the number of frames in the frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame >= 0 \endcode The first frame must not be negative.
+ * @pre \code n_frames >= 0 \endcode The number of frames must not be negative.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_new
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t n_frames);
+
+/**
+ * @brief Create and initialise a frame set with the time of the first frame
+ * specified.
+ * @param tng_data is the trajectory data container in which to add the frame
+ * set.
+ * @param first_frame is the first frame of the frame set.
+ * @param n_frames is the number of frames in the frame set.
+ * @param first_frame_time is the time stamp of the first frame (in seconds).
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame >= 0 \endcode The first frame must not be negative.
+ * @pre \code n_frames >= 0 \endcode The number of frames must not be negative.
+ * @pre \code first_frame_time >= 0 \endcode The time stamp of the first frame
+ * must not be negative.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_with_time_new
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t n_frames,
+                 const double first_frame_time);
+
+/**
+ * @brief Set the time stamp of the first frame of the current frame set.
+ * @param tng_data is the trajectory containing the frame set.
+ * @param first_frame_time is the time stamp of the first frame in the
+ * frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame_time >= 0 \endcode The time stamp of the first frame
+ * must not be negative.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_first_frame_time_set
+                (tng_trajectory_t tng_data,
+                 const double first_frame_time);
+
+/**
+ * @brief Read the number of the first frame of the next frame set.
+ * @param tng_data is the trajectory containing the frame set.
+ * @param frame is set to the frame number of the first frame in the
+ * next frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code tng_data->input_file != 0 \endcode An input file must be open
+ * to find the next frame set.
+ * @pre \code frame != 0 \endcode The pointer to the frame must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS(0) if successful, TNG_FAILURE(1) if there is no next
+ * frame set or TNG_CRITICAL(2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_frame_nr_of_next_frame_set_get
+                (tng_trajectory_t tng_data,
+                 int64_t *frame);
+
+/**
+ * @brief Add a non-particle dependent data block.
+ * @param tng_data is the trajectory data container in which to add the data
+ * block
+ * @param id is the block ID of the block to add.
+ * @param block_name is a descriptive name of the block to add
+ * @param datatype is the datatype of the data in the block (e.g. int/float)
+ * @param block_type_flag indicates if this is a non-trajectory block (added
+ * directly to tng_data) or if it is a trajectory block (added to the
+ * frame set)
+ * @param n_frames is the number of frames of the data block (automatically
+ * set to 1 if adding a non-trajectory data block)
+ * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+ * for a box shape block)
+ * @param stride_length is how many frames are between each entry in the
+ * data block
+ * @param codec_id is the ID of the codec to compress the data.
+ * @param new_data is an array of data values to add.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block_name != 0 \endcode The pointer to the block name must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame > 0 \endcode n_values_per_frame must be
+ * a positive integer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_add
+                (tng_trajectory_t tng_data,
+                 const int64_t id,
+                 const char *block_name,
+                 const char datatype,
+                 const char block_type_flag,
+                 int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 int64_t stride_length,
+                 const int64_t codec_id,
+                 void *new_data);
+
+/**
+ * @brief Add a particle dependent data block.
+ * @param tng_data is the trajectory data container in which to add the data
+ * block
+ * @param id is the block ID of the block to add.
+ * @param block_name is a descriptive name of the block to add
+ * @param datatype is the datatype of the data in the block (e.g. int/float)
+ * @param block_type_flag indicates if this is a non-trajectory block (added
+ * directly to tng_data) or if it is a trajectory block (added to the
+ * frame set)
+ * @param n_frames is the number of frames of the data block (automatically
+ * set to 1 if adding a non-trajectory data block)
+ * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+ * for a box shape block)
+ * @param stride_length is how many frames are between each entry in the
+ * data block
+ * @param num_first_particle is the number of the first particle stored
+ * in this data block
+ * @param n_particles is the number of particles stored in this data block
+ * @param codec_id is the ID of the codec to compress the data.
+ * @param new_data is an array of data values to add.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block_name != 0 \endcode The pointer to the block name must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame > 0 \endcode n_values_per_frame must be
+ * a positive integer.
+ * @pre \code num_first_particle >= 0 \endcode The number of the
+ * first particle must be >= 0.
+ * @pre \code n_particles >= 0 \endcode n_particles must be >= 0.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_block_add
+                (tng_trajectory_t tng_data,
+                 const int64_t id,
+                 const char *block_name,
+                 const char datatype,
+                 const char block_type_flag,
+                 int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 int64_t stride_length,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t codec_id,
+                 void *new_data);
+
+/** @brief Get the name of a data block of a specific ID.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the ID of the data block of which to get the name.
+ * @param name is a string, which is set to the name of the data block.
+ * Memory must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the data block is found, TNG_FAILURE (1)
+ * if a minor error has occured or the data block is not found or
+ * TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_name_get
+                (tng_trajectory_t tng_data,
+                 int64_t block_id,
+                 char *name,
+                 int max_len);
+
+/** @brief Get the dependency of a data block of a specific ID.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the ID of the data block of which to get the name.
+ * @param block_dependency is a pointer to the dependency of the data block.
+ * If the block is frame dependent it will be set to TNG_FRAME_DEPENDENT,
+ * if it is particle dependent it will be set to TNG_PARTICLE_DEPENDENT and
+ * if it is both it will be set to TNG_FRAME_DEPENDENT & TNG_PARTICLE_DEPENDENT.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block_dependency != 0 \endcode The pointer to the block dependency
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the data block is found, TNG_FAILURE (1)
+ * if a minor error has occured or the data block is not found or
+ * TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_dependency_get
+                (tng_trajectory_t tng_data,
+                 int64_t block_id,
+                 int *block_dependency);
+
+/** @brief Get the number of values per frame of a data block of a specific ID.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the ID of the data block of which to get the name.
+ * @param n_values_per_frame is a pointer set to the number of values per frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of values
+ * per frame must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the data block is found, TNG_FAILURE (1)
+ * if a minor error has occured or the data block is not found or
+ * TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_num_values_per_frame_get
+                (tng_trajectory_t tng_data,
+                 int64_t block_id,
+                 int64_t *n_values_per_frame);
+
+/**
+ * @brief Write data of one trajectory frame to the output_file of tng_data.
+ * @param tng_data is a trajectory data container. tng_data->output_file_path
+ * specifies which file to write to. If the file (output_file) is not open it
+ * will be opened.
+ * @param frame_nr is the index number of the frame to write.
+ * @param block_id is the ID of the data block to write the data to.
+ * @param values is an array of data to write. The length of the array should
+ * equal n_values_per_frame.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_data_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const int64_t block_id,
+                 const void *values,
+                 const char hash_mode);
+
+/**
+ * @brief Write particle data of one trajectory frame to the output_file of
+ * tng_data.
+ * @param tng_data is a trajectory data container. tng_data->output_file_path
+ * specifies which file to write to. If the file (output_file) is not open it
+ * will be opened.
+ * @param frame_nr is the index number of the frame to write.
+ * @param block_id is the ID of the data block to write the data to.
+ * @param val_first_particle is the number of the first particle in the data
+ * array.
+ * @param val_n_particles is the number of particles in the data array.
+ * @param values is a 1D-array of data to write. The length of the array should
+ * equal n_particles * n_values_per_frame.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code val_first_particle >= 0 \endcode The number of the
+ * first particle must be >= 0.
+ * @pre \code val_n_particles >= 0 \endcode The number of particles must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_particle_data_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const int64_t block_id,
+                 const int64_t val_first_particle,
+                 const int64_t val_n_particles,
+                 const void *values,
+                 const char hash_mode);
+
+/**
+ * @brief Free data of an array of values (2D).
+ * @param tng_data is a trajectory data container.
+ * @param values is the 2D array to free and will be set to 0 afterwards.
+ * @param n_frames is the number of frames in the data array.
+ * @param n_values_per_frame is the number of values per frame in the data array.
+ * @param type is the data type of the data in the array (e.g. int/float/char).
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_values_free
+                (const tng_trajectory_t tng_data,
+                 union data_values **values,
+                 const int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 const char type);
+
+/**
+ * @brief Free data of an array of values (3D).
+ * @param tng_data is a trajectory data container.
+ * @param values is the array to free and will be set to 0 afterwards.
+ * @param n_frames is the number of frames in the data array.
+ * @param n_particles is the number of particles in the data array.
+ * @param n_values_per_frame is the number of values per frame in the data array.
+ * @param type is the data type of the data in the array (e.g. int/float/char).
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_values_free
+                (const tng_trajectory_t tng_data,
+                 union data_values ***values,
+                 const int64_t n_frames,
+                 const int64_t n_particles,
+                 const int64_t n_values_per_frame,
+                 const char type);
+
+/**
+ * @brief Retrieve non-particle data, from the last read frame set. Obsolete!
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_values_per_frame).
+ * Since ***values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of frames in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_data_vector_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_get(tng_trajectory_t tng_data,
+                                                   const int64_t block_id,
+                                                   union data_values ***values,
+                                                   int64_t *n_frames,
+                                                   int64_t *n_values_per_frame,
+                                                   char *type);
+
+/**
+ * @brief Retrieve a vector (1D array) of non-particle data, from the last read frame set.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be (n_frames * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param stride_length is set to the stride length of the returned data.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_vector_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve non-particle data, in a specific interval. Obsolete!
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_values_per_frame).
+ * Since ***values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_data_vector_interval_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 union data_values ***values,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve a vector (1D array) of non-particle data,
+ * in a specific interval.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be (n_frames * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param stride_length is set to the stride length (writing interval) of
+ * the data.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_vector_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Retrieve particle data, from the last read frame set. Obsolete!
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path
+ * specifies which file to read from. If the file (input_file) is not open it
+ * will be opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_particles * n_values_per_frame).
+ * Since ****values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of frames in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_particle_data_vector_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 union data_values ****values,
+                 int64_t *n_frames,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Retrieve a vector (1D array) of particle data, from the last read frame set.
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path
+ * specifies which file to read from. If the file (input_file) is not open it
+ * will be opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be
+ * (n_frames * n_particles * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of frames in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param stride_length is set to the stride length of the returned data.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_vector_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve particle data, in a specific interval. Obsolete!
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_particles * n_values_per_frame).
+ * Since ****values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_particle_data_vector_interval_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 union data_values ****values,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve a vector (1D array) particle data, in a
+ * specific interval.
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be
+ * (n_frames * n_particles * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param stride_length is set to the stride length (writing interval) of
+ * the data.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_vector_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *n_particles,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Get the stride length of a specific data (particle dependency does not matter)
+ * block, either in the current frame set or of a specific frame.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the block ID of the data block, of which to retrieve the
+ * stride length of the data.
+ * @param frame is the frame from which to get the stride length. If frame is set to -1
+ * no specific frame will be used, but instead the first frame, starting from the last read
+ * frame set, containing the data block will be used.
+ * @param stride_length is set to the value of the stride length of the data block.
+ * @return  TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_get_stride_length
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int64_t frame,
+                 int64_t *stride_length);
+
+/**
+ * @brief Get the date and time of initial file creation in ISO format (string).
+ * @param tng_data is a trajectory data container.
+ * @param time is a pointer to the string in which the date will be stored. Memory
+ * must be reserved beforehand.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time != 0 \endcode The pointer to the time must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_time_get_str
+                (const tng_trajectory_t tng_data,
+                 char *time);
+/** @} */ /* end of group1 */
+
+/** @defgroup group2 High-level API
+ *  These functions make it easier to access and output TNG data. They
+ *  are recommended unless there is a special reason to use the more
+ *  detailed functions available in the low-level API.
+ *  @{
+ */
+
+/**
+ * @brief High-level function for opening and initializing a TNG trajectory.
+ * @param filename is a string containing the name of the trajectory to open.
+ * @param mode specifies the file mode of the trajectory. Can be set to 'r',
+ * 'w' or 'a' for reading, writing or appending respectively.
+ * @param tng_data_p is a pointer to the opened trajectory. This will be
+ * allocated by the TNG library. The trajectory must be
+ * closed by the user, whereby memory is freed.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code filename != 0 \endcode The pointer to the filename must not be a
+ * NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_open
+                (const char *filename,
+                 const char mode,
+                 tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief High-level function for closing a TNG trajectory.
+ * @param tng_data_p is a pointer to the trajectory to close. The memory
+ * will be freed after finalising the writing.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_close
+                (tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief High-level function for getting the time (in seconds) of a frame.
+ * @param tng_data is the trajectory containing the frame.
+ * @param frame_nr is the frame number of which to get the time.
+ * @param time is set to the time (in seconds) of the specified frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time != 0 \endcode The pointer to the time must not be a
+ * NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if a
+ * minor error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_time_of_frame_get
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 double *time);
+
+/*
+ * @brief High-level function for getting the molecules in the mol system.
+ * @param tng_data is the trajectory containing the mol system.
+ * @param n_mols is set to the number of molecules in the system.
+ * @param molecule_cnt_list will be pointing to the list of counts of each molecule
+ * in the mol system.
+ * @param mols pointing to the list of molecules in the mol system.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_mols != 0 \endcode The pointer to the number of molecules must
+ * not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+/*tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_molecules_get
+                (tng_trajectory_t tng_data,
+                 int64_t *n_mols,
+                 int64_t **molecule_cnt_list,
+                 tng_molecule_t *mols);
+*/
+/*
+ * @brief High-level function for adding a molecule to the mol system.
+ * @param tng_data is the trajectory containing the mol system.
+ * @param name is the name of the molecule to add.
+ * @param cnt is the count of the molecule.
+ * @param mol is set to point to the newly created molecule.
+ * @pre \code name != 0 \endcode The pointer to the name must not be a
+ * NULL pointer.
+ * @pre \code cnt >= 0 \endcode The requested count must be >= 0.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error has occured.
+ */
+/*tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_molecule_add
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t cnt,
+                 tng_molecule_t *mol);
+*/
+/*
+// tng_function_status DECLSPECDLLEXPORT tng_util_molecule_particles_get
+//                 (tng_trajectory_t tng_data,
+//                  const tng_molecule_t mol,
+//                  int64_t *n_particles,
+//                  char ***names,
+//                  char ***types,
+//                  char ***res_names,
+//                  int64_t **res_ids,
+//                  char ***chain_names,
+//                  int64_t **chain_ids);
+//
+// tng_function_status DECLSPECDLLEXPORT tng_util_molecule_particles_set
+//                 (tng_trajectory_t tng_data,
+//                  tng_molecule_t mol,
+//                  const int64_t n_particles,
+//                  const char **names,
+//                  const char **types,
+//                  const char **res_names,
+//                  const int64_t *res_ids,
+//                  const char **chain_names,
+//                  const int64_t *chain_ids);
+*/
+/**
+ * @brief High-level function for reading the positions of all particles
+ * from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param positions will be set to point at a 1-dimensional array of floats,
+ * which will contain the positions. The data is stored sequentially in order
+ * of frames. For each frame the positions (x, y and z coordinates) are stored.
+ * The variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code positions != 0 \endcode The pointer to the positions array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read
+                (tng_trajectory_t tng_data,
+                 float **positions,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the velocities of all particles
+ * from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param velocities will be set to point at a 1-dimensional array of floats,
+ * which will contain the velocities. The data is stored sequentially in order
+ * of frames. For each frame the velocities (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read
+                (tng_trajectory_t tng_data,
+                 float **velocities,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the forces of all particles
+ * from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param forces will be set to point at a 1-dimensional array of floats,
+ * which will contain the forces. The data is stored sequentially in order
+ * of frames. For each frame the forces (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code forces != 0 \endcode The pointer to the forces array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read
+                (tng_trajectory_t tng_data,
+                 float **forces,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the box shape from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param box_shape will be set to point at a 1-dimensional array of floats,
+ * which will contain the box shape. The data is stored sequentially in order
+ * of frames. The variable may point at already allocated memory or be a NULL pointer.
+ * If the box shape is not modified during the trajectory, but as general data,
+ * that will be returned instead.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @details This function should only be used if number of values used to specify
+ * the box shape is known (by default TNG uses 9 values) since it does not
+ * return the number of values in the array. It is recommended to use
+ * tng_data_vector_interval_get() instead.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read
+                (tng_trajectory_t tng_data,
+                 float **box_shape,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the next frame of particle-dependent
+ * data of a specific type.
+ * @param tng_data is the trajectory to read from.
+ * @param block_id is the ID number of the block containing the data of interest.
+ * @param values will be set to point at a 1-dimensional array containing the
+ * requested data. The variable may point at already allocated memory or be a
+ * NULL pointer. The memory must be freed afterwards.
+ * @param data_type will be pointing to a character indicating the size of the
+ * data of the returned values, e.g. TNG_INT_DATA, TNG_FLOAT_DATA or TNG_DOUBLE_DATA.
+ * @param retrieved_frame_number will be pointing at the frame number of the
+ * returned frame.
+ * @param retrieved_time will be pointing at the time stamp of the returned
+ * frame.
+ * @details If no frame has been read before the first frame of the trajectory
+ * is read.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code values != 0 \endcode The pointer to the values array
+ * must not be a NULL pointer.
+ * @pre \code data_type != 0 \endcode The pointer to the data type of the
+ * returned data must not be a NULL pointer.
+ * @pre \code retrieved_frame_number != 0 \endcode The pointer to the frame
+ * number of the returned data must not be a NULL pointer.
+ * @pre \code retrieved_time != 0 \endcode The pointer to the time of the
+ * returned data must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_particle_data_next_frame_read
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 char *data_type,
+                 int64_t *retrieved_frame_number,
+                 double *retrieved_time);
+
+/**
+ * @brief High-level function for reading the next frame of non-particle-dependent
+ * data of a specific type.
+ * @param tng_data is the trajectory to read from.
+ * @param block_id is the ID number of the block containing the data of interest.
+ * @param values will be set to point at a 1-dimensional array containing the
+ * requested data. The variable may point at already allocated memory or be a
+ * NULL pointer. The memory must be freed afterwards.
+ * @param data_type will be pointing to a character indicating the size of the
+ * data of the returned values, e.g. TNG_INT_DATA, TNG_FLOAT_DATA or TNG_DOUBLE_DATA.
+ * @param retrieved_frame_number will be pointing at the frame number of the
+ * returned frame.
+ * @param retrieved_time will be pointing at the time stamp of the returned
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code values != 0 \endcode The pointer to the values array
+ * must not be a NULL pointer.
+ * @pre \code data_type != 0 \endcode The pointer to the data type of the
+ * returned data must not be a NULL pointer.
+ * @pre \code retrieved_frame_number != 0 \endcode The pointer to the frame
+ * number of the returned data must not be a NULL pointer.
+ * @pre \code retrieved_time != 0 \endcode The pointer to the time of the
+ * returned data must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_non_particle_data_next_frame_read
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 char *data_type,
+                 int64_t *retrieved_frame_number,
+                 double *retrieved_time);
+
+/**
+ * @brief High-level function for reading the positions of all particles
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param positions will be set to point at a 1-dimensional array of floats,
+ * which will contain the positions. The data is stored sequentially in order
+ * of frames. For each frame the positions (x, y and z coordinates) are stored.
+ * The variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code positions != 0 \endcode The pointer to the positions array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **positions,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the velocities of all particles
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param velocities will be set to point at a 1-dimensional array of floats,
+ * which will contain the velocities. The data is stored sequentially in order
+ * of frames. For each frame the velocities (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **velocities,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the forces of all particles
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param forces will be set to point at a 1-dimensional array of floats,
+ * which will contain the forces. The data is stored sequentially in order
+ * of frames. For each frame the forces (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code forces != 0 \endcode The pointer to the forces array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **forces,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the box shape
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param box_shape will be set to point at a 1-dimensional array of floats,
+ * which will contain the box shape. The data is stored sequentially in order
+ * of frames.
+ * If the box shape is not modified during the trajectory, but as general data,
+ * that will be returned instead. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @details This function should only be used if number of values used to specify
+ * the box shape is known (by default TNG uses 9 values) since it does not
+ * return the number of values in the array. It is recommended to use
+ * tng_data_vector_interval_get() instead.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **box_shape,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for setting the writing interval of data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for setting the writing interval of data blocks
+ * containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for setting the writing interval of data blocks.
+ * Obsolete! Use tng_util_generic_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * This function is replaced by the more correcly named
+ * tng_util_generic_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for setting the writing interval of position
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a positions data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of position
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a positions data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of position
+ * data blocks. Obsolete! Use tng_util_pos_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a positions data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_pos_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a velocities data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a velocities data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks. Obsolete! Use tng_util_vel_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a velocities data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_vel_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of force
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a forces data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of force
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a forces data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of force
+ * data blocks. Obsolete! Use tng_util_force_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a forces data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_force_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of box shape
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a box shape data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of box shape
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a box shape data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks. Obsolete! Use tng_util_box_shape_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a box shape data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_box_shape_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for writing data of one frame to a data block.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for writing data of one frame to a double precision
+ * data block.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for adding data to positions data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *positions);
+
+/**
+ * @brief High-level function for adding data to positions data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *positions);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *velocities);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *velocities);
+
+/**
+ * @brief High-level function for adding data to forces data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *forces);
+
+/**
+ * @brief High-level function for adding data to forces data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *forces);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *box_shape);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *box_shape);
+
+/**
+ * @brief High-level function for writing data of one frame to a data block.
+ * If the frame is at the beginning of a frame set the time stamp of the frame
+ * set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for writing data of one frame to a double precision
+ * data block. If the frame is at the beginning of a frame set the time stamp of
+ * the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for adding data to positions data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *positions);
+
+/**
+ * @brief High-level function for adding data to positions data blocks at double
+ * precision. If the frame is at the beginning of a frame set the time stamp of
+ * the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *positions);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *velocities);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks at
+ * double precision. If the frame is at the beginning of a frame set the
+ * time stamp of the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *velocities);
+
+/**
+ * @brief High-level function for adding data to forces data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *forces);
+
+/**
+ * @brief High-level function for adding data to forces data blocks at
+ * double precision. If the frame is at the beginning of a frame set
+ * the time stamp of the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *forces);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *box_shape);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks at
+ * double precision. If the frame is at the beginning of a frame set the
+ * time stamp of the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *box_shape);
+
+/**
+ * @brief High-level function for getting the compression method and
+ * multiplication factor of the last read frame of a specific data block.
+ * @param tng_data is the trajectory to use.
+ * @param block_id is the ID number of the block containing the data of
+ * interest.
+ * @param codec_id will be set to the value of the codec_id of the
+ * compression of the data block. See tng_compression for more details.
+ * @param factor will be set to the multiplication factor applied to
+ * the values before compression, in order to get integers from them.
+ * factor is 1/precision.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code codec_id != 0 \endcode  The pointer to the returned codec id
+ * must not be a NULL pointer.
+ * @pre \code factor != 0 \endcode The pointer to the returned multiplication
+ * factor must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_frame_current_compression_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 char *codec_id,
+                 float *factor);
+
+/** @brief High-level function for determining the next frame with data and what
+ * data blocks have data for that frame. The search can be limited to certain
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param current_frame is the frame that was last read, from where to start
+ * looking for data.
+ * @param n_requested_data_block_ids is the number of data blocks listed in
+ * requested_data_block_ids. If this is 0 all data blocks will be taken into
+ * account.
+ * @param requested_data_block_ids is an array of data blocks to look for.
+ * @param next_frame will be set to the next frame with data.
+ * @param n_data_blocks_in_next_frame is set to the number of data blocks with
+ * data for next_frame.
+ * @param data_block_ids_in_next_frame is set to an array (of length
+ * n_data_blocks_in_next_frame) that lists the data block IDs with data for
+ * next_frame. It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code next_frame != 0 \endcode The pointer to the next frame must not
+ * be NULL.
+ * @pre \code n_data_blocks_in_next_frame != 0 \endcode The pointer to
+ * n_data_blocks_in_next_frame must not be NULL.
+ * @pre \code *data_block_ids_in_next_frame != 0 \endcode The pointer to the
+ * list of data block IDs must not be NULL.
+ * @pre \code n_requested_data_block_ids == 0 || requested_data_block_ids != 0 \endcode
+ * If the number of requested data blocks != 0 then the array of data block IDs must not be NULL.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_next_frame_present_data_blocks_find
+                (tng_trajectory_t tng_data,
+                 int64_t current_frame,
+                 const int64_t n_requested_data_block_ids,
+                 const int64_t *requested_data_block_ids,
+                 int64_t *next_frame,
+                 int64_t *n_data_blocks_in_next_frame,
+                 int64_t **data_block_ids_in_next_frame);
+
+/* @brief High-level function for getting all data block ids and their names
+ * and stride lengths.
+ * @param tng_data is the trajectory to use.
+ * @param n_data_blocks is set to the number of data blocks in the trajectory.
+ * @param data_block_ids is set to an array (of length
+ * n_data_blocks) that lists the data block IDs in the trajectory.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param data_block_names is set to an array (of length
+ * n_data_blocks) that contains the names of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param stride_lengths is set to an array (of length
+ * n_data_blocks) that lists the stride lengths of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param n_values_per_frame is set to an array (of length
+ * n_data_blocks) that lists the number of values per frame of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param block_types is set to an array (of length
+ * n_data_blocks) that lists the block types of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param dependencies is set to an array (of length
+ * n_data_blocks) that lists the dependencies of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param compressions is set to an array (of length
+ * n_data_blocks) that lists the compressions of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_data_blocks != 0 \endcode The pointer to
+ * n_data_blocks must not be NULL.
+ * @pre \code data_block_ids != 0 \endcode The pointer to the
+ * list of data block IDs must not be NULL.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+/*
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_all_data_block_types_get
+                (tng_trajectory_t tng_data,
+                 int64_t *n_data_blocks,
+                 int64_t **data_block_ids,
+                 char ***data_block_names,
+                 int64_t **stride_lengths,
+                 int64_t **n_values_per_frame,
+                 char **block_types,
+                 char **dependencies,
+                 char **compressions);
+*/
+
+/** @brief Finds the frame set of the specified frame in order to prepare for writing
+ * after it.
+ * @param tng_data is the trajectory to use.
+ * @param prev_frame is the frame after which to start appending.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code prev_frame >= 0 \endcode The previous frame must not be negative.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as not finding the requested frame) or TNG_CRITICAL (2)
+ * if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_prepare_append_after_frame
+                (tng_trajectory_t tng_data,
+                 const int64_t prev_frame);
+
+/** @} */ /* end of group2 */
+
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* TNG_IO_H */
diff --git a/src/external/tng_io/include/tng_io.hpp b/src/external/tng_io/include/tng_io.hpp
new file mode 100644 (file)
index 0000000..202f9db
--- /dev/null
@@ -0,0 +1,1705 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.4
+ *
+ * Written by Anders Gärdenäs
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef TNG_IO_HPP
+#define TNG_IO_HPP
+
+#include "tng_io.h"
+
+namespace Tng
+{
+class Trajectory;
+class Atom;
+class Residue;
+class Chain;
+class Molecule;
+typedef class Molecule * Molecule_t;
+
+
+class Trajectory {
+private:
+    tng_trajectory_t traj;
+    tng_function_status status;
+public:
+    /**
+    * @brief Add a molecule to the trajectory.
+    * @param name is a pointer to the string containing the name of the new molecule.
+    * @param molecule is a pointer to the newly created molecule.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+
+    tng_function_status addMolecule(const char *, Molecule_t);
+    tng_function_status addMoleculeWithId(const char *, int64_t id, Molecule_t);
+    tng_function_status findMolecule(const char *name, int64_t id, Molecule_t molecule);
+    friend class Atom;
+    friend class Residue;
+    friend class Chain;
+    friend class Molecule;
+
+    //! Normal constructor
+    Trajectory()
+    { status = tng_trajectory_init(&traj); }
+
+    //! Copy constructor
+    Trajectory(Trajectory * src)
+    { status = tng_trajectory_init_from_src(traj,&src->traj); }
+
+    //! Detructor
+    ~Trajectory()
+    { status = tng_trajectory_destroy(&traj); }
+
+    //! Status
+    tng_function_status getStatus()
+    { return status; }
+
+
+    /**
+    * @brief Get the name of the input file.
+    * @param file_name the string to fill with the name of the input file,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for file_name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getInputFile (char *file_name, const int max_len)
+    {
+        return status = tng_input_file_get(traj, file_name,   max_len);
+    }
+
+    /**
+    * @brief Set the name of the input file.
+    * @param file_name the name of the input file.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setInputFile(const char *file_name)
+    {
+        return status = tng_input_file_set(traj, file_name);
+    }
+
+
+    /**
+    * @brief Get the name of the output file.
+    * @param file_name the string to fill with the name of the output file,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for file_name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getOutputFile(char *file_name, const int max_len)
+    {
+        return status = tng_output_file_get(traj, file_name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the output file.
+    * @param file_name the name of the output file.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setOutputFile(const char *file_name)
+    {
+        return status = tng_output_file_set(traj, file_name);
+    }
+
+    /**
+    * @brief Get the endianness of the output file.
+    * current output file.
+    * @param endianness will contain the enumeration of the endianness.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+    * could not be retrieved.
+    */
+    tng_function_status getOutputFileEndianness
+                    (tng_file_endianness *endianness)
+    {
+        return status = tng_output_file_endianness_get(traj, endianness);
+    }
+
+    /**
+    * @brief Set the endianness of the output file.
+    * @param endianness the enumeration of the endianness, can be either
+    * TNG_BIG_ENDIAN (0) or TNG_LITTLE_ENDIAN (1).
+    * @details The endianness cannot be changed after file output has started.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+    * could not be set.
+    */
+    tng_function_status setOutputFileEndianness
+                    (const tng_file_endianness endianness)
+    {
+        return status = tng_output_file_endianness_set(traj, endianness);
+    }
+
+    /**
+    * @brief Get the name of the program used when creating the trajectory.
+    * @param name the string to fill with the name of the program,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstProgramName(char *name, const int max_len)
+    {
+        return status = tng_first_program_name_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the program used when creating the trajectory..
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstProgramName(const char *new_name)
+    {
+        return status = tng_first_program_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the program used when last modifying the trajectory.
+    * @param name the string to fill with the name of the program,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastProgramName(char *name, const int max_len)
+    {
+        return status = tng_last_program_name_get(traj, name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the program used when last modifying the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastProgramName(const char *new_name)
+    {
+        return status = tng_last_program_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the user who created the trajectory.
+    * @param name the string to fill with the name of the user,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstUserName(char *name, const int max_len)
+    {
+        return status = tng_first_user_name_get(traj,name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the user who created the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstUserName(const char *new_name)
+    {
+        return status = tng_first_user_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the user who last modified the trajectory.
+    * @param name the string to fill with the name of the user,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastUserName(char *name, const int max_len)
+    {
+        return status = tng_last_user_name_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the user who last modified the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastUserName(const char *new_name)
+    {
+        return status = tng_last_user_name_set(traj,new_name);
+    }
+
+
+
+    /**
+    * @brief Get the name of the computer used when creating the trajectory.
+    * @param name the string to fill with the name of the computer,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstComputerName(char *name, const int max_len)
+    {
+        return status = tng_first_computer_name_get(traj, name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the computer used when creating the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstComputerName(const char *new_name)
+    {
+        return status = tng_first_computer_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the computer used when last modifying the trajectory.
+    * @param name the string to fill with the name of the computer,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastComputerName(char *name, const int max_len)
+    {
+        return status = tng_last_computer_name_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the computer used when last modifying the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastComputerName(const char *new_name)
+    {
+        return status = tng_last_computer_name_set(traj,new_name);
+    }
+
+
+    /**
+    * @brief Get the pgp_signature of the user creating the trajectory.
+    * @param signature the string to fill with the signature,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstSignature(char *signature, const int max_len)
+    {
+        return status = tng_last_computer_name_get(traj, signature,max_len);
+    }
+
+
+    /**
+    * @brief Set the pgp_signature of the user creating the trajectory.
+    * @param signature is a string containing the pgp_signature.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstSignature(const char *signature)
+    {
+        return status = tng_first_signature_set(traj, signature);
+    }
+
+
+    /**
+    * @brief Get the pgp_signature of the user last modifying the trajectory.
+    * @param signature the string to fill with the signature,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastSignature(char *signature, const int max_len)
+    {
+        return status = tng_first_signature_get(traj, signature, max_len);
+    }
+
+
+    /**
+    * @brief Set the pgp_signature of the user last modifying the trajectory.
+    * @param signature is a string containing the pgp_signature.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastSignature(const char *signature)
+    {
+        return status = tng_last_signature_set(traj, signature);
+    }
+
+
+    /**
+    * @brief Get the name of the forcefield used in the trajectory.
+    * @param name the string to fill with the name of the forcefield,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getForcefieldName(char *name, const int max_len)
+    {
+        return status = tng_last_signature_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the forcefield used in the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setForcefieldName(const char *new_name)
+    {
+        return status = tng_forcefield_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the medium stride length of the trajectory.
+    * @param len is pointing to a value set to the stride length.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getMediumStrideLength(int64_t *len)
+    {
+        return status = tng_medium_stride_length_get(traj,len);
+    }
+
+
+    /**
+    * @brief Set the medium stride length of the trajectory.
+    * @param len is the wanted medium stride length.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred.
+    */
+    tng_function_status setMediumStrideLength(const int64_t len)
+    {
+        return status = tng_medium_stride_length_set(traj,len);
+    }
+
+
+    /**
+    * @brief Get the long stride length of the trajectory.
+    * @param len is pointing to a value set to the stride length.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getLongStrideLength(int64_t *len)
+    {
+        return status = tng_long_stride_length_get(traj, len);
+    }
+
+
+    /**
+    * @brief Set the long stride length of the trajectory.
+    * @param len is the wanted long stride length.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred.
+    */
+    tng_function_status setLongStrideLength(const int64_t len)
+    {
+        return status = tng_long_stride_length_set(traj,len);
+    }
+
+
+    /**
+    * @brief Get the current time per frame of the trajectory.
+    * @param len is pointing to a value set to the time per frame.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getTimePerFrame(double *time)
+    {
+        return status = tng_time_per_frame_get(traj, time);
+    }
+
+
+    /**
+    * @brief Set the time per frame of the trajectory.
+    * @param len is the new time per frame.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred.
+    */
+    tng_function_status setTimePerFrame(const double time)
+    {
+        return status = tng_time_per_frame_set(traj, time);
+    }
+
+
+    /**
+    * @brief Get the length of the input file.
+    * @param len is pointing to a value set to the file length.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getInputFileLen(int64_t *len)
+    {
+        return status = tng_input_file_len_get(traj, len);
+    }
+
+
+    /**
+    * @brief Get the number of frames in the trajectory
+    * @param n is pointing to a value set to the number of frames.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (could not find last frame set).
+    */
+    tng_function_status getNumFrames(int64_t *n)
+    {
+        return status = tng_num_frames_get(traj, n);
+    }
+
+    /**
+    * @brief Get the current number of particles.
+    * @param n is pointing to a value set to the number of particles.
+    * @details If variable number of particles are used this function will return
+    * the number of particles in the current frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNumParticles(int64_t *n)
+    {
+        return status = tng_num_particles_get(traj, n);
+    }
+
+
+
+
+    /**
+    * @brief Get the current total number of molecules.
+    * @param n is pointing to a value set to the number of molecules.
+    * @details If variable number of particles are used this function will return
+    * the total number of molecules in the current frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNumMolecules(int64_t *n)
+    {
+        return status = tng_num_molecules_get(traj,n);
+    }
+
+    /**
+    * @brief Get the exponential used for distances in the trajectory.
+    * @param exp is pointing to a value set to the distance unit exponential.
+    * @details Example: If the distances are specified in nm (default) exp is -9.
+    * If the distances are specified in Å exp is -10.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getDistanceUnitExponential
+                    (int64_t *exp)
+    {
+        return status = tng_distance_unit_exponential_get(traj, exp);
+    }
+
+    /**
+    * @brief Set the exponential used for distances in the trajectory.
+    * @param exp is the distance unit exponential to use.
+    * @details Example: If the distances are specified in nm (default) exp is -9.
+    * If the distances are specified in Å exp is -10.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status setDistanceUnitExponential
+                    (int64_t exp)
+    {
+        return status = tng_distance_unit_exponential_set(traj, exp);
+    }
+
+
+    /**
+    * @brief Get the number of frames per frame set.
+    * per frame set.
+    * @param n is pointing to a value set to the number of frames per frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNumFramesPerFrameSet(int64_t *n)
+    {
+        return status = tng_num_frames_per_frame_set_get(traj,n);
+    }
+
+    /**
+    * @brief Set the number of frames per frame set.
+    * @param n is the number of frames per frame set.
+    * @details This does not affect already existing frame sets. For
+    * consistency the number of frames per frame set should be set
+    * betfore creating any frame sets.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status setNumFramesPerFrameSet(const int64_t n)
+    {
+        return status = tng_num_frames_per_frame_set_set(traj,n);
+    }
+
+    /**
+    * @brief Get the number of frame sets.
+    * @param n is pointing to a value set to the number of frame sets.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getNumFrameSets(int64_t *n)
+    {
+        return status = tng_num_frame_sets_get(traj, n);
+    }
+
+
+    /**
+    * @brief Get the current trajectory frame set.
+    * @param frame_set_p will be set to point at the memory position of
+    * the found frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getCurrentFrameSet(tng_trajectory_frame_set_t *frame_set_p)
+    {
+        return status = tng_current_frame_set_get(traj, frame_set_p);
+    }
+
+
+    /**
+    * @brief Find the requested frame set number.
+    * @param nr is the frame set number to search for.
+    * @details tng_data->current_trajectory_frame_set will contain the
+    * found trajectory if successful.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status findFrameSetNr(const int64_t nr)
+    {
+        return status = tng_frame_set_nr_find(traj,nr);
+    }
+
+
+    /**
+    * @brief Find the frame set containing a specific frame.
+    * @param frame is the frame number to search for.
+    * @details tng_data->current_trajectory_frame_set will contain the
+    * found trajectory if successful.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status findFrameSetOfFrame(const int64_t frame)
+    {
+        return status = tng_frame_set_of_frame_find(traj, frame);
+    }
+
+
+    /**
+    * @brief Get the file position of the next frame set in the input file.
+    * @param frame_set is the frame set of which to get the position of the
+    * following frame set.
+    * @param pos is pointing to a value set to the file position.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNextFrameSetFilePos
+        (const tng_trajectory_frame_set_t frame_set,int64_t *pos)
+    {
+        return status = tng_frame_set_next_frame_set_file_pos_get(traj,frame_set,pos );
+    }
+
+    /**
+    * @brief Get the file position of the previous frame set in the input file.
+    * @param frame_set is the frame set of which to get the position of the
+    * previous frame set.
+    * @param pos is pointing to a value set to the file position.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getPrevFrameSetFilePos
+        (const tng_trajectory_frame_set_t frame_set,int64_t *pos)
+    {
+        return status = tng_frame_set_prev_frame_set_file_pos_get(traj, frame_set, pos);
+    }
+
+
+    /**
+    * @brief Get the first and last frames of the frame set.
+    * @param frame_set is the frame set of which to get the frame range.
+    * @param first_frame is set to the first frame of the frame set.
+    * @param last_frame is set to the last frame of the frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getFrameSetFrameRange
+        (const tng_trajectory_frame_set_t frame_set,
+        int64_t *first_frame,
+        int64_t *last_frame)
+    {
+        return status = tng_frame_set_frame_range_get(traj,frame_set, first_frame, last_frame);
+    }
+
+
+    /**
+    * @brief Get the molecume name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the molecule. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getMoleculeNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_molecule_name_of_particle_nr_get(traj,nr,name,max_len);
+    }
+
+
+    /**
+    * @brief Get the chain name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the chain. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getChainNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_chain_name_of_particle_nr_get(traj, nr, name, max_len);
+    }
+
+
+    /**
+    * @brief Get the residue name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the residue. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getResidueNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_residue_name_of_particle_nr_get(traj,nr,name,max_len);
+    }
+
+
+    /**
+    * @brief Get the atom name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the atom. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getAtomNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_atom_name_of_particle_nr_get(traj, nr,name,max_len);
+    }
+
+
+    /**
+    * @brief Add a particle mapping table.
+    * @details Each particle mapping table will be written as a separate block,
+    * followed by the data blocks for the corresponding particles. In most cases
+    * there is one particle mapping block for each thread writing the trajectory.
+    * @details The mapping information is added to the currently active frame set
+    * of tng_data
+    * @param first_particle_number is the first particle number of this mapping
+    * block.
+    * @param n_particles is the number of particles in this mapping block.
+    * @param mapping_table is a list of the real particle numbers (i.e. the numbers
+    * used in the molecular system). The list is n_particles long.
+    * @details mapping_table[0] is the real particle number of the first particle
+    * in the following data blocks.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addParticleMapping
+        (const int64_t first_particle_number,
+        const int64_t n_particles,
+        const int64_t *mapping_table)
+    {
+        return status = tng_particle_mapping_add(traj,first_particle_number,n_particles,mapping_table );
+    }
+
+
+    /**
+    * @brief Read the header blocks from the input_file of tng_data.
+    * @details The trajectory blocks must be read separately and iteratively in chunks
+    * to fit in memory.
+    * @details tng_data->input_file_path specifies
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status readFileHeaders(const tng_hash_mode hash_mode)
+    {
+        return status = tng_file_headers_read(traj, hash_mode);
+    }
+
+
+    /**
+    * @brief Write the header blocks to the output_file of tng_data.
+    * @details The trajectory blocks must be written separately and iteratively in chunks
+    * to fit in memory.
+    * @details tng_data->output_file_path
+    * specifies which file to write to. If the file (output_file) is not open it
+    * will be opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status writeFileHeaders(const tng_hash_mode hash_mode)
+    {
+        return status = tng_file_headers_write(traj, hash_mode);
+    }
+
+
+
+    /**
+    * @brief Read one (the next) block (of any kind) from the input_file of tng_data.
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param block_data is a pointer to the struct which will be populated with the
+    * data.
+    * @details If block_data->input_file_pos > 0 it is the position from where the
+    * reading starts otherwise it starts from the current position.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status readNextBlock(const tng_hash_mode hash_mode, tng_gen_block_t block_data)
+    {
+        return status = tng_block_read_next(traj,block_data, hash_mode);
+    }
+
+
+
+    /**
+    * @brief Read one (the next) frame set, including mapping and related data blocks
+    * from the input_file of tng_data.
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status readNextFrameSet(const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_set_read_next(traj, hash_mode);
+    }
+
+
+    /**
+    * @brief Write one frame set, including mapping and related data blocks
+    * to the output_file of tng_data.
+    * @details  tng_data->output_file_path specifies
+    * which file to write to. If the file (output_file) is not open it will be
+    * opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status writeFrameSet(const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_set_write(traj, hash_mode);
+    }
+
+
+    /**
+    * @brief Create and initialise a frame set.
+    * @param first_frame is the first frame of the frame set.
+    * @param n_frames is the number of frames in the frame set.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status newFrameSet(const int64_t first_frame,
+        const int64_t n_frames)
+    {
+        return status =  tng_frame_set_new(traj, first_frame, n_frames);
+    }
+
+
+    /**
+    * @brief Create and initialise a frame set with the time of the first frame
+    * specified.
+    * @param first_frame is the first frame of the frame set.
+    * @param n_frames is the number of frames in the frame set.
+    * @param first_frame_time is the time stamp of the first frame (in seconds).
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status newFrameSetWithTime
+                (const int64_t first_frame,
+                 const int64_t n_frames,
+                 const double first_frame_time)
+    {
+        return status =  tng_frame_set_with_time_new(traj,
+                                                     first_frame, n_frames,
+                                                     first_frame_time);
+    }
+
+
+    /**
+    * @brief Set the time stamp of the first frame of the current frame set.
+    * @param first_frame_time is the time stamp of the first frame in the
+    * frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status setTimeOfFirstFrameOfFrameSet
+                    (const double first_frame_time)
+    {
+        return status = tng_frame_set_first_frame_time_set(traj,
+                                                           first_frame_time);
+    }
+
+    /**
+    * @brief Add a non-particle dependent data block.
+    * @param id is the block ID of the block to add.
+    * @param block_name is a descriptive name of the block to add
+    * @param datatype is the datatype of the data in the block (e.g. int/float)
+    * @param block_type_flag indicates if this is a non-trajectory block (added
+    * directly to tng_data) or if it is a trajectory block (added to the
+    * frame set)
+    * @param n_frames is the number of frames of the data block (automatically
+    * set to 1 if adding a non-trajectory data block)
+    * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+    * for a box shape block)
+    * @param stride_length is how many frames are between each entry in the
+    * data block
+    * @param codec_id is the ID of the codec to compress the data.
+    * @param new_data is an array of data values to add.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addDataBlock(const int64_t id,
+        const char *block_name,
+        const tng_data_type datatype,
+        const tng_block_type block_type_flag,
+        int64_t n_frames,
+        const int64_t n_values_per_frame,
+        const int64_t stride_length,
+        const int64_t codec_id,
+        void *new_data)
+    {
+        return status = tng_data_block_add(traj, id,block_name,
+            datatype,block_type_flag, n_frames,
+            n_values_per_frame, stride_length,
+            codec_id, new_data);
+    }
+
+
+    /**
+    * @brief Add a particle dependent data block.
+    * @param id is the block ID of the block to add.
+    * @param block_name is a descriptive name of the block to add
+    * @param datatype is the datatype of the data in the block (e.g. int/float)
+    * @param block_type_flag indicates if this is a non-trajectory block (added
+    * directly to tng_data) or if it is a trajectory block (added to the
+    * frame set)
+    * @param n_frames is the number of frames of the data block (automatically
+    * set to 1 if adding a non-trajectory data block)
+    * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+    * for a box shape block)
+    * @param stride_length is how many frames are between each entry in the
+    * data block
+    * @param first_particle_number is the number of the first particle stored
+    * in this data block
+    * @param n_particles is the number of particles stored in this data block
+    * @param codec_id is the ID of the codec to compress the data.
+    * @param new_data is an array of data values to add.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addParticleDataBlock(const int64_t id,
+        const char *block_name,
+        const tng_data_type datatype,
+        const tng_block_type block_type_flag,
+        int64_t n_frames,
+        const int64_t n_values_per_frame,
+        const int64_t stride_length,
+        const int64_t first_particle_number,
+        const int64_t n_particles,
+        const int64_t codec_id,
+        void *new_data)
+    {
+        return status = tng_particle_data_block_add(traj,id,    block_name,
+            datatype, block_type_flag, n_frames,n_values_per_frame,
+            stride_length,first_particle_number,n_particles,
+            codec_id, new_data);
+    }
+
+
+    /**
+    * @brief Write data of one trajectory frame to the output_file of tng_data.
+    * @param frame_nr is the index number of the frame to write.
+    * @param block_id is the ID of the data block to write the data to.
+    * @param data is an array of data to write. The length of the array should
+    * equal n_values_per_frame.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status writeFrameData(const int64_t frame_nr,
+        const int64_t block_id,
+        const void *data,
+        const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_data_write(traj,frame_nr,block_id,data,hash_mode);
+    }
+
+
+    /**
+    * @brief Write particle data of one trajectory frame to the output_file of
+    * tng_data.
+    * @param frame_nr is the index number of the frame to write.
+    * @param block_id is the ID of the data block to write the data to.
+    * @param val_first_particle is the number of the first particle in the data
+    * array.
+    * @param val_n_particles is the number of particles in the data array.
+    * @param data is a 1D-array of data to write. The length of the array should
+    * equal n_particles * n_values_per_frame.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status writeFrameParticleData(const int64_t frame_nr,
+        const int64_t block_id,
+        const int64_t val_first_particle,
+        const int64_t val_n_particles,
+        const void *data,
+        const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_particle_data_write(traj,frame_nr,block_id,val_first_particle,val_n_particles,data,hash_mode);
+    }
+
+
+    /**
+    * @brief Free data is an array of values (2D).
+    * @param values is the 2D array to free and will be set to 0 afterwards.
+    * @param n_frames is the number of frames in the data array.
+    * @param n_values_per_frame is the number of values per frame in the data array.
+    * @param type is the data type of the data in the array (e.g. int/float/char).
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status freeDataValues(union data_values **values,
+        const int64_t n_frames,
+        const int64_t n_values_per_frame,
+        const tng_data_type type)
+    {
+        return status = tng_data_values_free(traj, values, n_frames,n_values_per_frame,type);
+    }
+
+
+    /**
+    * @brief Free data is an array of values (3D).
+    * @param values is the array to free and will be set to 0 afterwards.
+    * @param n_frames is the number of frames in the data array.
+    * @param n_particles is the number of particles in the data array.
+    * @param n_values_per_frame is the number of values per frame in the data array.
+    * @param type is the data type of the data in the array (e.g. int/float/char).
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status freeParticleDataValues(union data_values ***values,
+        const int64_t n_frames,
+        const int64_t n_particles,
+        const int64_t n_values_per_frame,
+        const tng_data_type type)
+    {
+        return status = tng_particle_data_values_free(traj, values,n_frames,n_particles,n_values_per_frame,type);
+    }
+
+
+    /**
+    * @brief Retrieve non-particle data, from the last read frame set. Obsolete!
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_values_per_frame).
+    * Since ***values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getData(const int64_t block_id,
+        union data_values ***values,
+        int64_t *n_frames,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = tng_data_get(traj, block_id, values, n_frames,
+                                     n_values_per_frame, type);
+    }
+
+    /**
+    * @brief Retrieve a vector (1D array) of non-particle data, from the last read frame set.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be (n_frames * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param stride_length is set to the stride length of the returned data.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getDataVector
+                (const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_data_vector_get(traj, block_id, values, n_frames,
+                                            stride_length, n_values_per_frame,
+                                            type);
+    }
+
+    /**
+    * @brief Read and retrieve non-particle data, in a specific interval. Obsolete!
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_values_per_frame).
+    * Since ***values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getDataInterval(const int64_t block_id,
+        const int64_t start_frame_nr,
+        const int64_t end_frame_nr,
+        const tng_hash_mode hash_mode,
+        union data_values ***values,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = tng_data_interval_get(traj, block_id, start_frame_nr,
+                                              end_frame_nr, hash_mode, values,
+                                              n_values_per_frame, type);
+    }
+
+
+    /**
+    * @brief Read and retrieve a vector (1D array) of non-particle data,
+    * in a specific interval.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be (n_frames * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param stride_length is set to the stride length (writing frequency) of
+    * the data.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getDataVectorInterval
+                (const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_data_vector_interval_get(traj, block_id,
+                                                     start_frame_nr,
+                                                     end_frame_nr,
+                                                     hash_mode, values,
+                                                     stride_length,
+                                                     n_values_per_frame,
+                                                     type);
+    }
+
+    /**
+    * @brief Retrieve particle data, from the last read frame set. Obsolete!
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * specifies which file to read from. If the file (input_file) is not open it
+    * will be opened.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since ****values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleData(const int64_t block_id,
+        union data_values ****values,
+        int64_t *n_frames,
+        int64_t *n_particles,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = (tng_particle_data_get(traj, block_id, values, n_frames,
+            n_particles, n_values_per_frame, type));
+    }
+
+
+    /**
+    * @brief Retrieve a vector (1D array) of particle data, from the last read frame set.
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of frames in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param stride_length is set to the stride length of the returned data.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleDataVector
+                (const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_particle_data_vector_get(traj, block_id,
+                                                     values, n_frames,
+                                                     stride_length,
+                                                     n_particles,
+                                                     n_values_per_frame, type);
+    }
+
+
+    /**
+    * @brief Read and retrieve particle data, in a specific interval. Obsolete!
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since ****values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleDataInterval(const int64_t block_id,
+        const int64_t start_frame_nr,
+        const int64_t end_frame_nr,
+        const tng_hash_mode hash_mode,
+        union data_values ****values,
+        int64_t *n_particles,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = (tng_particle_data_interval_get(traj, block_id, start_frame_nr,
+            end_frame_nr, hash_mode, values,
+            n_particles, n_values_per_frame,
+            type));
+    }
+
+
+    /**
+    * @brief Read and retrieve a vector (1D array) particle data, in a
+    * specific interval.
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param stride_length is set to the stride length (writing frequency) of
+    * the data.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleDataVectorInterval
+                (const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const tng_hash_mode hash_mode,
+                 void **values,
+                 int64_t *n_particles,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_particle_data_vector_interval_get(traj, block_id,
+                                                              start_frame_nr,
+                                                              end_frame_nr,
+                                                              hash_mode,
+                                                              values,
+                                                              n_particles,
+                                                              stride_length,
+                                                              n_values_per_frame,
+                                                              type);
+    }
+
+
+    /** @brief Get the date and time of initial file creation in ISO format (string).
+    *  @param time is a pointer to the string in which the date will be stored. Memory
+    must be reserved beforehand.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getTimeStr(char *time)
+    {
+        return status = tng_time_get_str(traj, time);
+    }
+
+
+};
+
+
+
+
+
+class Molecule
+{
+private:
+
+    tng_molecule_t mol;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    tng_function_status addChain(const char *name, Chain *chain);
+    tng_function_status findChain(const char *name, int64_t id, Chain *chain);
+    friend class Trajectory;
+    //Constructor
+    Molecule(Trajectory * trajectory)
+    {
+        traj = trajectory;
+
+        //status = tng_molecule_init(traj->traj,mol);
+    }
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+    ~Molecule()
+    {
+        status = tng_molecule_destroy(traj->traj,mol);
+    }
+        //! Status
+    tng_function_status getStatus()
+    { return status; }
+
+
+    /**
+    * @brief Set the name of a molecule.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status = tng_molecule_name_set(traj->traj,mol,new_name);
+    }
+
+    /**
+    * @brief Get the count of a molecule.
+    * @param cnt is a pointer to the variable to be populated with the count.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getCnt(int64_t *cnt)
+    {
+        return status = tng_molecule_cnt_get(traj->traj,mol,cnt);
+    }
+
+    /**
+    * @brief Set the count of a molecule.
+    * @param cnt is the number of instances of this molecule.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status setCnt(int64_t cnt)
+    {
+        return status = tng_molecule_cnt_set(traj->traj,mol,cnt);
+    }
+
+};
+
+    tng_function_status Trajectory::addMolecule(const char *name, Molecule_t molecule)
+{
+    return status = tng_molecule_add(traj,name, &molecule->mol);
+}
+
+    tng_function_status Trajectory::addMoleculeWithId
+                (const char *name,
+                 const int64_t id,
+                 Molecule_t molecule)
+{
+    return status = tng_molecule_w_id_add(traj, name, id, &molecule->mol);
+}
+
+/**
+* @brief Find a molecule.
+* @param tng_data is the trajectory data container containing the molecule.
+* @param name is a string containing the name of the molecule. If name is empty
+* only id will be used for finding the molecule.
+* @param id is the id of the molecule to look for. If id is -1 only the name of
+* the molecule will be used for finding the molecule.
+* @param molecule is a pointer to the molecule if it was found - otherwise 0.
+* @return TNG_SUCCESS (0) if the molecule is found or TNG_FAILURE (1) if the
+* molecule is not found.
+* @details If name is an empty string and id is -1 the first molecule will be
+* found.
+*/
+tng_function_status Trajectory::findMolecule
+                (const char *name,
+                int64_t id,
+                Molecule_t molecule)
+{
+    return status = tng_molecule_find(traj, name, id,
+                            &molecule->mol);
+}
+
+
+class Atom
+{
+private:
+    tng_atom_t atom;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    friend class Residue;
+    //constructor
+    Atom(Trajectory * trajectory)
+    {
+        traj = trajectory;
+    }
+    //deonstructor
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+        ~Atom()
+    {
+        //delete atom;
+    }
+            //! Status
+    tng_function_status getStatus()
+    { return status; }
+    /**
+    * @brief Set the name of an atom.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status = tng_atom_name_set(traj->traj, atom , new_name);
+    }
+
+    /**
+    * @param new_type is a string containing the atom type.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setType(const char *new_type)
+    {
+        return status = tng_atom_type_set(traj->traj, atom, new_type);
+    }
+};
+
+class Residue
+{
+    private:
+    tng_residue_t residue;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    friend class Chain;
+    //constructor
+    Residue(Trajectory  * trajectory)
+    {
+        traj = trajectory;
+    }
+    //deonstructor
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+        ~Residue()
+    {
+        //delete residue;
+    }
+    //! Status
+    tng_function_status getStatus()
+    { return status; }
+    /**
+    * @brief Set the name of a residue.
+    * @param residue is the residue to rename.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status = tng_residue_name_set(traj->traj, residue,new_name);
+    }
+
+    /**
+    * @brief Add an atom to a residue.
+    * @param atom_name is a string containing the name of the atom.
+    * @param atom_type is a string containing the atom type of the atom.
+    * @param atom is a pointer to the newly created atom.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status addAtom(const char *atom_name,
+        const char *atom_type,
+        Atom * atom)
+    {
+        return status = tng_residue_atom_add(traj->traj, residue, atom_name,
+                                             atom_type, &atom->atom);
+    }
+
+
+    /**
+    * @brief Add an atom with a specific ID to a residue.
+    * @param atom_name is a string containing the name of the atom.
+    * @param atom_type is a string containing the atom type of the atom.
+    * @param id is the ID of the created atom.
+    * @param atom is a pointer to the newly created atom.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+    * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addAtomWithId
+                (const char *atom_name,
+                 const char *atom_type,
+                 const int64_t id,
+                 Atom * atom)
+    {
+        return status = tng_residue_atom_w_id_add(traj->traj, residue,
+                                                  atom_name, atom_type,
+                                                  id, &atom->atom);
+    }
+
+};
+
+class Chain
+{
+    private:
+    tng_chain_t chain;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    friend class Molecule;
+    //constructor
+    Chain(Trajectory * trajectory)
+    {
+        traj = trajectory;
+    }
+    //deonstructor
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+        ~Chain()
+    {
+        //delete chain;
+    }
+    //! Status
+    tng_function_status getStatus()
+    { return status; }
+    /**
+    * @brief Set the name of a chain.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status  = tng_chain_name_set(traj->traj, chain, new_name);
+    }
+
+
+    /**
+    * @brief Find a residue in a chain.
+    * @param name is a string containing the name of the residue.
+    * @param id is the id of the residue to find. If id == -1 the first residue
+    * that matches the specified name will be found.
+    * @param residue is a pointer to the residue if it was found - otherwise 0.
+    * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+    * residue is not found.
+    * @details If name is an empty string the first residue will be found.
+    */
+    tng_function_status findResidue
+                (const char *name,
+                 int64_t id,
+                 Residue *residue)
+    {
+        return status = tng_chain_residue_find(traj->traj, chain, name,
+                                               id, &residue->residue);
+    }
+
+    /**
+    * @brief Add a residue to a chain.
+    * @param name is a string containing the name of the residue.
+    * @param residue is a pointer to the newly created residue.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status addResidue(const char *name,
+        Residue * residue)
+    {
+        return status = tng_chain_residue_add(traj->traj, chain,
+                                              name, &residue->residue);
+    }
+
+    /**
+    * @brief Add a residue with a specific ID to a chain.
+    * @param name is a string containing the name of the residue.
+    * @param id is the ID of the created residue.
+    * @param residue is a pointer to the newly created residue.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+    * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addResidueWithId
+                (const char *name,
+                 const int64_t id,
+                 Residue * residue)
+    {
+        return status = tng_chain_residue_w_id_add(traj->traj, chain,
+                                                   name, id, &residue->residue);
+    }
+};
+
+/**
+* @brief Add a chain to a molecule.
+* @param name is a string containing the name of the chain.
+* @param chain s a pointer to the newly created chain.
+* @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+* error has occured.
+*/
+tng_function_status Molecule::addChain(const char *name, Chain *chain)
+{
+    return status = tng_molecule_chain_add(traj->traj,mol,name,&chain->chain);
+}
+
+/**
+* @brief Find a chain in a molecule.
+* @param name is a string containing the name of the chain. If name is empty
+* only id will be used for finding the chain.
+* @param id is the id of the chain to look for. If id is -1 only the name of
+* the chain will be used for finding the chain.
+* @param chain is a pointer to the chain if it was found - otherwise 0.
+* @return TNG_SUCCESS (0) if the chain is found or TNG_FAILURE (1) if the
+* chain is not found.
+* @details If name is an empty string and id is -1 the first chain will be
+* found.
+*/
+tng_function_status Molecule::findChain
+                (const char *name,
+                int64_t id,
+                Chain *chain)
+{
+    return status = tng_molecule_chain_find(traj->traj, mol, name, id,
+                                            &chain->chain);
+}
+
+}
+#endif
diff --git a/src/external/tng_io/include/tng_io_fwd.h b/src/external/tng_io/include/tng_io_fwd.h
new file mode 100644 (file)
index 0000000..340c264
--- /dev/null
@@ -0,0 +1,41 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.4
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef TNG_IO_FWD_H
+#define TNG_IO_FWD_H     1
+
+/** A pointer to the main trajectory data storage. */
+typedef struct tng_trajectory *tng_trajectory_t;
+/** A pointer to a molecule description. */
+typedef struct tng_molecule *tng_molecule_t;
+/** A pointer to a molecular chain description. */
+typedef struct tng_chain *tng_chain_t;
+/** A pointer to a molecular residue description. */
+typedef struct tng_residue *tng_residue_t;
+/** A pointer to a molecular atom description. */
+typedef struct tng_atom *tng_atom_t;
+/** A pointer to a bond between two atoms. */
+typedef struct tng_bond *tng_bond_t;
+/** A pointer to a structure containing data common to all trajectory blocks,
+ *  such as header and contents. */
+typedef struct tng_gen_block *tng_gen_block_t;
+/** A pointer to particle mapping information. */
+typedef struct tng_particle_mapping *tng_particle_mapping_t;
+/** A pointer to a structure containing frame set information. */
+typedef struct tng_trajectory_frame_set *tng_trajectory_frame_set_t;
+/** A pointer to a particle data container. */
+typedef struct tng_particle_data *tng_particle_data_t;
+/** A pointer to a non-particle data container. */
+typedef struct tng_non_particle_data *tng_non_particle_data_t;
+
+#endif
diff --git a/src/external/tng_io/src/CMakeLists.txt b/src/external/tng_io/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d5f2bc0
--- /dev/null
@@ -0,0 +1,3 @@
+add_subdirectory(compression)
+add_subdirectory(lib)
+add_subdirectory(tests)
diff --git a/src/external/tng_io/src/compression/CMakeLists.txt b/src/external/tng_io/src/compression/CMakeLists.txt
new file mode 100644 (file)
index 0000000..267a45a
--- /dev/null
@@ -0,0 +1,11 @@
+add_library(tng_compress bwlzh.c bwt.c coder.c dict.c fixpoint.c huffman.c huffmem.c lz77.c merge_sort.c mtf.c rle.c tng_compress.c vals16.c warnmalloc.c widemuldiv.c xtc2.c xtc3.c)
+if(UNIX)
+target_link_libraries(tng_compress m)
+endif()
+
+set_property(TARGET tng_compress PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
+set_property(TARGET tng_compress PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
+
+install(TARGETS tng_compress
+        LIBRARY DESTINATION lib
+        ARCHIVE DESTINATION lib)
diff --git a/src/external/tng_io/src/compression/bwlzh.c b/src/external/tng_io/src/compression/bwlzh.c
new file mode 100644 (file)
index 0000000..de009d9
--- /dev/null
@@ -0,0 +1,799 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/tng_compress.h"
+#include "../../include/compression/bwlzh.h"
+#include "../../include/compression/dict.h"
+#include "../../include/compression/vals16.h"
+#include "../../include/compression/rle.h"
+#include "../../include/compression/mtf.h"
+#include "../../include/compression/bwt.h"
+#include "../../include/compression/lz77.h"
+
+#if 0
+#define SHOWIT
+#endif
+
+#if 0
+#define SHOWTEST
+#endif
+
+#define MAX_VALS_PER_BLOCK 200000
+
+#if 1
+#define PARTIAL_MTF3
+#endif
+
+#if 0
+#define PARTIAL_MTF
+#endif
+
+int bwlzh_get_buflen(int nvals)
+{
+  return 132000+nvals*8+12*((nvals+MAX_VALS_PER_BLOCK)/MAX_VALS_PER_BLOCK);
+}
+
+#ifdef SHOWIT
+static void printvals(char *name, unsigned int *vals, int nvals)
+{
+  int i;
+  int nvalsmax=nvals;
+  if (nvalsmax>99)
+    nvalsmax=99;
+#if 0
+  for (i=0; i<nvalsmax; i++)
+    fprintf(stderr,"%s %d %u\n",name,i,vals[i]);
+#else
+  fprintf(stderr,"%s\n",name);
+  {
+    unsigned char *x=(unsigned char*)vals;
+    for (i=0; i<nvalsmax*4; i++)
+      fprintf(stderr,"%02x",(unsigned int)x[i]);
+    fprintf(stderr,"\n");
+  }
+#endif
+
+}
+#endif
+
+
+static void bwlzh_compress_gen(unsigned int *vals, int nvals,
+                               unsigned char *output, int *output_len,
+                               int enable_lz77,
+                               int verbose)
+{
+  unsigned int *vals16;
+  int nvals16;
+  int huffdatalen;
+  int nhufflen[N_HUFFMAN_ALGO];
+  int huffalgo;
+  int bwt_index;
+  unsigned int *bwt=NULL;
+#ifdef PARTIAL_MTF3
+  unsigned char *mtf3=NULL;
+  int imtfinner;
+#endif
+  unsigned int *mtf=NULL;
+  unsigned int *rle=NULL;
+  unsigned int *offsets=NULL;
+  unsigned int *lens=NULL;
+  unsigned int *dict=warnmalloc(0x20004*sizeof *dict);
+  unsigned int *hist=warnmalloc(0x20004*sizeof *hist);
+  int nrle;
+  int noffsets;
+  int nlens;
+  unsigned char *bwlzhhuff=NULL;
+  int bwlzhhufflen;
+  int max_vals_per_block=MAX_VALS_PER_BLOCK;
+  int valsleft;
+  int thisvals;
+  int valstart;
+  int outdata=0;
+
+  unsigned int *tmpmem=warnmalloc(max_vals_per_block*18*sizeof *tmpmem);
+
+#if 0
+  verbose=1;
+#endif
+
+  bwlzhhuff=warnmalloc(Ptngc_comp_huff_buflen(3*nvals));
+  vals16=tmpmem;
+  bwt=tmpmem+max_vals_per_block*3;
+  mtf=tmpmem+max_vals_per_block*6;
+  rle=tmpmem+max_vals_per_block*9;
+  offsets=tmpmem+max_vals_per_block*12;
+  lens=tmpmem+max_vals_per_block*15;
+#ifdef PARTIAL_MTF3
+  mtf3=warnmalloc(max_vals_per_block*3*3*sizeof *mtf3); /* 3 due to expansion of 32 bit to 16 bit, 3 due to up to 3 bytes
+                                                           per 16 value. */
+#endif
+  if (verbose)
+    {
+      fprintf(stderr,"Number of input values: %d\n",nvals);
+    }
+
+  /* Store the number of real values in the whole block. */
+  output[outdata++]=((unsigned int)nvals)&0xFFU;
+  output[outdata++]=(((unsigned int)nvals)>>8)&0xFFU;
+  output[outdata++]=(((unsigned int)nvals)>>16)&0xFFU;
+  output[outdata++]=(((unsigned int)nvals)>>24)&0xFFU;
+
+  valsleft=nvals;
+  valstart=0;
+  while (valsleft)
+    {
+      int reducealgo=1; /* Reduce algo is LZ77. */
+      if (!enable_lz77)
+        reducealgo=0;
+      thisvals=valsleft;
+      if (thisvals>max_vals_per_block)
+        thisvals=max_vals_per_block;
+      valsleft-=thisvals;
+      if (verbose)
+        fprintf(stderr,"Creating vals16 block from %d values.\n",thisvals);
+
+#ifdef SHOWIT
+      printvals("vals",vals+valstart,thisvals);
+#endif
+
+      Ptngc_comp_conv_to_vals16(vals+valstart,thisvals,vals16,&nvals16);
+      valstart+=thisvals;
+
+#ifdef SHOWTEST
+  nvals16=99;
+#endif
+
+#ifdef SHOWIT
+      printvals("vals16",vals16,nvals16);
+#endif
+
+      if (verbose)
+        {
+          fprintf(stderr,"Resulting vals16 values: %d\n",nvals16);
+        }
+      if (verbose)
+        {
+          fprintf(stderr,"BWT\n");
+        }
+      Ptngc_comp_to_bwt(vals16,nvals16,bwt,&bwt_index);
+
+#ifdef SHOWIT
+      printvals("bwt",bwt,nvals16);
+      fprintf(stderr,"BWT INDEX is %d\n",bwt_index);
+#endif
+
+      /* Store the number of real values in this block. */
+      output[outdata++]=((unsigned int)thisvals)&0xFFU;
+      output[outdata++]=(((unsigned int)thisvals)>>8)&0xFFU;
+      output[outdata++]=(((unsigned int)thisvals)>>16)&0xFFU;
+      output[outdata++]=(((unsigned int)thisvals)>>24)&0xFFU;
+
+      /* Store the number of nvals16 values in this block. */
+      output[outdata++]=((unsigned int)nvals16)&0xFFU;
+      output[outdata++]=(((unsigned int)nvals16)>>8)&0xFFU;
+      output[outdata++]=(((unsigned int)nvals16)>>16)&0xFFU;
+      output[outdata++]=(((unsigned int)nvals16)>>24)&0xFFU;
+
+      /* Store the BWT index. */
+      output[outdata++]=((unsigned int)bwt_index)&0xFFU;
+      output[outdata++]=(((unsigned int)bwt_index)>>8)&0xFFU;
+      output[outdata++]=(((unsigned int)bwt_index)>>16)&0xFFU;
+      output[outdata++]=(((unsigned int)bwt_index)>>24)&0xFFU;
+
+      if (verbose)
+        fprintf(stderr,"MTF\n");
+#ifdef PARTIAL_MTF3
+      Ptngc_comp_conv_to_mtf_partial3(bwt,nvals16,
+                                mtf3);
+      for (imtfinner=0; imtfinner<3; imtfinner++)
+        {
+          int i;
+          if (verbose)
+            fprintf(stderr,"Doing partial MTF: %d\n",imtfinner);
+          for (i=0; i<nvals16; i++)
+            mtf[i]=(unsigned int)mtf3[imtfinner*nvals16+i];
+#else
+#ifdef PARTIAL_MTF
+          Ptngc_comp_conv_to_mtf_partial(bwt,nvals16,mtf);
+#else
+      int ndict;
+          Ptngc_comp_canonical_dict(dict,&ndict);
+          Ptngc_comp_conv_to_mtf(bwt,nvals16,
+                           dict,ndict,mtf);
+#endif
+
+#ifdef SHOWIT
+          printvals("mtf",mtf,nvals16);
+#endif
+#endif
+
+
+          if (reducealgo==1)
+            {
+              if (verbose)
+                fprintf(stderr,"LZ77\n");
+              reducealgo=1;
+              Ptngc_comp_to_lz77(mtf,nvals16,rle,&nrle,lens,&nlens,offsets,&noffsets);
+
+              if (verbose)
+                {
+                  fprintf(stderr,"Resulting LZ77 values: %d\n",nrle);
+                  fprintf(stderr,"Resulting LZ77 lens: %d\n",nlens);
+                  fprintf(stderr,"Resulting LZ77 offsets: %d\n",noffsets);
+                }
+#ifdef SHOWIT
+              printvals("lz77 table",rle,nrle);
+              printvals("lz77 lengths",lens,nlens);
+              printvals("lz77 offsets",offsets,noffsets);
+#endif
+
+#if 0
+              if (noffsets)
+                {
+                  unsigned int thist[0x20004];
+                  unsigned int coarse[17]={0,};
+                  int jj;
+                  Ptngc_comp_make_dict_hist(lens,nlens,dict,&ndict,thist);
+                  for (jj=0; jj<ndict; jj++)
+                    fprintf(stderr,"%d %u %u L\n",jj,dict[jj],thist[jj]);
+
+                  Ptngc_comp_make_dict_hist(offsets,noffsets,dict,&ndict,thist);
+                  for (jj=0; jj<ndict; jj++)
+                    {
+                      unsigned int v=dict[jj];
+                      int numbits=0;
+                      while (v)
+                        {
+                          numbits++;
+                          v>>=1;
+                        }
+                      coarse[numbits-1]+=thist[jj];
+                    }
+#if 1
+                  for (jj=0; jj<ndict; jj++)
+                    fprintf(stderr,"%d %u %u O\n",jj,dict[jj],thist[jj]);
+#else
+                  for (jj=0; jj<17; jj++)
+                    fprintf(stderr,"%d %u\n",jj+1,coarse[jj]);
+#endif
+
+                }
+              exit(0);
+#endif
+
+              if (nlens<2)
+                reducealgo=0;
+
+#ifdef SHOWTEST
+              reducealgo=1;
+#endif
+            }
+          if (reducealgo==0)
+            {
+              if (verbose)
+                fprintf(stderr,"RLE\n");
+              /* Do RLE. For any repetetitive characters. */
+              Ptngc_comp_conv_to_rle(mtf,nvals16,rle,&nrle,1);
+
+#ifdef SHOWIT
+              printvals("rle",rle,nrle);
+#endif
+              if (verbose)
+                fprintf(stderr,"Resulting RLE values: %d\n",nrle);
+            }
+
+          /* reducealgo: RLE == 0, LZ77 == 1 */
+          output[outdata++]=reducealgo;
+
+          if (verbose)
+            fprintf(stderr,"Huffman\n");
+
+          huffalgo=-1;
+          Ptngc_comp_huff_compress_verbose(rle,nrle,bwlzhhuff,&bwlzhhufflen,&huffdatalen,nhufflen,&huffalgo,1);
+#ifdef SHOWTEST
+          {
+            int i;
+            fprintf(stderr,"Huffman\n");
+            for (i=0; i<bwlzhhufflen; i++)
+              fprintf(stderr,"%02x",(unsigned int)bwlzhhuff[i]);
+            fprintf(stderr,"\n");
+            exit(0);
+          }
+#endif
+          if (verbose)
+            {
+              int i;
+              fprintf(stderr,"Huffman data length is %d B.\n",huffdatalen);
+              for (i=0; i<N_HUFFMAN_ALGO; i++)
+                fprintf(stderr,"Huffman dictionary for algorithm %s is %d B.\n",Ptngc_comp_get_huff_algo_name(i),nhufflen[i]-huffdatalen);
+              fprintf(stderr,"Resulting algorithm: %s. Size=%d B\n",Ptngc_comp_get_huff_algo_name(huffalgo),bwlzhhufflen);
+            }
+
+          /* Store the number of huffman values in this block. */
+          output[outdata++]=((unsigned int)nrle)&0xFFU;
+          output[outdata++]=(((unsigned int)nrle)>>8)&0xFFU;
+          output[outdata++]=(((unsigned int)nrle)>>16)&0xFFU;
+          output[outdata++]=(((unsigned int)nrle)>>24)&0xFFU;
+
+          /* Store the size of the huffman block. */
+          output[outdata++]=((unsigned int)bwlzhhufflen)&0xFFU;
+          output[outdata++]=(((unsigned int)bwlzhhufflen)>>8)&0xFFU;
+          output[outdata++]=(((unsigned int)bwlzhhufflen)>>16)&0xFFU;
+          output[outdata++]=(((unsigned int)bwlzhhufflen)>>24)&0xFFU;
+
+          /* Store the huffman block. */
+          memcpy(output+outdata,bwlzhhuff,bwlzhhufflen);
+          outdata+=bwlzhhufflen;
+
+          if (reducealgo==1)
+            {
+              /* Store the number of values in this block. */
+              output[outdata++]=((unsigned int)noffsets)&0xFFU;
+              output[outdata++]=(((unsigned int)noffsets)>>8)&0xFFU;
+              output[outdata++]=(((unsigned int)noffsets)>>16)&0xFFU;
+              output[outdata++]=(((unsigned int)noffsets)>>24)&0xFFU;
+
+              if (noffsets>0)
+                {
+                  if (verbose)
+                    fprintf(stderr,"Huffman for offsets\n");
+
+                  huffalgo=-1;
+                  Ptngc_comp_huff_compress_verbose(offsets,noffsets,bwlzhhuff,&bwlzhhufflen,&huffdatalen,nhufflen,&huffalgo,1);
+                  if (verbose)
+                    {
+                      int i;
+                      fprintf(stderr,"Huffman data length is %d B.\n",huffdatalen);
+                      for (i=0; i<N_HUFFMAN_ALGO; i++)
+                        fprintf(stderr,"Huffman dictionary for algorithm %s is %d B.\n",Ptngc_comp_get_huff_algo_name(i),nhufflen[i]-huffdatalen);
+                      fprintf(stderr,"Resulting algorithm: %s. Size=%d B\n",Ptngc_comp_get_huff_algo_name(huffalgo),bwlzhhufflen);
+                    }
+
+                  /* If huffman was bad for these offsets, just store the offsets as pairs of bytes. */
+                  if (bwlzhhufflen<noffsets*2)
+                    {
+                      output[outdata++]=0;
+
+                      /* Store the size of the huffman block. */
+                      output[outdata++]=((unsigned int)bwlzhhufflen)&0xFFU;
+                      output[outdata++]=(((unsigned int)bwlzhhufflen)>>8)&0xFFU;
+                      output[outdata++]=(((unsigned int)bwlzhhufflen)>>16)&0xFFU;
+                      output[outdata++]=(((unsigned int)bwlzhhufflen)>>24)&0xFFU;
+
+                      /* Store the huffman block. */
+                      memcpy(output+outdata,bwlzhhuff,bwlzhhufflen);
+                      outdata+=bwlzhhufflen;
+                    }
+                  else
+                    {
+                      int i;
+                      output[outdata++]=1;
+                      for (i=0; i<noffsets; i++)
+                        {
+                          output[outdata++]=((unsigned int)offsets[i])&0xFFU;
+                          output[outdata++]=(((unsigned int)offsets[i])>>8)&0xFFU;
+                        }
+                      if (verbose)
+                        fprintf(stderr,"Store raw offsets: %d B\n",noffsets*2);
+                    }
+                }
+
+#if 0
+              {
+                int i,ndict;
+                FILE *f=fopen("len.dict","w");
+                Ptngc_comp_make_dict_hist(lens,nlens,dict,&ndict,hist);
+                for (i=0; i<ndict; i++)
+                  fprintf(f,"%d %d %d\n",i,dict[i],hist[i]);
+                fclose(f);
+                f=fopen("off.dict","w");
+                Ptngc_comp_make_dict_hist(offsets,noffsets,dict,&ndict,hist);
+                for (i=0; i<ndict; i++)
+                  fprintf(f,"%d %d %d\n",i,dict[i],hist[i]);
+                fclose(f);
+                f=fopen("len.time","w");
+                for (i=0; i<ndict; i++)
+                  fprintf(f,"%d\n",lens[i]);
+                fclose(f);
+                f=fopen("off.time","w");
+                for (i=0; i<ndict; i++)
+                  fprintf(f,"%d\n",offsets[i]);
+                fclose(f);
+              }
+#endif
+
+              if (verbose)
+                fprintf(stderr,"Huffman for lengths\n");
+
+              huffalgo=-1;
+              Ptngc_comp_huff_compress_verbose(lens,nlens,bwlzhhuff,&bwlzhhufflen,&huffdatalen,nhufflen,&huffalgo,1);
+              if (verbose)
+                {
+                  int i;
+                  fprintf(stderr,"Huffman data length is %d B.\n",huffdatalen);
+                  for (i=0; i<N_HUFFMAN_ALGO; i++)
+                    fprintf(stderr,"Huffman dictionary for algorithm %s is %d B.\n",Ptngc_comp_get_huff_algo_name(i),nhufflen[i]-huffdatalen);
+                  fprintf(stderr,"Resulting algorithm: %s. Size=%d B\n",Ptngc_comp_get_huff_algo_name(huffalgo),bwlzhhufflen);
+                }
+
+              /* Store the number of values in this block. */
+              output[outdata++]=((unsigned int)nlens)&0xFFU;
+              output[outdata++]=(((unsigned int)nlens)>>8)&0xFFU;
+              output[outdata++]=(((unsigned int)nlens)>>16)&0xFFU;
+              output[outdata++]=(((unsigned int)nlens)>>24)&0xFFU;
+
+              /* Store the size of the huffman block. */
+              output[outdata++]=((unsigned int)bwlzhhufflen)&0xFFU;
+              output[outdata++]=(((unsigned int)bwlzhhufflen)>>8)&0xFFU;
+              output[outdata++]=(((unsigned int)bwlzhhufflen)>>16)&0xFFU;
+              output[outdata++]=(((unsigned int)bwlzhhufflen)>>24)&0xFFU;
+
+              /* Store the huffman block. */
+              memcpy(output+outdata,bwlzhhuff,bwlzhhufflen);
+              outdata+=bwlzhhufflen;
+            }
+#ifdef PARTIAL_MTF3
+        }
+#endif
+    }
+
+  *output_len=outdata;
+  free(hist);
+  free(dict);
+  free(bwlzhhuff);
+#ifdef PARTIAL_MTF3
+  free(mtf3);
+#endif
+  free(tmpmem);
+}
+
+
+void DECLSPECDLLEXPORT bwlzh_compress(unsigned int *vals, int nvals,
+                  unsigned char *output, int *output_len)
+{
+  bwlzh_compress_gen(vals,nvals,output,output_len,1,0);
+}
+
+void DECLSPECDLLEXPORT bwlzh_compress_verbose(unsigned int *vals, int nvals,
+                          unsigned char *output, int *output_len)
+{
+  bwlzh_compress_gen(vals,nvals,output,output_len,1,1);
+}
+
+
+void DECLSPECDLLEXPORT bwlzh_compress_no_lz77(unsigned int *vals, int nvals,
+                  unsigned char *output, int *output_len)
+{
+  bwlzh_compress_gen(vals,nvals,output,output_len,0,0);
+}
+
+void DECLSPECDLLEXPORT bwlzh_compress_no_lz77_verbose(unsigned int *vals, int nvals,
+                          unsigned char *output, int *output_len)
+{
+  bwlzh_compress_gen(vals,nvals,output,output_len,0,1);
+}
+
+
+static void bwlzh_decompress_gen(unsigned char *input, int nvals,
+                               unsigned int *vals,
+                               int verbose)
+{
+  unsigned int *vals16;
+  int nvals16;
+  int bwt_index;
+  unsigned int *bwt=NULL;
+  unsigned int *mtf=NULL;
+#ifdef PARTIAL_MTF3
+  unsigned char *mtf3=NULL;
+  int imtfinner;
+#endif
+  unsigned int *rle=NULL;
+  unsigned int *offsets=NULL;
+  unsigned int *lens=NULL;
+  unsigned int *dict=warnmalloc(0x20004*sizeof *dict);
+  unsigned int *hist=warnmalloc(0x20004*sizeof *hist);
+  int nrle, noffsets, nlens;
+  unsigned char *bwlzhhuff=NULL;
+  int bwlzhhufflen;
+  int max_vals_per_block=MAX_VALS_PER_BLOCK;
+  int valsleft;
+  int thisvals;
+  int valstart;
+  int inpdata=0;
+  int nvalsfile;
+
+  unsigned int *tmpmem=warnmalloc(max_vals_per_block*18*sizeof *tmpmem);
+
+#if 0
+  verbose=1;
+#endif
+
+
+  bwlzhhuff=warnmalloc(Ptngc_comp_huff_buflen(3*nvals));
+  vals16=tmpmem;
+  bwt=tmpmem+max_vals_per_block*3;
+  mtf=tmpmem+max_vals_per_block*6;
+  rle=tmpmem+max_vals_per_block*9;
+  offsets=tmpmem+max_vals_per_block*12;
+  lens=tmpmem+max_vals_per_block*15;
+#ifdef PARTIAL_MTF3
+  mtf3=warnmalloc(max_vals_per_block*3*3*sizeof *mtf3); /* 3 due to expansion of 32 bit to 16 bit, 3 due to up to 3 bytes
+                                                           per 16 value. */
+#endif
+
+  if (verbose)
+    {
+      fprintf(stderr,"Number of input values: %d\n",nvals);
+    }
+
+  /* Read the number of real values in the whole block. */
+  nvalsfile=(int)(((unsigned int)input[inpdata]) |
+                  (((unsigned int)input[inpdata+1])<<8) |
+                  (((unsigned int)input[inpdata+2])<<16) |
+                  (((unsigned int)input[inpdata+3])<<24));
+  inpdata+=4;
+
+  if (nvalsfile!=nvals)
+    {
+      fprintf(stderr,"BWLZH: The number of values found in the file is different from the number of values expected.\n");
+      exit(EXIT_FAILURE);
+    }
+
+  valsleft=nvals;
+  valstart=0;
+  while (valsleft)
+    {
+      int valsnew;
+      int reducealgo;
+      /* Read the number of real values in this block. */
+      thisvals=(int)(((unsigned int)input[inpdata]) |
+                     (((unsigned int)input[inpdata+1])<<8) |
+                     (((unsigned int)input[inpdata+2])<<16) |
+                     (((unsigned int)input[inpdata+3])<<24));
+      inpdata+=4;
+
+      valsleft-=thisvals;
+
+      /* Read the number of nvals16 values in this block. */
+      nvals16=(int)(((unsigned int)input[inpdata]) |
+                    (((unsigned int)input[inpdata+1])<<8) |
+                    (((unsigned int)input[inpdata+2])<<16) |
+                    (((unsigned int)input[inpdata+3])<<24));
+      inpdata+=4;
+
+      /* Read the BWT index. */
+      bwt_index=(int)(((unsigned int)input[inpdata]) |
+                      (((unsigned int)input[inpdata+1])<<8) |
+                      (((unsigned int)input[inpdata+2])<<16) |
+                      (((unsigned int)input[inpdata+3])<<24));
+      inpdata+=4;
+
+      if (thisvals>max_vals_per_block)
+        {
+          /* More memory must be allocated for decompression. */
+          max_vals_per_block=thisvals;
+          if (verbose)
+            fprintf(stderr,"Allocating more memory: %d B\n",(int)(max_vals_per_block*15*sizeof *tmpmem));
+          tmpmem=warnrealloc(tmpmem,max_vals_per_block*18*sizeof *tmpmem);
+          vals16=tmpmem;
+          bwt=tmpmem+max_vals_per_block*3;
+          mtf=tmpmem+max_vals_per_block*6;
+          rle=tmpmem+max_vals_per_block*9;
+          offsets=tmpmem+max_vals_per_block*12;
+          lens=tmpmem+max_vals_per_block*15;
+#ifdef PARTIAL_MTF3
+          mtf3=warnrealloc(mtf3,max_vals_per_block*3*3*sizeof *mtf3); /* 3 due to expansion of 32 bit to 16 bit, 3 due to up to 3 bytes
+                                                                         per 16 value. */
+#endif
+        }
+
+#ifdef PARTIAL_MTF3
+      for (imtfinner=0; imtfinner<3; imtfinner++)
+        {
+          int i;
+          if (verbose)
+            fprintf(stderr,"Doing partial MTF: %d\n",imtfinner);
+#endif
+
+          reducealgo=(int)input[inpdata];
+          inpdata++;
+
+          /* Read the number of huffman values in this block. */
+          nrle=(int)(((unsigned int)input[inpdata]) |
+                     (((unsigned int)input[inpdata+1])<<8) |
+                     (((unsigned int)input[inpdata+2])<<16) |
+                     (((unsigned int)input[inpdata+3])<<24));
+          inpdata+=4;
+
+          /* Read the size of the huffman block. */
+          bwlzhhufflen=(int)(((unsigned int)input[inpdata]) |
+                             (((unsigned int)input[inpdata+1])<<8) |
+                             (((unsigned int)input[inpdata+2])<<16) |
+                             (((unsigned int)input[inpdata+3])<<24));
+          inpdata+=4;
+
+          if (verbose)
+            fprintf(stderr,"Decompressing huffman block of length %d.\n",bwlzhhufflen);
+          /* Decompress the huffman block. */
+          Ptngc_comp_huff_decompress(input+inpdata,bwlzhhufflen,rle);
+          inpdata+=bwlzhhufflen;
+
+          if (reducealgo==1) /* LZ77 */
+            {
+              int offstore;
+              /* Read the number of huffman values in this block. */
+              noffsets=(int)(((unsigned int)input[inpdata]) |
+                             (((unsigned int)input[inpdata+1])<<8) |
+                             (((unsigned int)input[inpdata+2])<<16) |
+                             (((unsigned int)input[inpdata+3])<<24));
+              inpdata+=4;
+
+              if (noffsets>0)
+                {
+                  /* How are the offsets stored? */
+                  offstore=(int)input[inpdata++];
+                  if (offstore==0)
+                    {
+                      /* Read the size of the huffman block. */
+                      bwlzhhufflen=(int)(((unsigned int)input[inpdata]) |
+                                         (((unsigned int)input[inpdata+1])<<8) |
+                                         (((unsigned int)input[inpdata+2])<<16) |
+                                         (((unsigned int)input[inpdata+3])<<24));
+                      inpdata+=4;
+
+                      if (verbose)
+                        fprintf(stderr,"Decompressing offset huffman block.\n");
+
+                      /* Decompress the huffman block. */
+                      Ptngc_comp_huff_decompress(input+inpdata,bwlzhhufflen,offsets);
+                      inpdata+=bwlzhhufflen;
+                    }
+                  else
+                    {
+                      int i;
+                      if (verbose)
+                        fprintf(stderr,"Reading offset block.\n");
+                      for (i=0; i<noffsets; i++)
+                        {
+                          offsets[i]=(int)(((unsigned int)input[inpdata]) |
+                                           (((unsigned int)input[inpdata+1])<<8));
+                          inpdata+=2;
+                        }
+                    }
+                }
+#if 0
+              {
+                int i;
+                for (i=0; i<nrle; i++)
+                  fprintf(stderr,"RLE %d: %d\n",i,rle[i]);
+                for (i=0; i<noffsets; i++)
+                  fprintf(stderr,"OFFSET %d: %d\n",i,offsets[i]);
+              }
+#endif
+
+
+              /* Read the number of huffman values in this block. */
+              nlens=(int)(((unsigned int)input[inpdata]) |
+                          (((unsigned int)input[inpdata+1])<<8) |
+                          (((unsigned int)input[inpdata+2])<<16) |
+                          (((unsigned int)input[inpdata+3])<<24));
+              inpdata+=4;
+
+              /* Read the size of the huffman block. */
+              bwlzhhufflen=(int)(((unsigned int)input[inpdata]) |
+                                 (((unsigned int)input[inpdata+1])<<8) |
+                                 (((unsigned int)input[inpdata+2])<<16) |
+                                 (((unsigned int)input[inpdata+3])<<24));
+              inpdata+=4;
+
+              if (verbose)
+                fprintf(stderr,"Decompressing length huffman block.\n");
+
+              /* Decompress the huffman block. */
+              Ptngc_comp_huff_decompress(input+inpdata,bwlzhhufflen,lens);
+              inpdata+=bwlzhhufflen;
+
+              if (verbose)
+                fprintf(stderr,"Decompressing LZ77.\n");
+
+              Ptngc_comp_from_lz77(rle,nrle,lens,nlens,offsets,noffsets,mtf,nvals16);
+            }
+          else if (reducealgo==0) /* RLE */
+            {
+#ifdef SHOWIT
+              printvals("rle",rle,nrle);
+#endif
+
+              if (verbose)
+                fprintf(stderr,"Decompressing rle block.\n");
+              Ptngc_comp_conv_from_rle(rle,mtf,nvals16);
+            }
+
+#ifdef PARTIAL_MTF3
+          for (i=0; i<nvals16; i++)
+            mtf3[imtfinner*nvals16+i]=(unsigned char)mtf[i];
+        }
+#else
+#ifdef SHOWIT
+      printvals("mtf",mtf,nvals16);
+#endif
+
+#endif
+
+
+      if (verbose)
+        fprintf(stderr,"Inverse MTF.\n");
+#ifdef PARTIAL_MTF3
+      Ptngc_comp_conv_from_mtf_partial3(mtf3,nvals16,bwt);
+#else
+#ifdef PARTIAL_MTF
+      Ptngc_comp_conv_from_mtf_partial(mtf,nvals16,bwt);
+#else
+      int ndict;
+      Ptngc_comp_canonical_dict(dict,&ndict);
+      Ptngc_comp_conv_from_mtf(mtf,nvals16,dict,ndict,bwt);
+#endif
+#endif
+
+#ifdef SHOWIT
+      printvals("bwt",bwt,nvals16);
+      fprintf(stderr,"BWT INDEX is %d\n",bwt_index);
+#endif
+
+      if (verbose)
+        fprintf(stderr,"Inverse BWT.\n");
+      Ptngc_comp_from_bwt(bwt,nvals16,bwt_index,vals16);
+
+#ifdef SHOWIT
+      printvals("vals16",vals16,nvals16);
+#endif
+
+      if (verbose)
+        fprintf(stderr,"Decompressing vals16 block.\n");
+      Ptngc_comp_conv_from_vals16(vals16,nvals16,vals+valstart,&valsnew);
+
+#ifdef SHOWIT
+      printvals("vals",vals+valstart,thisvals);
+#endif
+
+      if (valsnew!=thisvals)
+        {
+          fprintf(stderr,"BWLZH: Block contained different number of values than expected.\n");
+          exit(EXIT_FAILURE);
+        }
+      valstart+=thisvals;
+    }
+  free(hist);
+  free(dict);
+  free(bwlzhhuff);
+#ifdef PARTIAL_MTF3
+  free(mtf3);
+#endif
+  free(tmpmem);
+}
+
+
+void DECLSPECDLLEXPORT bwlzh_decompress(unsigned char *input, int nvals,
+                    unsigned int *vals)
+{
+  bwlzh_decompress_gen(input,nvals,vals,0);
+}
+
+void DECLSPECDLLEXPORT bwlzh_decompress_verbose(unsigned char *input, int nvals,
+                            unsigned int *vals)
+{
+  bwlzh_decompress_gen(input,nvals,vals,1);
+}
+
diff --git a/src/external/tng_io/src/compression/bwt.c b/src/external/tng_io/src/compression/bwt.c
new file mode 100644 (file)
index 0000000..66d3ecf
--- /dev/null
@@ -0,0 +1,335 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/bwt.h"
+
+#if 0
+#define SHOWIT
+#endif
+#if 0
+#define SHOWIT2
+#endif
+
+static int compare_index(int i1,int i2,int nvals,unsigned int *vals,unsigned int *nrepeat)
+{
+  int i,j;
+  for (i=0; i<nvals; i++)
+    {
+      /* If we have repeating patterns, we might be able to start the
+         comparison later in the string. */
+      /* Do we have a repeating pattern? If so are
+         the repeating patterns the same length? */
+      int repeat1=(int)(nrepeat[i1]>>8);
+      int k1=(int)(nrepeat[i1]&0xFFU);
+      int repeat2=(int)(nrepeat[i2]>>8);
+      int k2=(int)(nrepeat[i2]&0xFFU);
+
+      if ((repeat1>1) && (repeat2>1) && (k1==k2))
+        {
+          int maxskip=0;
+          /* Yes. Compare the repeating patterns. */
+          for (j=0; j<k1; j++)
+            {
+              unsigned int v1=vals[(i1+j)%nvals];
+              unsigned int v2=vals[(i2+j)%nvals];
+              if (v1<v2)
+                return -1;
+              else if (v1>v2)
+                return 1;
+            }
+          /* The repeating patterns are equal. Skip as far as we can
+             before continuing. */
+          maxskip=repeat1;
+          if (repeat2<repeat1)
+            maxskip=repeat2;
+          i1=(i1+maxskip)%nvals;
+          i2=(i2+maxskip)%nvals;
+          i+=maxskip-1;
+        }
+      else
+        {
+          if (vals[i1]<vals[i2])
+            return -1;
+          else if (vals[i1]>vals[i2])
+            return 1;
+          i1++;
+          if (i1>=nvals)
+            i1=0;
+          i2++;
+          if (i2>=nvals)
+            i2=0;
+        }
+    }
+  return 0;
+}
+
+void Ptngc_bwt_merge_sort_inner(int *indices, int nvals,unsigned int *vals,
+                          int start, int end,
+                          unsigned int *nrepeat,
+                          int *workarray)
+{
+  int middle;
+  if ((end-start)>1)
+    {
+      middle=start+(end-start)/2;
+#if 0
+      printf("For start %d end %d obtained new middle: %d\n",start,end,middle);
+#endif
+      Ptngc_bwt_merge_sort_inner(indices,nvals,vals,
+                           start,middle,
+                           nrepeat,
+                           workarray);
+      Ptngc_bwt_merge_sort_inner(indices,nvals,vals,
+                           middle,end,
+                           nrepeat,
+                           workarray);
+#if 0
+      printf("For start %d end %d Before merge: Comparing element %d with %d\n",start,end,middle-1,middle);
+#endif
+      if (compare_index(indices[middle-1],indices[middle],nvals,vals,nrepeat)>0)
+        {
+          /* Merge to work array. */
+          int i, n=end-start;
+          int ileft=start;
+          int iright=middle;
+          for (i=0; i<n; i++)
+            {
+              if (ileft==middle)
+                {
+                  workarray[i]=indices[iright];
+                  iright++;
+                }
+              else if (iright==end)
+                {
+                  workarray[i]=indices[ileft];
+                  ileft++;
+                }
+              else
+                {
+#if 0
+                  printf("For start %d end %d In merge: Comparing element %d with %d\n",start,end,ileft,iright);
+#endif
+                  if (compare_index(indices[ileft],indices[iright],nvals,vals,nrepeat)>0)
+                    {
+                      workarray[i]=indices[iright];
+                      iright++;
+                    }
+                  else
+                    {
+                      workarray[i]=indices[ileft];
+                      ileft++;
+                    }
+                }
+            }
+          /* Copy result back. */
+          memcpy(indices+start,workarray,(end-start)*sizeof(int));
+        }
+    }
+}
+
+/* Burrows-Wheeler transform. */
+void Ptngc_comp_to_bwt(unsigned int *vals, int nvals,
+                 unsigned int *output, int *index)
+{
+  int i;
+  int *indices=warnmalloc(2*nvals*sizeof *indices);
+  unsigned int *nrepeat=warnmalloc(nvals*sizeof *nrepeat);
+  int *warr=indices+nvals;
+
+  if (nvals>0xFFFFFF)
+    {
+      fprintf(stderr,"BWT cannot pack more than %d values.\n",0xFFFFFF);
+      exit(1);
+    }
+
+  /* Also note that repeat pattern k (kmax) cannot be larger than 255. */
+#if 0
+  printf("Size of arrays is %.2f M\n",4*nvals*sizeof *indices/1024./1024.);
+#endif
+  for (i=0; i<nvals; i++)
+    indices[i]=i;
+  /* Find the length of the initial repeating pattern for the strings. */
+  /* First mark that the index does not have a found repeating string. */
+  for (i=0; i<nvals; i++)
+    nrepeat[i]=0U;
+#ifdef SHOWIT
+  printf("nvals is %d\n",nvals);
+#endif
+  for (i=0; i<nvals; i++)
+    {
+      /* If we have not already found a repeating string we must find
+         it. */
+      if (!nrepeat[i])
+        {
+          int maxrepeat=nvals*2;
+          int j,k,m;
+          int good_j=-1, good_k=0;
+          int kmax=16;
+          /* Track repeating patterns.
+             k=1 corresponds to AAAAA...
+             k=2 corresponds to ABABAB...
+             k=3 corresponds to ABCABCABCABC...
+             k=4 corresponds to ABCDABCDABCD...
+             etc. */
+          for (k=kmax; k>=1; k--)
+            {
+            try_next_k:
+              if (k>=1)
+                {
+#ifdef SHOWIT
+                  printf("Trying k=%d at i=%d\n",k,i);
+#endif
+                  for (j=k; j<maxrepeat; j+=k)
+                    {
+                      int is_equal=1;
+#ifdef SHOWIT
+                      printf("Trying j=%d at i=%d for k %d\n",j,i,k);
+#endif
+                      for (m=0; m<k; m++)
+                        if (vals[(i+m)%nvals]!=vals[(i+j+m)%nvals])
+                          {
+                            is_equal=0;
+                            break;
+                          }
+                      if (is_equal)
+                        {
+                          int new_j=j+k;
+                          if (new_j>maxrepeat)
+                            new_j=j;
+                          if ((new_j>good_j) || ((new_j==good_j) && (k<good_k)))
+                            {
+                              good_j=new_j; /* We have found that
+                                             the strings repeat
+                                             for this length... */
+                              good_k=k;        /* ...and with this
+                                                  length of the
+                                                  repeating
+                                                  pattern. */
+#ifdef SHOWIT
+                              printf("Best j and k is now %d and %d\n",good_j,good_k);
+#endif
+                            }
+                        }
+                      else
+                        {
+                          /* We know that it is no point in trying
+                             with more than m */
+                          if (j==0)
+                            {
+                              k=m;
+#ifdef SHOWIT
+                              printf("Setting new k to m: %d\n",k);
+#endif
+                            }
+                          else
+                            k--;
+#ifdef SHOWIT
+                          printf("Trying next k\n");
+#endif
+                          goto try_next_k;
+                        }
+                    }
+                }
+            }
+          /* From good_j and good_k we know the repeat for a large
+             number of strings. The very last repeat length should not
+             be assigned, since it can be much longer if a new test is
+             done. */
+          for (m=0; (m+good_k<good_j) && (i+m<nvals); m+=good_k)
+            {
+              int repeat=good_j-m;
+              if (repeat>nvals)
+                repeat=nvals;
+              nrepeat[i+m]=((unsigned int) (good_k)) | (((unsigned int) (repeat))<<8);
+            }
+          /* If no repetition was found for this value signal that here. */
+          if (!nrepeat[i])
+            nrepeat[i+m]=257U; /* This is 1<<8 | 1 */
+        }
+    }
+#ifdef SHOWIT
+  for (i=0; i<nvals; i++)
+    if ((nrepeat[i]>>8)!=1)
+      printf("String repeats at %d: %d %d\n",i,nrepeat[i]>>8,nrepeat[i]&0xFFU);
+#endif
+
+  /* Sort cyclic shift matrix. */
+  Ptngc_bwt_merge_sort_inner(indices,nvals,vals,0,nvals,nrepeat,warr);
+
+#if 0
+  /* Test that it really is sorted. */
+  for (i=0; i<nvals-1; i++)
+    if (compare_index(indices[i],indices[i+1],nvals,vals,nrepeat)!=-1)
+      fprintf(stderr,"SORTING NOT COMPLETED AT %d. Index %d against %d\n",i,indices[i],indices[i+1]);
+
+#endif
+
+
+#ifdef SHOWIT2
+  for (i=0; i<nvals; i++)
+    {
+      int j;
+      for (j=0; j<nvals; j++)
+        printf("%c",vals[(indices[i]+j)%nvals]);
+      printf("\n");
+    }
+#endif
+  /* Which one is the original string? */
+  for (i=0; i<nvals; i++)
+    if (indices[i]==0)
+      break;
+  *index=i;
+  /* Form output. */
+  for (i=0; i<nvals; i++)
+    {
+      int lastchar=indices[i]-1;
+      if (lastchar<0)
+        lastchar=nvals-1;
+      output[i]=vals[lastchar];
+    }
+  free(nrepeat);
+  free(indices);
+}
+
+/* Burrows-Wheeler inverse transform. */
+void Ptngc_comp_from_bwt(unsigned int *input, int nvals, int index,
+                   unsigned int *vals)
+{
+  /* Straightforward from the Burrows-Wheeler paper (page 13). */
+  int i;
+  unsigned int *c=warnmalloc(0x10000*sizeof *c);
+  unsigned int *p=warnmalloc(nvals*sizeof *p);
+  unsigned int sum=0;
+  for (i=0; i<0x10000; i++)
+    c[i]=0;
+  for (i=0; i<nvals; i++)
+    {
+      p[i]=c[input[i]];
+      c[input[i]]++;
+    }
+  for (i=0; i<0x10000; i++)
+    {
+      sum+=c[i];
+      c[i]=sum-c[i];
+    }
+  for (i=nvals-1; i>=0; i--)
+    {
+      vals[i]=input[index];
+      index=p[index]+c[input[index]];
+    }
+  free(p);
+  free(c);
+}
+
diff --git a/src/external/tng_io/src/compression/coder.c b/src/external/tng_io/src/compression/coder.c
new file mode 100644 (file)
index 0000000..cf89140
--- /dev/null
@@ -0,0 +1,515 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../../include/compression/tng_compress.h"
+#include "../../include/compression/bwlzh.h"
+#include "../../include/compression/coder.h"
+#include "../../include/compression/warnmalloc.h"
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifdef USE_WINDOWS
+#define TNG_INLINE __inline
+#define TNG_SNPRINTF _snprintf
+#else
+#define TNG_INLINE inline
+#define TNG_SNPRINTF snprintf
+#endif
+
+struct coder DECLSPECDLLEXPORT *Ptngc_coder_init(void)
+{
+    struct coder *coder_inst=warnmalloc(sizeof *coder_inst);
+    coder_inst->pack_temporary_bits=0;
+    return coder_inst;
+}
+
+void DECLSPECDLLEXPORT Ptngc_coder_deinit(struct coder *coder_inst)
+{
+    free(coder_inst);
+}
+
+TNG_INLINE void DECLSPECDLLEXPORT Ptngc_out8bits(struct coder *coder_inst, unsigned char **output)
+{
+  int pack_temporary_bits=coder_inst->pack_temporary_bits;
+  unsigned int pack_temporary=coder_inst->pack_temporary;
+  while (pack_temporary_bits>=8)
+    {
+      unsigned int mask=~(0xFFU<<(pack_temporary_bits-8));
+      unsigned char out=(unsigned char)(pack_temporary>>(pack_temporary_bits-8));
+      **output=out;
+      (*output)++;
+      pack_temporary_bits-=8;
+      pack_temporary&=mask;
+    }
+  coder_inst->pack_temporary_bits=pack_temporary_bits;
+  coder_inst->pack_temporary=pack_temporary;
+}
+
+void DECLSPECDLLEXPORT Ptngc_write_pattern(struct coder *coder_inst, unsigned int pattern,
+                         int nbits, unsigned char **output)
+{
+    unsigned int mask1,mask2;
+    mask1=1;
+    mask2=1<<(nbits-1);
+    coder_inst->pack_temporary<<=nbits; /* Make room for new data. */
+    coder_inst->pack_temporary_bits+=nbits;
+    while (nbits)
+    {
+        if (pattern & mask1)
+            coder_inst->pack_temporary|=mask2;
+        nbits--;
+        mask1<<=1;
+        mask2>>=1;
+    }
+    Ptngc_out8bits(coder_inst,output);
+}
+
+/* Write up to 24 bits */
+TNG_INLINE void DECLSPECDLLEXPORT Ptngc_writebits(struct coder *coder_inst,
+                                unsigned int value, int nbits,
+                                unsigned char **output_ptr)
+{
+  /* Make room for the bits. */
+  coder_inst->pack_temporary<<=nbits;
+  coder_inst->pack_temporary_bits+=nbits;
+  coder_inst->pack_temporary|=value;
+  Ptngc_out8bits(coder_inst,output_ptr);
+}
+
+/* Write up to 32 bits */
+void DECLSPECDLLEXPORT Ptngc_write32bits(struct coder *coder_inst,unsigned int value,
+                       int nbits, unsigned char **output_ptr)
+{
+  unsigned int mask;
+  if (nbits>=8)
+    mask=0xFFU<<(nbits-8);
+  else
+    mask=0xFFU>>(8-nbits);
+  while (nbits>8)
+    {
+      /* Make room for the bits. */
+      coder_inst->pack_temporary<<=8;
+      coder_inst->pack_temporary_bits+=8;
+      coder_inst->pack_temporary|=(value&mask)>>(nbits-8);
+      Ptngc_out8bits(coder_inst,output_ptr);
+      nbits-=8;
+      mask>>=8;
+    }
+  if (nbits)
+    Ptngc_writebits(coder_inst,value&mask,nbits,output_ptr);
+}
+
+/* Write "arbitrary" number of bits */
+void DECLSPECDLLEXPORT Ptngc_writemanybits(struct coder *coder_inst, unsigned char *value,
+                         int nbits, unsigned char **output_ptr)
+{
+  int vptr=0;
+  while (nbits>=24)
+    {
+      unsigned int v=((((unsigned int)value[vptr])<<16)|
+                      (((unsigned int)value[vptr+1])<<8)|
+                      (((unsigned int)value[vptr+2])));
+      Ptngc_writebits(coder_inst,v,24,output_ptr);
+      vptr+=3;
+      nbits-=24;
+    }
+  while (nbits>=8)
+    {
+      Ptngc_writebits(coder_inst,(unsigned int)value[vptr],8,output_ptr);
+      vptr++;
+      nbits-=8;
+    }
+  if (nbits)
+    {
+      Ptngc_writebits(coder_inst,(unsigned int)value[vptr],nbits,output_ptr);
+    }
+}
+
+static int write_stop_bit_code(struct coder *coder_inst, unsigned int s,
+                               unsigned int coding_parameter,
+                               unsigned char **output)
+{
+  do {
+    unsigned int extract=~(0xffffffffU<<coding_parameter);
+    unsigned int this=(s&extract)<<1;
+    s>>=coding_parameter;
+    if (s)
+      {
+        this|=1U;
+        coder_inst->stat_overflow++;
+      }
+    coder_inst->pack_temporary<<=(coding_parameter+1);
+    coder_inst->pack_temporary_bits+=coding_parameter+1;
+    coder_inst->pack_temporary|=this;
+    Ptngc_out8bits(coder_inst,output);
+    if (s)
+      {
+        coding_parameter>>=1;
+        if (coding_parameter<1)
+          coding_parameter=1;
+      }
+  } while (s);
+  coder_inst->stat_numval++;
+  return 0;
+}
+
+static int pack_stopbits_item(struct coder *coder_inst,int item,
+                              unsigned char **output, int coding_parameter)
+{
+    /* Find this symbol in table. */
+    int s=0;
+    if (item>0)
+        s=1+(item-1)*2;
+    else if (item<0)
+        s=2+(-item-1)*2;
+    return write_stop_bit_code(coder_inst,s,coding_parameter,output);
+}
+
+static int pack_triplet(struct coder *coder_inst, unsigned int *s,
+                        unsigned char **output, int coding_parameter,
+                        unsigned int max_base, int maxbits)
+{
+  /* Determine base for this triplet. */
+  unsigned int min_base=1U<<coding_parameter;
+  unsigned int this_base=min_base;
+  int i;
+  unsigned int jbase=0;
+  unsigned int bits_per_value;
+  for (i=0; i<3; i++)
+    while (s[i]>=this_base)
+      {
+        this_base*=2;
+        jbase++;
+      }
+  bits_per_value=coding_parameter+jbase;
+  if (jbase>=3)
+    {
+      if (this_base>max_base)
+        return 1;
+      bits_per_value=maxbits;
+      jbase=3;
+    }
+  /* 2 bits selects the base */
+  coder_inst->pack_temporary<<=2;
+  coder_inst->pack_temporary_bits+=2;
+  coder_inst->pack_temporary|=jbase;
+  Ptngc_out8bits(coder_inst,output);
+  for (i=0; i<3; i++)
+    Ptngc_write32bits(coder_inst,s[i],bits_per_value,output);
+  return 0;
+}
+
+void DECLSPECDLLEXPORT Ptngc_pack_flush(struct coder *coder_inst,unsigned char **output)
+{
+  /* Zero-fill just enough. */
+  if (coder_inst->pack_temporary_bits>0)
+    Ptngc_write_pattern(coder_inst,0,8-coder_inst->pack_temporary_bits,output);
+}
+
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array(struct coder *coder_inst,
+                                int *input, int *length, int coding,
+                                int coding_parameter, int natoms, int speed)
+{
+  if ((coding==TNG_COMPRESS_ALGO_BWLZH1) || (coding==TNG_COMPRESS_ALGO_BWLZH2))
+    {
+      unsigned char *output=warnmalloc(4+bwlzh_get_buflen(*length));
+      int i,j,k,n=*length;
+      unsigned int *pval=warnmalloc(n*sizeof *pval);
+      int nframes=n/natoms/3;
+      int cnt=0;
+      int most_negative=2147483647;
+      for (i=0; i<n; i++)
+        if (input[i]<most_negative)
+          most_negative=input[i];
+      most_negative=-most_negative;
+      output[0]=((unsigned int)most_negative)&0xFFU;
+      output[1]=(((unsigned int)most_negative)>>8)&0xFFU;
+      output[2]=(((unsigned int)most_negative)>>16)&0xFFU;
+      output[3]=(((unsigned int)most_negative)>>24)&0xFFU;
+      for (i=0; i<natoms; i++)
+        for (j=0; j<3; j++)
+          for (k=0; k<nframes; k++)
+            {
+              int item=input[k*3*natoms+i*3+j];
+              pval[cnt++]=(unsigned int)(item+most_negative);
+
+            }
+      if (speed>=5)
+        bwlzh_compress(pval,n,output+4,length);
+      else
+        bwlzh_compress_no_lz77(pval,n,output+4,length);
+      (*length)+=4;
+      free(pval);
+      return output;
+    }
+  else if (coding==TNG_COMPRESS_ALGO_POS_XTC3)
+    return Ptngc_pack_array_xtc3(input,length,natoms,speed);
+  else if (coding==TNG_COMPRESS_ALGO_POS_XTC2)
+    return Ptngc_pack_array_xtc2(coder_inst,input,length);
+  else
+    {
+      unsigned char *output=NULL;
+      unsigned char *output_ptr=NULL;
+      int i;
+      int output_length=0;
+
+      coder_inst->stat_numval=0;
+      coder_inst->stat_overflow=0;
+      /* Allocate enough memory for output */
+      output=warnmalloc(8* *length*sizeof *output);
+      output_ptr=output;
+      if ((coding==TNG_COMPRESS_ALGO_TRIPLET) ||
+          (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA) ||
+          (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE))
+        {
+          /* Pack triplets. */
+          int ntriplets=*length/3;
+          /* Determine max base and maxbits */
+          unsigned int max_base=1U<<coding_parameter;
+          unsigned int maxbits=coding_parameter;
+          unsigned int intmax=0;
+          for (i=0; i<*length; i++)
+            {
+              int item=input[i];
+              unsigned int s=0;
+              if (item>0)
+                s=1+(item-1)*2;
+              else if (item<0)
+                s=2+(-item-1)*2;
+              if (s>intmax)
+                intmax=s;
+            }
+          /* Store intmax */
+          coder_inst->pack_temporary_bits=32;
+          coder_inst->pack_temporary=intmax;
+          Ptngc_out8bits(coder_inst,&output_ptr);
+          while (intmax>=max_base)
+            {
+              max_base*=2;
+              maxbits++;
+            }
+          for (i=0; i<ntriplets; i++)
+            {
+              int j;
+              unsigned int s[3];
+              for (j=0; j<3; j++)
+                {
+                  int item=input[i*3+j];
+                  /* Find this symbol in table. */
+                  s[j]=0;
+                  if (item>0)
+                    s[j]=1+(item-1)*2;
+                  else if (item<0)
+                    s[j]=2+(-item-1)*2;
+                }
+              if (pack_triplet(coder_inst, s, &output_ptr,
+                               coding_parameter, max_base,maxbits))
+                {
+                  free(output);
+                  return NULL;
+                }
+            }
+        }
+      else
+        for (i=0; i<*length; i++)
+          if (pack_stopbits_item(coder_inst,input[i],&output_ptr,coding_parameter))
+            {
+              free(output);
+              return NULL;
+            }
+      Ptngc_pack_flush(coder_inst,&output_ptr);
+      output_length=(int)(output_ptr-output);
+      *length=output_length;
+      return output;
+    }
+}
+
+static int unpack_array_stop_bits(struct coder *coder_inst,
+                                  unsigned char *packed,int *output,
+                                  int length, int coding_parameter)
+{
+  int i,j;
+  unsigned int extract_mask=0x80;
+  unsigned char *ptr=packed;
+  (void) coder_inst;
+  for (i=0; i<length; i++)
+    {
+      unsigned int pattern=0;
+      int numbits=coding_parameter;
+      unsigned int bit;
+      int s;
+      unsigned int insert_mask=1U<<(numbits-1);
+      int inserted_bits=numbits;
+      do {
+        for (j=0; j<numbits; j++)
+          {
+            bit=*ptr & extract_mask;
+            if (bit)
+              pattern|=insert_mask;
+            insert_mask>>=1;
+            extract_mask>>=1;
+            if (!extract_mask)
+              {
+                extract_mask=0x80;
+                ptr++;
+              }
+          }
+        /* Check stop bit */
+        bit=*ptr & extract_mask;
+        extract_mask>>=1;
+        if (!extract_mask)
+          {
+            extract_mask=0x80;
+            ptr++;
+          }
+        if (bit)
+          {
+            numbits>>=1;
+            if (numbits<1)
+              numbits=1;
+            inserted_bits+=numbits;
+            insert_mask=1U<<(inserted_bits-1);
+          }
+      } while (bit);
+      s=(pattern+1)/2;
+      if ((pattern%2)==0)
+        s=-s;
+      output[i]=s;
+    }
+  return 0;
+}
+
+static int unpack_array_triplet(struct coder *coder_inst,
+                                unsigned char *packed, int *output,
+                                int length, int coding_parameter)
+{
+  int i,j;
+  unsigned int extract_mask=0x80;
+  unsigned char *ptr=packed;
+  /* Determine max base and maxbits */
+  unsigned int max_base=1U<<coding_parameter;
+  unsigned int maxbits=coding_parameter;
+  unsigned int intmax;
+  /* Get intmax */
+  (void) coder_inst;
+  intmax=((unsigned int)ptr[0])<<24|
+    ((unsigned int)ptr[1])<<16|
+    ((unsigned int)ptr[2])<<8|
+    ((unsigned int)ptr[3]);
+  ptr+=4;
+  while (intmax>=max_base)
+    {
+      max_base*=2;
+      maxbits++;
+    }
+  length/=3;
+  for (i=0; i<length; i++)
+    {
+      /* Find base */
+      unsigned int jbase=0;
+      unsigned int numbits;
+      unsigned int bit;
+      for (j=0; j<2; j++)
+        {
+          bit=*ptr & extract_mask;
+          jbase<<=1;
+          if (bit)
+            jbase|=1U;
+          extract_mask>>=1;
+          if (!extract_mask)
+            {
+              extract_mask=0x80;
+              ptr++;
+            }
+        }
+      if (jbase==3)
+        numbits=maxbits;
+      else
+        numbits=coding_parameter+jbase;
+      for (j=0; j<3; j++)
+        {
+          int s;
+          unsigned int jbit;
+          unsigned int pattern=0;
+          for (jbit=0; jbit<numbits; jbit++)
+            {
+              bit=*ptr & extract_mask;
+              pattern<<=1;
+              if (bit)
+                pattern|=1U;
+              extract_mask>>=1;
+              if (!extract_mask)
+                {
+                  extract_mask=0x80;
+                  ptr++;
+                }
+            }
+          s=(pattern+1)/2;
+          if ((pattern%2)==0)
+            s=-s;
+          output[i*3+j]=s;
+        }
+    }
+  return 0;
+}
+
+static int unpack_array_bwlzh(struct coder *coder_inst,
+                              unsigned char *packed, int *output,
+                              int length, int natoms)
+{
+  int i,j,k,n=length;
+  unsigned int *pval=warnmalloc(n*sizeof *pval);
+  int nframes=n/natoms/3;
+  int cnt=0;
+  int most_negative=(int)(((unsigned int)packed[0]) |
+                          (((unsigned int)packed[1])<<8) |
+                          (((unsigned int)packed[2])<<16) |
+                          (((unsigned int)packed[3])<<24));
+  (void) coder_inst;
+  bwlzh_decompress(packed+4,length,pval);
+  for (i=0; i<natoms; i++)
+    for (j=0; j<3; j++)
+      for (k=0; k<nframes; k++)
+        {
+          unsigned int s=pval[cnt++];
+          output[k*3*natoms+i*3+j]=(int)s-most_negative;
+        }
+  free(pval);
+  return 0;
+}
+
+int DECLSPECDLLEXPORT Ptngc_unpack_array(struct coder *coder_inst,
+                       unsigned char *packed, int *output,
+                       int length, int coding, int coding_parameter,
+                       int natoms)
+{
+  if ((coding==TNG_COMPRESS_ALGO_STOPBIT) ||
+      (coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER))
+    return unpack_array_stop_bits(coder_inst, packed, output, length, coding_parameter);
+  else if ((coding==TNG_COMPRESS_ALGO_TRIPLET) ||
+           (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA) ||
+           (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE))
+    return unpack_array_triplet(coder_inst, packed, output, length, coding_parameter);
+  else if (coding==TNG_COMPRESS_ALGO_POS_XTC2)
+    return Ptngc_unpack_array_xtc2(coder_inst, packed, output, length);
+  else if ((coding==TNG_COMPRESS_ALGO_BWLZH1) || (coding==TNG_COMPRESS_ALGO_BWLZH2))
+    return unpack_array_bwlzh(coder_inst, packed, output, length,natoms);
+  else if (coding==TNG_COMPRESS_ALGO_POS_XTC3)
+    return Ptngc_unpack_array_xtc3(packed, output, length,natoms);
+  return 1;
+}
+
diff --git a/src/external/tng_io/src/compression/dict.c b/src/external/tng_io/src/compression/dict.c
new file mode 100644 (file)
index 0000000..24e6ae7
--- /dev/null
@@ -0,0 +1,43 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <string.h>
+#include "../../include/compression/dict.h"
+
+void Ptngc_comp_canonical_dict(unsigned int *dict, int *ndict)
+{
+  int i;
+  for (i=0; i<0x20004; i++)
+    dict[i]=i;
+  *ndict=0x20004;
+}
+
+void Ptngc_comp_make_dict_hist(unsigned int *vals, int nvals,
+                         unsigned int *dict, int *ndict,
+                         unsigned int *hist)
+{
+  int i;
+  int j=0;
+  for (i=0; i<0x20004; i++)
+    hist[i]=0;
+  for (i=0; i<0x20004; i++)
+    dict[i]=i;
+  for (i=0; i<nvals; i++)
+    hist[vals[i]]++;
+  for (i=0; i<0x20004; i++)
+    if (hist[i]!=0)
+      {
+        hist[j]=hist[i];
+        dict[j]=dict[i];
+        j++;
+      }
+  *ndict=j;
+}
diff --git a/src/external/tng_io/src/compression/fixpoint.c b/src/external/tng_io/src/compression/fixpoint.c
new file mode 100644 (file)
index 0000000..17fe6e8
--- /dev/null
@@ -0,0 +1,126 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "../../include/compression/fixpoint.h"
+
+#define MAX32BIT 4294967295UL
+#define MAX31BIT 2147483647UL
+#define SIGN32BIT 2147483648UL
+
+/* Conversion routines from / to double precision */
+
+/* Positive double to 32 bit fixed point value */
+fix_t Ptngc_ud_to_fix_t(double d,double max)
+{
+  fix_t val;
+  if (d<0.)
+    d=0.;
+  if (d>max)
+    d=max;
+  val=(fix_t)(MAX32BIT*(d/max));
+  if (val>MAX32BIT)
+    val=MAX32BIT;
+  return val;
+}
+
+/* double to signed 32 bit fixed point value */
+fix_t Ptngc_d_to_fix_t(double d,double max)
+{
+  fix_t val;
+  int sign=0;
+  if (d<0.)
+    {
+      sign=1;
+      d=-d;
+    }
+  if (d>max)
+    d=max;
+  val=(fix_t)(MAX31BIT*(d/max));
+  if (val>MAX31BIT)
+    val=MAX31BIT;
+  if (sign)
+    val|=SIGN32BIT;
+  return val;
+}
+
+
+/* 32 bit fixed point value to positive double */
+double Ptngc_fix_t_to_ud(fix_t f, double max)
+{
+  return (double)f*(max/MAX32BIT);
+}
+
+/* signed 32 bit fixed point value to double */
+double Ptngc_fix_t_to_d(fix_t f, double max)
+{
+  int sign=0;
+  double d;
+  if (f&SIGN32BIT)
+    {
+      sign=1;
+      f&=MAX31BIT;
+    }
+  d=(double)f*(max/MAX31BIT);
+  if (sign)
+    d=-d;
+  return d;
+}
+
+
+/* Convert a floating point variable to two 32 bit integers with range
+   -2.1e9 to 2.1e9 and precision to somewhere around 1e-9. */
+void Ptngc_d_to_i32x2(double d, fix_t *hi, fix_t *lo)
+{
+  int sign=0;
+  double frac;
+  double ent;
+  fix_t val,vallo;
+  if (d<0.)
+    {
+      sign=1;
+      d=-d;
+    }
+  /* First the integer part */
+  ent=floor(d);
+  /* Then the fractional part */
+  frac=d-ent;
+
+  val=(fix_t)ent;
+  if (sign)
+    val|=SIGN32BIT;
+
+  vallo=Ptngc_ud_to_fix_t(frac,1.);
+
+  *hi=val;
+  *lo=vallo;
+}
+
+/* Convert two 32 bit integers to a floating point variable
+   -2.1e9 to 2.1e9 and precision to somewhere around 1e-9. */
+double Ptngc_i32x2_to_d(fix_t hi, fix_t lo)
+{
+  double ent,frac=0.;
+  double val=0.;
+  int sign=0;
+  if (hi&SIGN32BIT)
+    {
+      sign=1;
+      hi&=MAX31BIT;
+    }
+  ent=(double)hi;
+  frac=Ptngc_fix_t_to_ud(lo,1.);
+  val=ent+frac;
+  if (sign)
+    val=-val;
+  return val;
+}
+
diff --git a/src/external/tng_io/src/compression/huffman.c b/src/external/tng_io/src/compression/huffman.c
new file mode 100644 (file)
index 0000000..2abcbcf
--- /dev/null
@@ -0,0 +1,581 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/merge_sort.h"
+#include "../../include/compression/huffman.h"
+
+#define MAX_HUFFMAN_LEN 31
+
+enum htree_type { htree_leaf, htree_node };
+
+struct htree_leaf
+{
+  enum htree_type nodeleaf;
+  unsigned int idict; /* Index into input dictionary */
+  unsigned int prob;
+  unsigned int bit; /* One or zero */
+};
+
+struct htree_node
+{
+  enum htree_type nodeleaf;
+  union htree_nodeleaf *n1;
+  union htree_nodeleaf *n2;
+  unsigned int bit; /* One or zero */
+  unsigned int prob;
+};
+
+union htree_nodeleaf
+{
+  enum htree_type nodeleaf;
+  struct htree_node node;
+  struct htree_leaf leaf;
+};
+
+struct codelength
+{
+  unsigned int code;
+  int length;
+  unsigned int dict;
+  unsigned int prob;
+};
+
+static int comp_htree(const void *leafptr1, const void *leafptr2, const void *private)
+{
+  const union htree_nodeleaf *leaf1=(union htree_nodeleaf *)leafptr1;
+  const union htree_nodeleaf *leaf2=(union htree_nodeleaf *)leafptr2;
+  int rval=0;
+  (void)private;
+
+  if (leaf1->leaf.prob<leaf2->leaf.prob)
+    rval=1;
+  else if (leaf1->leaf.prob>leaf2->leaf.prob)
+    rval=-1;
+  return rval;
+}
+
+static void assign_codes(union htree_nodeleaf *htree,
+                         struct codelength *codelength,
+                         unsigned int code,
+                         int length,
+                         int top)
+{
+#if 0
+  printf("Assign codes called with code %d length %d\n",code,length);
+#endif
+  if (htree->nodeleaf==htree_leaf)
+    {
+      codelength[htree->leaf.idict].length=length+1;
+      codelength[htree->leaf.idict].code=(code<<1)|htree->leaf.bit;
+#if 0
+      printf("I am a leaf: %d %d\n",
+             codelength[htree->leaf.idict].length,
+             codelength[htree->leaf.idict].code);
+#endif
+    }
+  else
+    {
+      if (!top)
+        {
+          code<<=1;
+          code|=htree->node.bit;
+          length++;
+        }
+#if 0
+      printf("I am a node length: %d\n",length);
+      printf("I am a node code: %d\n",code);
+#endif
+      assign_codes(htree->node.n1,codelength,code,length,0);
+      assign_codes(htree->node.n2,codelength,code,length,0);
+    }
+}
+
+static void free_nodes(union htree_nodeleaf *htree, int top)
+{
+  if (htree->nodeleaf==htree_leaf)
+    {
+      if (!top)
+        free(htree);
+    }
+  else
+    {
+      free_nodes(htree->node.n1,0);
+      free_nodes(htree->node.n2,0);
+      if (!top)
+        free(htree);
+    }
+}
+
+static void flush_8bits(unsigned int *combine, unsigned char **output, int *bitptr)
+{
+  while ((*bitptr)>=8)
+    {
+      unsigned int mask=~(0xFFU<<((*bitptr)-8));
+      unsigned char out=(unsigned char)((*combine)>>((*bitptr)-8));
+      **output=out;
+      (*output)++;
+      (*bitptr)-=8;
+      (*combine)&=mask;
+    }
+}
+
+static void writebits(unsigned int value,int length, unsigned char **output, int *bitptr)
+{
+  unsigned int mask;
+  unsigned int combine=(unsigned int)**output;
+  if (length>=8)
+    mask=0xFFU<<(length-8);
+  else
+    mask=0xFFU>>(8-length);
+  while (length>8)
+    {
+      /* Make room for the bits. */
+      combine<<=8;
+      (*bitptr)+=8;
+      combine|=(value&mask)>>(length-8);
+      flush_8bits(&combine,output,bitptr);
+      length-=8;
+      mask>>=8;
+    }
+  if (length)
+    {
+      /* Make room for the bits. */
+      combine<<=length;
+      (*bitptr)+=length;
+      combine|=value;
+      flush_8bits(&combine,output,bitptr);
+    }
+  **output=(unsigned char)combine;
+}
+
+static unsigned int readbits(int length, unsigned char **input, int *bitptr)
+{
+  unsigned int val=0U;
+  unsigned int extract_mask=0x80U>>*bitptr;
+  unsigned char thisval=**input;
+  while (length--)
+    {
+      val<<=1;
+      val|=((extract_mask & thisval)!=0);
+      *bitptr=(*bitptr)+1;
+      extract_mask>>=1;
+      if (!extract_mask)
+        {
+          extract_mask=0x80U;
+          *input=(*input)+1;
+          *bitptr=0;
+          thisval=**input;
+        }
+    }
+  return val;
+}
+
+static int comp_codes(const void *codeptr1, const void *codeptr2, const void *private)
+{
+  const struct codelength *code1=(struct codelength *)codeptr1;
+  const struct codelength *code2=(struct codelength *)codeptr2;
+  int rval=0; /* It shouldn't be possible to get equal here, though. */
+  (void)private;
+  if (code1->length>code2->length)
+    rval=1;
+  else if (code1->length<code2->length)
+    rval=-1;
+  else if (code1->dict>code2->dict)
+    rval=1;
+  else
+    rval=-1;
+  return rval;
+}
+
+static int comp_codes_value(const void *codeptr1, const void *codeptr2, const void *private)
+{
+  const struct codelength *code1=(struct codelength *)codeptr1;
+  const struct codelength *code2=(struct codelength *)codeptr2;
+
+  int rval=0; /* It shouldn't be possible to get equal here, though. */
+  (void)private;
+  if (code1->dict>code2->dict)
+    rval=1;
+  else
+    rval=-1;
+  return rval;
+}
+
+/* The huffman_dict array should be 131077 (0x20005) long. The
+huffman_dict_unpacked array should be 131077 long (note five longer than
+0x20000) */
+void Ptngc_comp_conv_to_huffman(unsigned int *vals, int nvals,
+                          unsigned int *dict, int ndict,
+                          unsigned int *prob,
+                          unsigned char *huffman,
+                          int *huffman_len,
+                          unsigned char *huffman_dict,
+                          int *huffman_dictlen,
+                          unsigned int *huffman_dict_unpacked,
+                          int *huffman_dict_unpackedlen)
+{
+  int i;
+  int nleft;
+  union htree_nodeleaf *htree;
+  struct codelength *codelength;
+  int bitptr;
+  unsigned char *huffman_ptr;
+  int code;
+  int longcodes=1;
+  while (longcodes)
+    {
+      /* Create array of leafs (will be array of nodes/trees during
+         buildup of tree. */
+      htree=warnmalloc(ndict*sizeof *htree);
+      codelength=warnmalloc(ndict*sizeof *codelength);
+      bitptr=0;
+      huffman_ptr=huffman;
+      for (i=0; i<ndict; i++)
+        {
+          htree[i].nodeleaf=htree_leaf;
+          htree[i].leaf.idict=i;
+          htree[i].leaf.prob=prob[i];
+        }
+      /* Sort the leafs wrt probability. */
+      Ptngc_merge_sort(htree,ndict,sizeof *htree,comp_htree,NULL);
+
+#if 0
+      for (i=0; i<ndict; i++)
+        {
+          printf("%d %d\n",dict[htree[i].leaf.idict],htree[i].leaf.prob);
+        }
+#endif
+
+      /* Build tree. */
+      if (ndict==1)
+        {
+          codelength[0].code=1;
+          codelength[0].length=1;
+        }
+      else
+        {
+          /* Nodes and leafs left. */
+          nleft=ndict;
+
+          /* Take the two least probable symbols (which are at the end of the
+             array and combine them until there is nothing left. */
+          while (nleft>1)
+            {
+              union htree_nodeleaf *n1=warnmalloc(sizeof *n1);
+              union htree_nodeleaf *n2=warnmalloc(sizeof *n2);
+              int new_place;
+              int p1,p2, new_prob;
+              *n1=htree[nleft-1];
+              *n2=htree[nleft-2];
+              if (n1->nodeleaf==htree_leaf)
+                {
+                  p1=n1->leaf.prob;
+                  n1->leaf.bit=0;
+                }
+              else
+                {
+                  p1=n1->node.prob;
+                  n1->node.bit=0;
+                }
+              if (n2->nodeleaf==htree_leaf)
+                {
+                  p2=n2->leaf.prob;
+                  n2->leaf.bit=1;
+                }
+              else
+                {
+                  p2=n2->node.prob;
+                  n2->node.bit=1;
+                }
+              nleft--;
+              /* Create a new node */
+              htree[nleft-1].nodeleaf=htree_node;
+              htree[nleft-1].node.n1=n1;
+              htree[nleft-1].node.n2=n2;
+              new_prob=p1+p2;
+              htree[nleft-1].node.prob=new_prob;
+              /* Use insertion sort to place this in the correct place in the
+                 array. */
+              /* Where should it be inserted? */
+              new_place=nleft;
+              while (new_place>0)
+                {
+                  int pc;
+                  if (htree[new_place-1].nodeleaf==htree_node)
+                    pc=htree[new_place-1].node.prob;
+                  else
+                    pc=htree[new_place-1].leaf.prob;
+                  if (new_prob<pc)
+                    break;
+                  else
+                    new_place--;
+                }
+              if (new_place!=nleft)
+                {
+                  /* Shift array (overlapping regions!) */
+                  union htree_nodeleaf nodecopy=htree[nleft-1];
+                  memmove(htree+new_place+1,
+                          htree+new_place,
+                          (nleft-1-new_place)*sizeof *htree);
+                  htree[new_place]=nodecopy;
+                }
+            }
+        }
+      /* Create codes from tree */
+      assign_codes(htree,codelength,0,0,1);
+      /* Canonicalize */
+      /* First put values into to the codelength array for sorting. */
+      for (i=0; i<ndict; i++)
+        {
+          codelength[i].dict=dict[i];
+          codelength[i].prob=prob[i];
+        }
+      /* Sort codes wrt length/value */
+      Ptngc_merge_sort(codelength,ndict,sizeof *codelength,comp_codes,NULL);
+      /* Canonicalize codes. */
+      code=0;
+      for (i=0; i<ndict; i++)
+        {
+          codelength[i].code=code;
+          if (i<ndict-1)
+            code=(code+1)<<(codelength[i+1].length-codelength[i].length);
+        }
+      /* Free all nodes / leaves. */
+      free_nodes(htree,1);
+      /* Free array that held nodes/leaves. */
+      free(htree);
+
+      longcodes=0;
+      /* Check if there is a too long huffman code. */
+      for (i=0; i<ndict; i++)
+        if (codelength[i].length>MAX_HUFFMAN_LEN)
+          longcodes=1;
+
+      /* If the codes are too long alter the probabilities. */
+      if (longcodes)
+        {
+          for (i=0; i<ndict; i++)
+            {
+              prob[i]>>=1;
+              if (prob[i]==0)
+                prob[i]=1;
+            }
+
+          /* Free codelength. We will compute a new one. */
+          free(codelength);
+        }
+    }
+
+#if 0
+  {
+    for (i=0; i<ndict; i++)
+      {
+        printf("%d %d\t\t %d %d ",codelength[i].dict,codelength[i].prob,codelength[i].length,codelength[i].code);
+        {
+          unsigned int c=codelength[i].code;
+          int j;
+          unsigned int mask=1<<(codelength[i].length-1);
+          for (j=codelength[i].length-1; j>=0; j--)
+            {
+              int bit=c&mask;
+              if (bit)
+                printf("1");
+              else
+                printf("0");
+              mask>>=1;
+            }
+          printf("\n");
+        }
+      }
+  }
+#endif
+
+  /* Simply do compression by writing out the bits. */
+  for (i=0; i<nvals; i++)
+    {
+      int r;
+      for (r=0; r<ndict; r++)
+        if (codelength[r].dict==vals[i])
+          break;
+      writebits(codelength[r].code,codelength[r].length,&huffman_ptr,&bitptr);
+    }
+  if (bitptr)
+    writebits(0,8-bitptr,&huffman_ptr,&bitptr);
+  *huffman_len=(int)(huffman_ptr-huffman);
+  /* Output dictionary. */
+  /* First the largest symbol value is written in 16 bits. No bits are
+     encoded for symbols larger than this.  Then one bit signifies if
+     there is a used symbol: 1 If unused entry: 0 If used symbol the 5
+     following bits encode the length of the symbol. Worst case is
+     thus 6*65538 bits used for the dictionary. That won't happen
+     unless there's really that many values in use. If that is so,
+     well, either we compress well, or we have many values anyway. */
+  /* First sort the dictionary wrt symbol */
+  Ptngc_merge_sort(codelength,ndict,sizeof *codelength,comp_codes_value,NULL);
+  bitptr=0;
+  huffman_ptr=huffman_dict;
+  *huffman_ptr++=(unsigned char)(codelength[ndict-1].dict&0xFFU);
+  *huffman_ptr++=(unsigned char)((codelength[ndict-1].dict>>8)&0xFFU);
+  *huffman_ptr++=(unsigned char)((codelength[ndict-1].dict>>16)&0xFFU);
+  huffman_dict_unpacked[0]=(unsigned char)(codelength[ndict-1].dict&0xFFU);
+  huffman_dict_unpacked[1]=(unsigned char)((codelength[ndict-1].dict>>8)&0xFFU);
+  huffman_dict_unpacked[2]=(unsigned char)((codelength[ndict-1].dict>>16)&0xFFU);
+  for (i=0; i<=(int)codelength[ndict-1].dict; i++)
+    {
+      /* Do I have this value? */
+      int ihave=0;
+      int j;
+      for (j=0; j<ndict; j++)
+        if (codelength[j].dict==(unsigned int)i)
+          {
+
+            ihave=1;
+            writebits(1,1,&huffman_ptr,&bitptr);
+            writebits(codelength[j].length,5,&huffman_ptr,&bitptr);
+            huffman_dict_unpacked[3+i]=codelength[j].length;
+            break;
+          }
+      if (!ihave)
+        {
+          writebits(0,1,&huffman_ptr,&bitptr);
+          huffman_dict_unpacked[3+i]=0;
+        }
+    }
+  if (bitptr)
+    writebits(0,8-bitptr,&huffman_ptr,&bitptr);
+  *huffman_dictlen=(int)(huffman_ptr-huffman_dict);
+  *huffman_dict_unpackedlen=3+codelength[ndict-1].dict+1;
+
+  /* Free info about codes and length. */
+  free(codelength);
+}
+
+void Ptngc_comp_conv_from_huffman(unsigned char *huffman,
+                            unsigned int *vals, int nvals,
+                            int ndict,
+                            unsigned char *huffman_dict,
+                            int huffman_dictlen,
+                            unsigned int *huffman_dict_unpacked,
+                            int huffman_dict_unpackedlen)
+{
+  struct codelength *codelength=warnmalloc(ndict*sizeof *codelength);
+  int i,j;
+  int maxdict;
+  int code;
+  unsigned char *huffman_ptr;
+  int bitptr;
+  (void)huffman_dictlen;
+  (void)huffman_dict_unpackedlen;
+  if (huffman_dict_unpacked)
+    {
+      maxdict=huffman_dict_unpacked[0]|(huffman_dict_unpacked[1]<<8)|(huffman_dict_unpacked[2]<<16);
+      j=0;
+      for(i=0; i<=maxdict; i++)
+        {
+          if (huffman_dict_unpacked[3+i]!=0)
+            {
+              codelength[j].length=huffman_dict_unpacked[3+i];
+              codelength[j].dict=i;
+#if 0
+              printf("%d %d\n",
+                     codelength[j].length,
+                     codelength[j].dict);
+#endif
+              j++;
+            }
+        }
+    }
+  else
+    {
+      huffman_ptr=huffman_dict;
+      maxdict=((unsigned int)huffman_ptr[0])|(((unsigned int)huffman_ptr[1])<<8)|(((unsigned int)huffman_ptr[2])<<16);
+      huffman_ptr+=3;
+      bitptr=0;
+      j=0;
+      for(i=0; i<=maxdict; i++)
+        {
+          int bit=readbits(1,&huffman_ptr,&bitptr);
+          if (bit)
+            {
+              codelength[j].length=readbits(5,&huffman_ptr,&bitptr);
+              codelength[j].dict=i;
+#if 0
+              printf("%d %d\n",
+                     codelength[j].length,
+                     codelength[j].dict);
+#endif
+              j++;
+            }
+        }
+    }
+  /* Sort codes wrt length/value. */
+  Ptngc_merge_sort(codelength,ndict,sizeof *codelength,comp_codes,NULL);
+  /* Canonicalize codes. */
+  code=0;
+  for (i=0; i<ndict; i++)
+    {
+      codelength[i].code=code;
+      if (i<ndict-1)
+        code=(code+1)<<(codelength[i+1].length-codelength[i].length);
+    }
+#if 0
+  {
+    for (i=0; i<ndict; i++)
+      {
+        printf("%d -\t\t %d %d ",codelength[i].dict,codelength[i].length,codelength[i].code);
+        {
+          unsigned int c=codelength[i].code;
+          int j;
+          unsigned int mask=1<<(codelength[i].length-1);
+          for (j=codelength[i].length-1; j>=0; j--)
+            {
+              int bit=c&mask;
+              if (bit)
+                printf("1");
+              else
+                printf("0");
+              mask>>=1;
+            }
+          printf("\n");
+        }
+      }
+  }
+#endif
+  /* Decompress data. */
+  huffman_ptr=huffman;
+  bitptr=0;
+  for (i=0; i<nvals; i++)
+    {
+      unsigned int symbol;
+      int len=codelength[0].length;
+      symbol=readbits(len,&huffman_ptr,&bitptr);
+      j=0;
+      while (symbol!=codelength[j].code)
+        {
+          int newlen;
+          j++;
+          newlen=codelength[j].length;
+          if (newlen!=len)
+            {
+              symbol<<=(newlen-len);
+              symbol|=readbits(newlen-len,&huffman_ptr,&bitptr);
+              len=newlen;
+            }
+        }
+      vals[i]=codelength[j].dict;
+    }
+  /* Free info about codes and length. */
+  free(codelength);
+}
diff --git a/src/external/tng_io/src/compression/huffmem.c b/src/external/tng_io/src/compression/huffmem.c
new file mode 100644 (file)
index 0000000..183eff1
--- /dev/null
@@ -0,0 +1,356 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/tng_compress.h"
+#include "../../include/compression/bwlzh.h"
+#include "../../include/compression/huffman.h"
+#include "../../include/compression/dict.h"
+#include "../../include/compression/rle.h"
+#include "../../include/compression/vals16.h"
+
+int Ptngc_comp_huff_buflen(int nvals)
+{
+  return 132000+nvals*8;
+}
+
+/* the value pointed to by chosen_algo should be sent as -1 for autodetect. */
+void Ptngc_comp_huff_compress_verbose(unsigned int *vals, int nvals,
+                                unsigned char *huffman, int *huffman_len,
+                                int *huffdatalen,
+                                int *huffman_lengths,int *chosen_algo,
+                                int isvals16)
+{
+  unsigned int *dict=warnmalloc(0x20005*sizeof *dict);
+  unsigned int *hist=warnmalloc(0x20005*sizeof *hist);
+  unsigned int *vals16=NULL;
+  unsigned char *huffdict=warnmalloc(0x20005*sizeof *huffdict);
+  unsigned int *huffdictunpack=warnmalloc(0x20005*sizeof *huffdictunpack);
+  unsigned char *huffman1=warnmalloc(2*0x20005*sizeof *huffman1);
+  unsigned char *huffdict1=warnmalloc(0x20005*sizeof *huffdict1);
+  unsigned int *huffdictunpack1=warnmalloc(0x20005*sizeof *huffdictunpack1);
+  unsigned int *huffdictrle=warnmalloc((3*0x20005+3)*sizeof *huffdictrle);
+  unsigned char *huffman2=warnmalloc(6*0x20005*sizeof *huffman2);
+  unsigned char *huffdict2=warnmalloc(0x20005*sizeof *huffdict2);
+  unsigned int *huffdictunpack2=warnmalloc(0x20005*sizeof *huffdictunpack2);
+  int i;
+  int ndict,ndict1,ndict2;
+  int nhuff,nhuffdict,nhuffdictunpack;
+  int nhuff1,nhuffdict1,nhuffdictunpack1;
+  int nhuffrle,nhuff2,nhuffdict2,nhuffdictunpack2;
+  int nvals16;
+
+  /* Do I need to convert to vals16? */
+  if (!isvals16)
+  {
+    vals16=warnmalloc(nvals*3*sizeof *vals16);
+    Ptngc_comp_conv_to_vals16(vals,nvals,vals16,&nvals16);
+    nvals=nvals16;
+    vals=vals16;
+  }
+  else
+    nvals16=nvals;
+
+  /* Determine probabilities. */
+  Ptngc_comp_make_dict_hist(vals,nvals,dict,&ndict,hist);
+
+  /* First compress the data using huffman coding (place it ready for output at 14 (code for algorithm+length etc.). */
+  Ptngc_comp_conv_to_huffman(vals,nvals,dict,ndict,hist,
+                       huffman+14,&nhuff,
+                       huffdict,&nhuffdict,
+                       huffdictunpack,&nhuffdictunpack);
+  *huffdatalen=nhuff;
+
+  /* Algorithm 0 stores the huffman dictionary directly (+ a code for
+     the algorithm) + lengths of the huffman buffer (4) and the huffman dictionary (3). */
+  huffman_lengths[0]=nhuff+nhuffdict+1*2+3*4+3+3;
+  /* Next we try to compress the huffman dictionary using huffman
+     coding ... (algorithm 1) */
+
+  /* Determine probabilities. */
+  Ptngc_comp_make_dict_hist(huffdictunpack,nhuffdictunpack,dict,&ndict1,hist);
+  /* Pack huffman dictionary */
+  Ptngc_comp_conv_to_huffman(huffdictunpack,nhuffdictunpack,
+                       dict,ndict1,hist,
+                       huffman1,&nhuff1,
+                       huffdict1,&nhuffdict1,
+                       huffdictunpack1,&nhuffdictunpack1);
+  huffman_lengths[1]=nhuff+nhuff1+nhuffdict1+1*2+3*4+3+3+3+3+3;
+
+  /* ... and rle + huffman coding ... (algorithm 2) Pack any repetetitive patterns. */
+  Ptngc_comp_conv_to_rle(huffdictunpack,nhuffdictunpack,
+                   huffdictrle,&nhuffrle,1);
+
+  /* Determine probabilities. */
+  Ptngc_comp_make_dict_hist(huffdictrle,nhuffrle,dict,&ndict2,hist);
+  /* Pack huffman dictionary */
+  Ptngc_comp_conv_to_huffman(huffdictrle,nhuffrle,
+                       dict,ndict2,hist,
+                       huffman2,&nhuff2,
+                       huffdict2,&nhuffdict2,
+                       huffdictunpack2,&nhuffdictunpack2);
+  huffman_lengths[2]=nhuff+nhuff2+nhuffdict2+1*2+3*4+3+3+3+3+3+3;
+
+  /* Choose the best algorithm and output the data. */
+  if ((*chosen_algo==0) || ((*chosen_algo==-1) &&
+                            (((huffman_lengths[0]<huffman_lengths[1]) &&
+                              (huffman_lengths[0]<huffman_lengths[2])))))
+    {
+      *chosen_algo=0;
+      *huffman_len=huffman_lengths[0];
+      huffman[0]=isvals16;
+      huffman[1]=0;
+      huffman[2]=((unsigned int)nvals16)&0xFFU;
+      huffman[3]=(((unsigned int)nvals16)>>8)&0xFFU;
+      huffman[4]=(((unsigned int)nvals16)>>16)&0xFFU;
+      huffman[5]=(((unsigned int)nvals16)>>24)&0xFFU;
+      huffman[6]=((unsigned int)nvals)&0xFFU;
+      huffman[7]=(((unsigned int)nvals)>>8)&0xFFU;
+      huffman[8]=(((unsigned int)nvals)>>16)&0xFFU;
+      huffman[9]=(((unsigned int)nvals)>>24)&0xFFU;
+      huffman[10]=((unsigned int)nhuff)&0xFFU;
+      huffman[11]=(((unsigned int)nhuff)>>8)&0xFFU;
+      huffman[12]=(((unsigned int)nhuff)>>16)&0xFFU;
+      huffman[13]=(((unsigned int)nhuff)>>24)&0xFFU;
+      huffman[14+nhuff]=((unsigned int)nhuffdict)&0xFFU;
+      huffman[15+nhuff]=(((unsigned int)nhuffdict)>>8)&0xFFU;
+      huffman[16+nhuff]=(((unsigned int)nhuffdict)>>16)&0xFFU;
+      huffman[17+nhuff]=((unsigned int)ndict)&0xFFU;
+      huffman[18+nhuff]=(((unsigned int)ndict)>>8)&0xFFU;
+      huffman[19+nhuff]=(((unsigned int)ndict)>>16)&0xFFU;
+      for (i=0; i<nhuffdict; i++)
+        huffman[20+nhuff+i]=huffdict[i];
+    }
+  else if ((*chosen_algo==1) || ((*chosen_algo==-1) &&
+                                 ((huffman_lengths[1]<huffman_lengths[2]))))
+    {
+      *chosen_algo=1;
+      *huffman_len=huffman_lengths[1];
+      huffman[0]=isvals16;
+      huffman[1]=1;
+      huffman[2]=((unsigned int)nvals16)&0xFFU;
+      huffman[3]=(((unsigned int)nvals16)>>8)&0xFFU;
+      huffman[4]=(((unsigned int)nvals16)>>16)&0xFFU;
+      huffman[5]=(((unsigned int)nvals16)>>24)&0xFFU;
+      huffman[6]=((unsigned int)nvals)&0xFFU;
+      huffman[7]=(((unsigned int)nvals)>>8)&0xFFU;
+      huffman[8]=(((unsigned int)nvals)>>16)&0xFFU;
+      huffman[9]=(((unsigned int)nvals)>>24)&0xFFU;
+      huffman[10]=((unsigned int)nhuff)&0xFFU;
+      huffman[11]=(((unsigned int)nhuff)>>8)&0xFFU;
+      huffman[12]=(((unsigned int)nhuff)>>16)&0xFFU;
+      huffman[13]=(((unsigned int)nhuff)>>24)&0xFFU;
+      huffman[14+nhuff]=((unsigned int)nhuffdictunpack)&0xFFU;
+      huffman[15+nhuff]=(((unsigned int)nhuffdictunpack)>>8)&0xFFU;
+      huffman[16+nhuff]=(((unsigned int)nhuffdictunpack)>>16)&0xFFU;
+      huffman[17+nhuff]=((unsigned int)ndict)&0xFFU;
+      huffman[18+nhuff]=(((unsigned int)ndict)>>8)&0xFFU;
+      huffman[19+nhuff]=(((unsigned int)ndict)>>16)&0xFFU;
+      huffman[20+nhuff]=((unsigned int)nhuff1)&0xFFU;
+      huffman[21+nhuff]=(((unsigned int)nhuff1)>>8)&0xFFU;
+      huffman[22+nhuff]=(((unsigned int)nhuff1)>>16)&0xFFU;
+      huffman[23+nhuff]=((unsigned int)nhuffdict1)&0xFFU;
+      huffman[24+nhuff]=(((unsigned int)nhuffdict1)>>8)&0xFFU;
+      huffman[25+nhuff]=(((unsigned int)nhuffdict1)>>16)&0xFFU;
+      huffman[26+nhuff]=((unsigned int)ndict1)&0xFFU;
+      huffman[27+nhuff]=(((unsigned int)ndict1)>>8)&0xFFU;
+      huffman[28+nhuff]=(((unsigned int)ndict1)>>16)&0xFFU;
+      for (i=0; i<nhuff1; i++)
+        huffman[29+nhuff+i]=huffman1[i];
+      for (i=0; i<nhuffdict1; i++)
+        huffman[29+nhuff+nhuff1+i]=huffdict1[i];
+    }
+  else
+    {
+      *chosen_algo=2;
+      *huffman_len=huffman_lengths[2];
+      huffman[0]=isvals16;
+      huffman[1]=2;
+      huffman[2]=((unsigned int)nvals16)&0xFFU;
+      huffman[3]=(((unsigned int)nvals16)>>8)&0xFFU;
+      huffman[4]=(((unsigned int)nvals16)>>16)&0xFFU;
+      huffman[5]=(((unsigned int)nvals16)>>24)&0xFFU;
+      huffman[6]=((unsigned int)nvals)&0xFFU;
+      huffman[7]=(((unsigned int)nvals)>>8)&0xFFU;
+      huffman[8]=(((unsigned int)nvals)>>16)&0xFFU;
+      huffman[9]=(((unsigned int)nvals)>>24)&0xFFU;
+      huffman[10]=((unsigned int)nhuff)&0xFFU;
+      huffman[11]=(((unsigned int)nhuff)>>8)&0xFFU;
+      huffman[12]=(((unsigned int)nhuff)>>16)&0xFFU;
+      huffman[13]=(((unsigned int)nhuff)>>24)&0xFFU;
+      huffman[14+nhuff]=((unsigned int)nhuffdictunpack)&0xFFU;
+      huffman[15+nhuff]=(((unsigned int)nhuffdictunpack)>>8)&0xFFU;
+      huffman[16+nhuff]=(((unsigned int)nhuffdictunpack)>>16)&0xFFU;
+      huffman[17+nhuff]=((unsigned int)ndict)&0xFFU;
+      huffman[18+nhuff]=(((unsigned int)ndict)>>8)&0xFFU;
+      huffman[19+nhuff]=(((unsigned int)ndict)>>16)&0xFFU;
+      huffman[20+nhuff]=((unsigned int)nhuffrle)&0xFFU;
+      huffman[21+nhuff]=(((unsigned int)nhuffrle)>>8)&0xFFU;
+      huffman[22+nhuff]=(((unsigned int)nhuffrle)>>16)&0xFFU;
+      huffman[23+nhuff]=((unsigned int)nhuff2)&0xFFU;
+      huffman[24+nhuff]=(((unsigned int)nhuff2)>>8)&0xFFU;
+      huffman[25+nhuff]=(((unsigned int)nhuff2)>>16)&0xFFU;
+      huffman[26+nhuff]=((unsigned int)nhuffdict2)&0xFFU;
+      huffman[27+nhuff]=(((unsigned int)nhuffdict2)>>8)&0xFFU;
+      huffman[28+nhuff]=(((unsigned int)nhuffdict2)>>16)&0xFFU;
+      huffman[29+nhuff]=((unsigned int)ndict2)&0xFFU;
+      huffman[30+nhuff]=(((unsigned int)ndict2)>>8)&0xFFU;
+      huffman[31+nhuff]=(((unsigned int)ndict2)>>16)&0xFFU;
+      for (i=0; i<nhuff2; i++)
+        huffman[32+nhuff+i]=huffman2[i];
+      for (i=0; i<nhuffdict2; i++)
+        huffman[32+nhuff+nhuff2+i]=huffdict2[i];
+    }
+  if (!isvals16)
+    free(vals16);
+
+  free(huffdictunpack2);
+  free(huffdict2);
+  free(huffman2);
+  free(huffdictrle);
+  free(huffdictunpack1);
+  free(huffdict1);
+  free(huffman1);
+  free(huffdictunpack);
+  free(huffdict);
+  free(hist);
+  free(dict);
+}
+
+void Ptngc_comp_huff_compress(unsigned int *vals, int nvals,
+                        unsigned char *huffman, int *huffman_len)
+{
+  int huffman_lengths[N_HUFFMAN_ALGO];
+  int algo=-1;
+  int huffdatalen;
+  Ptngc_comp_huff_compress_verbose(vals,nvals,huffman,huffman_len,&huffdatalen,
+                             huffman_lengths,&algo,0);
+}
+
+void Ptngc_comp_huff_decompress(unsigned char *huffman, int huffman_len,
+                          unsigned int *vals)
+{
+  int isvals16=(int)huffman[0];
+  unsigned int *vals16=NULL;
+  int algo=(int)huffman[1];
+  int nvals16=(int)((unsigned int)huffman[2]|
+                  (((unsigned int)huffman[3])<<8)|
+                  (((unsigned int)huffman[4])<<16)|
+                  (((unsigned int)huffman[5])<<24));
+  int nvals=(int)((unsigned int)huffman[6]|
+                  (((unsigned int)huffman[7])<<8)|
+                  (((unsigned int)huffman[8])<<16)|
+                  (((unsigned int)huffman[9])<<24));
+  int nhuff=(int)((unsigned int)huffman[10]|
+                  (((unsigned int)huffman[11])<<8)|
+                  (((unsigned int)huffman[12])<<16)|
+                  (((unsigned int)huffman[13])<<24));
+  int ndict=(int)((unsigned int)huffman[17+nhuff]|
+                  (((unsigned int)huffman[18+nhuff])<<8)|
+                  (((unsigned int)huffman[19+nhuff])<<16));
+  (void)huffman_len;
+  if (!isvals16)
+    vals16=warnmalloc(nvals16*sizeof *vals16);
+  else
+    {
+      vals16=vals;
+      nvals16=nvals;
+    }
+  if (algo==0)
+    {
+      int nhuffdict=(int)((unsigned int)huffman[14+nhuff]|
+                          (((unsigned int)huffman[15+nhuff])<<8)|
+                          (((unsigned int)huffman[16+nhuff])<<16));
+      Ptngc_comp_conv_from_huffman(huffman+14,vals16,nvals16,ndict,
+                             huffman+20+nhuff,nhuffdict,NULL,0);
+    }
+  else if (algo==1)
+    {
+      unsigned int *huffdictunpack=warnmalloc(0x20005*sizeof *huffdictunpack);
+      /* First the dictionary needs to be uncompressed. */
+      int nhuffdictunpack=(int)((unsigned int)huffman[14+nhuff]|
+                                (((unsigned int)huffman[15+nhuff])<<8)|
+                                (((unsigned int)huffman[16+nhuff])<<16));
+      int nhuff1=(int)((unsigned int)huffman[20+nhuff]|
+                       (((unsigned int)huffman[21+nhuff])<<8)|
+                       (((unsigned int)huffman[22+nhuff])<<16));
+      int nhuffdict1=(int)((unsigned int)huffman[23+nhuff]|
+                           (((unsigned int)huffman[24+nhuff])<<8)|
+                           (((unsigned int)huffman[25+nhuff])<<16));
+      int ndict1=(int)((unsigned int)huffman[26+nhuff]|
+                       (((unsigned int)huffman[27+nhuff])<<8)|
+                       (((unsigned int)huffman[28+nhuff])<<16));
+      Ptngc_comp_conv_from_huffman(huffman+29+nhuff,huffdictunpack,
+                             nhuffdictunpack,ndict1,
+                             huffman+29+nhuff+nhuff1,nhuffdict1,NULL,0);
+      /* Then decompress the "real" data. */
+      Ptngc_comp_conv_from_huffman(huffman+14,vals16,nvals16,ndict,
+                             NULL,0,huffdictunpack,nhuffdictunpack);
+      free(huffdictunpack);
+    }
+  else if (algo==2)
+    {
+      unsigned int *huffdictunpack=warnmalloc(0x20005*sizeof *huffdictunpack);
+      unsigned int *huffdictrle=warnmalloc((3*0x20005+3)*sizeof *huffdictrle);
+      /* First the dictionary needs to be uncompressed. */
+      int nhuffdictunpack=(int)((unsigned int)huffman[14+nhuff]|
+                                (((unsigned int)huffman[15+nhuff])<<8)|
+                                (((unsigned int)huffman[16+nhuff])<<16));
+      int nhuffrle=(int)((unsigned int)huffman[20+nhuff]|
+                         (((unsigned int)huffman[21+nhuff])<<8)|
+                         (((unsigned int)huffman[22+nhuff])<<16));
+      int nhuff2=(int)((unsigned int)huffman[23+nhuff]|
+                       (((unsigned int)huffman[24+nhuff])<<8)|
+                       (((unsigned int)huffman[25+nhuff])<<16));
+      int nhuffdict2=(int)((unsigned int)huffman[26+nhuff]|
+                           (((unsigned int)huffman[27+nhuff])<<8)|
+                           (((unsigned int)huffman[28+nhuff])<<16));
+      int ndict2=(int)((unsigned int)huffman[29+nhuff]|
+                       (((unsigned int)huffman[30+nhuff])<<8)|
+                       (((unsigned int)huffman[31+nhuff])<<16));
+      Ptngc_comp_conv_from_huffman(huffman+32+nhuff,huffdictrle,
+                             nhuffrle,ndict2,
+                             huffman+32+nhuff+nhuff2,nhuffdict2,NULL,0);
+      /* Then uncompress the rle data */
+      Ptngc_comp_conv_from_rle(huffdictrle,huffdictunpack,nhuffdictunpack);
+      /* Then decompress the "real" data. */
+      Ptngc_comp_conv_from_huffman(huffman+14,vals16,nvals16,ndict,
+                             NULL,0,huffdictunpack,nhuffdictunpack);
+      free(huffdictrle);
+      free(huffdictunpack);
+    }
+
+  /* Do I need to convert from vals16? */
+  if (!isvals16)
+  {
+    int nvalsx;
+    Ptngc_comp_conv_from_vals16(vals16,nvals16,vals,&nvalsx);
+    free(vals16);
+  }
+}
+
+static char *huff_algo_names[N_HUFFMAN_ALGO]=
+  {
+    "Huffman (dict=raw)",
+    "Huffman (dict=Huffman)",
+    "Huffman (dict=RLE+Huffman)"
+  };
+
+char *Ptngc_comp_get_huff_algo_name(int algo)
+{
+  if (algo<0)
+    return NULL;
+  else if (algo>=N_HUFFMAN_ALGO)
+    return NULL;
+  return huff_algo_names[algo];
+}
diff --git a/src/external/tng_io/src/compression/lz77.c b/src/external/tng_io/src/compression/lz77.c
new file mode 100644 (file)
index 0000000..17b147c
--- /dev/null
@@ -0,0 +1,348 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/bwt.h"
+#include "../../include/compression/lz77.h"
+
+/* This is a simple Lempel-Ziv-77 compressor. It has not been set up
+   to remove every possible repeating pattern, but it might be better
+   than simple RLE.
+
+   Lempel-Ziv 77 with separate outputs for length, data, and offsets.
+ */
+
+#if 0
+/* Sort the strings (similar to BWT) to find similar patterns in the
+   input data.  The output is an array with two values for each
+   input value. The sorted index value is the first in each doublet.
+   The second value is the "inverse" of the first value and can be
+   used to find the locations of similar strings. */
+static void sort_strings(unsigned int *vals, int nvals,
+                         unsigned int *output)
+{
+  int i;
+  int *indices=warnmalloc(2*nvals*sizeof *indices);
+  unsigned int *nrepeat=warnmalloc(nvals*sizeof *nrepeat);
+  int *warr=indices+nvals;
+
+  if (nvals>0xFFFFFF)
+    {
+      fprintf(stderr,"BWT cannot pack more than %d values.\n",0xFFFFFF);
+      exit(1);
+    }
+
+  /* Also note that repeat pattern k (kmax) cannot be larger than 255. */
+  for (i=0; i<nvals; i++)
+    indices[i]=i;
+  /* Find the length of the initial repeating pattern for the strings. */
+  /* First mark that the index does not have a found repeating string. */
+  for (i=0; i<nvals; i++)
+    nrepeat[i]=0U;
+  for (i=0; i<nvals; i++)
+    {
+      /* If we have not already found a repeating string we must find
+         it. */
+      if (!nrepeat[i])
+        {
+          int maxrepeat=nvals*2;
+          int j,k,m;
+          int good_j=-1, good_k=0;
+          int kmax=16;
+          /* Track repeating patterns.
+             k=1 corresponds to AAAAA...
+             k=2 corresponds to ABABAB...
+             k=3 corresponds to ABCABCABCABC...
+             k=4 corresponds to ABCDABCDABCD...
+             etc. */
+          for (k=kmax; k>=1; k--)
+            {
+            try_next_k:
+              if (k>=1)
+                {
+                  for (j=k; j<maxrepeat; j+=k)
+                    {
+                      int is_equal=1;
+                      for (m=0; m<k; m++)
+                        if (vals[(i+m)%nvals]!=vals[(i+j+m)%nvals])
+                          {
+                            is_equal=0;
+                            break;
+                          }
+                      if (is_equal)
+                        {
+                          int new_j=j+k;
+                          if (new_j>maxrepeat)
+                            new_j=j;
+                          if ((new_j>good_j) || ((new_j==good_j) && (k<good_k)))
+                            {
+                              good_j=new_j; /* We have found that
+                                             the strings repeat
+                                             for this length... */
+                              good_k=k;        /* ...and with this
+                                                  length of the
+                                                  repeating
+                                                  pattern. */
+                            }
+                        }
+                      else
+                        {
+                          /* We know that it is no point in trying
+                             with more than m */
+                          if (j==0)
+                            {
+                              k=m;
+                            }
+                          else
+                            k--;
+                          goto try_next_k;
+                        }
+                    }
+                }
+            }
+          /* From good_j and good_k we know the repeat for a large
+             number of strings. The very last repeat length should not
+             be assigned, since it can be much longer if a new test is
+             done. */
+          for (m=0; (m+good_k<good_j) && (i+m<nvals); m+=good_k)
+            {
+              int repeat=good_j-m;
+              if (repeat>nvals)
+                repeat=nvals;
+              nrepeat[i+m]=((unsigned int) (good_k)) | (((unsigned int) (repeat))<<8);
+            }
+          /* If no repetition was found for this value signal that here. */
+          if (!nrepeat[i])
+            nrepeat[i+m]=257U; /* This is 1<<8 | 1 */
+        }
+    }
+
+  /* Sort cyclic shift matrix. */
+  Ptngc_bwt_merge_sort_inner(indices,nvals,vals,0,nvals,nrepeat,warr);
+
+  /* Form output. */
+  for (i=0; i<nvals; i++)
+    {
+      output[i*2]=indices[i];
+      output[indices[i]*2+1]=i;
+    }
+  free(nrepeat);
+  free(indices);
+}
+#endif
+
+#define NUM_PREVIOUS 4
+#define MAX_LEN 0xFFFF
+#define MAX_OFFSET 0xFFFF
+#define MAX_STRING_SEARCH 8
+
+static void add_circular(int *previous,int v, int i)
+{
+  if (previous[(NUM_PREVIOUS+3)*v+2]!=i-1)
+    {
+      previous[(NUM_PREVIOUS+3)*v]++;
+      if (previous[(NUM_PREVIOUS+3)*v]>NUM_PREVIOUS)
+        previous[(NUM_PREVIOUS+3)*v]=NUM_PREVIOUS;
+      previous[(NUM_PREVIOUS+3)*v+3+previous[(NUM_PREVIOUS+3)*v+1]]=i;
+      previous[(NUM_PREVIOUS+3)*v+1]++;
+      if (previous[(NUM_PREVIOUS+3)*v+1]>=NUM_PREVIOUS)
+        previous[(NUM_PREVIOUS+3)*v+1]=0;
+    }
+  previous[(NUM_PREVIOUS+3)*v+2]=i;
+}
+
+void Ptngc_comp_to_lz77(unsigned int *vals, int nvals,
+                  unsigned int *data, int *ndata,
+                  unsigned int *len, int *nlens,
+                  unsigned int *offsets, int *noffsets)
+{
+  int noff=0;
+  int ndat=0;
+  int nlen=0;
+  int i,j;
+  int *previous=warnmalloc(0x20000*(NUM_PREVIOUS+3)*sizeof *previous);
+#if 0
+  unsigned int *info=warnmalloc(2*nvals*sizeof *info);
+  sort_strings(vals,nvals,info);
+#endif
+  for (i=0; i<0x20000; i++)
+    {
+      previous[(NUM_PREVIOUS+3)*i]=0; /* Number of items in a circular buffer */
+      previous[(NUM_PREVIOUS+3)*i+1]=0; /* Pointer to beginning of circular buffer. */
+      previous[(NUM_PREVIOUS+3)*i+2]=-2; /* Last offset that had this value. -2 is really never... */
+    }
+  for (i=0; i<nvals; i++)
+    {
+      int k;
+#if 0
+      int kmin,kmax;
+#endif
+      int firstoffset=i-MAX_OFFSET;
+      if (firstoffset<0)
+        firstoffset=0;
+      if (i!=0)
+        {
+          int largest_len=0;
+          int largest_offset=0;
+          int icirc, ncirc;
+          /* Is this identical to a previous offset?  Prefer close
+             values for offset. Search through circular buffer for the
+             possible values for the start of this string. */
+          ncirc=previous[(NUM_PREVIOUS+3)*vals[i]];
+          for (icirc=0; icirc<ncirc; icirc++)
+            {
+              int iptr=previous[(NUM_PREVIOUS+3)*vals[i]+1]-icirc-1;
+              if (iptr<0)
+                iptr+=NUM_PREVIOUS;
+              j=previous[(NUM_PREVIOUS+3)*vals[i]+3+iptr];
+              if (j<firstoffset)
+                break;
+#if 0
+              fprintf(stderr,"Starting search for %d at %d. Found %d\n",vals[i],j,vals[j]);
+#endif
+              while ((j<i) && (vals[j]==vals[i]))
+                {
+                  if (j>=firstoffset)
+                    {
+                      for (k=0; i+k<nvals; k++)
+                        if (vals[j+k]!=vals[i+k])
+                          break;
+                      if ((k>largest_len) && ((k>=(i-j)+16) || ((k>4) && (i-j==1))))
+                        {
+                          largest_len=k;
+                          largest_offset=j;
+                        }
+                    }
+                  j++;
+                }
+            }
+#if 0
+          /* Search in sorted string buffer too. */
+          kmin=info[i*2+1]-MAX_STRING_SEARCH;
+          kmax=info[i*2+1]+MAX_STRING_SEARCH;
+          if (kmin<0)
+            kmin=0;
+          if (kmax>=nvals)
+            kmax=nvals;
+          for (k=kmin; k<kmax; k++)
+            {
+              int m;
+              int s=info[k*2];
+              if ((s<i) && (s+MAX_OFFSET>=i))
+                {
+                  for (m=0; i+m<nvals; m++)
+                    if (vals[i+m]!=vals[(s+m)%nvals])
+                      break;
+                  if ((m>largest_len) && (m>4) && (m+2>=(i-s)))
+                    {
+                      largest_len=m;
+                      largest_offset=s;
+#if 0
+                      fprintf(stderr,"Offset: %d %d\n",m,i-s);
+#endif
+                    }
+                }
+            }
+#endif
+          /* Check how to write this info. */
+          if (largest_len>MAX_LEN)
+            largest_len=MAX_LEN;
+          if (largest_len)
+            {
+              if (i-largest_offset==1)
+                {
+                  data[ndat++]=0;
+                }
+              else
+                {
+                  data[ndat++]=1;
+                  offsets[noff++]=i-largest_offset;
+                }
+              len[nlen++]=largest_len;
+#if 0
+              fprintf(stderr,"l:o: %d:%d data=%d i=%d\n",largest_len,i-largest_offset,ndat,i);
+              fflush(stderr);
+#endif
+
+#if 0
+              fprintf(stderr,"Found largest len %d at %d.\n",largest_len,i-largest_offset);
+#endif
+              /* Add these values to the circular buffer. */
+              for (k=0; k<largest_len; k++)
+                add_circular(previous,vals[i+k],i+k);
+              i+=largest_len-1;
+            }
+          else
+            {
+              data[ndat++]=vals[i]+2;
+              /* Add this value to circular buffer. */
+              add_circular(previous,vals[i],i);
+            }
+        }
+      else
+        {
+          data[ndat++]=vals[i]+2;
+          /* Add this value to circular buffer. */
+          add_circular(previous,vals[i],i);
+        }
+    }
+  *noffsets=noff;
+  *ndata=ndat;
+  *nlens=nlen;
+#if 0
+  free(info);
+#endif
+  free(previous);
+}
+
+void Ptngc_comp_from_lz77(unsigned int *data, int ndata,
+                    unsigned int *len, int nlens,
+                    unsigned int *offsets, int noffsets,
+                    unsigned int *vals, int nvals)
+{
+  int i=0;
+  int joff=0;
+  int jdat=0;
+  int jlen=0;
+  (void)ndata;
+  (void)nlens;
+  (void)noffsets;
+  while (i<nvals)
+    {
+      unsigned int v=data[jdat++];
+      if (v<2)
+        {
+          int offset=1;
+          int k;
+          int length=(int)len[jlen++];
+#if 0
+          fprintf(stderr,"len=%d off=%d i=%d\n",length,offset,i);
+#endif
+          if (v==1)
+            offset=offsets[joff++];
+          for (k=0; k<length; k++)
+            {
+              vals[i]=vals[i-offset];
+              if (i>=nvals)
+                {
+                  fprintf(stderr,"too many vals.\n");
+                  exit(EXIT_FAILURE);
+                }
+              i++;
+            }
+        }
+      else
+        vals[i++]=v-2;
+    }
+}
diff --git a/src/external/tng_io/src/compression/merge_sort.c b/src/external/tng_io/src/compression/merge_sort.c
new file mode 100644 (file)
index 0000000..7cb9837
--- /dev/null
@@ -0,0 +1,128 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/merge_sort.h"
+
+static void ms_inner(void *base, size_t size,
+             size_t start, size_t end,
+             int (*compar)(const void *v1,const void *v2,const void *private),
+             const void *private,
+             char *workarray)
+{
+  size_t middle;
+  if ((end-start)>1)
+    {
+      char *cbase=(char *)base;
+      middle=start+(end-start)/2;
+#if 0
+      printf("For start %d end %d obtained new middle: %d\n",start,end,middle);
+#endif
+      ms_inner(base,size,
+           start,middle,
+           compar,private,workarray);
+      ms_inner(base,size,
+           middle,end,
+           compar,private,workarray);
+#if 0
+      printf("For start %d end %d Before merge: Comparing element %d with %d\n",start,end,middle-1,middle);
+#endif
+      if (compar(cbase+(middle-1)*size,cbase+middle*size,private)>0)
+        {
+          /* Merge to work array. */
+          size_t i, n=end-start;
+          size_t ileft=start;
+          size_t iright=middle;
+          for (i=0; i<n; i++)
+            {
+              if (ileft==middle)
+                {
+                  memcpy(workarray+i*size,cbase+iright*size,size);
+                  iright++;
+                }
+              else if (iright==end)
+                {
+                  memcpy(workarray+i*size,cbase+ileft*size,size);
+                  ileft++;
+                }
+              else
+                {
+        #if 0
+                  printf("For start %d end %d In merge: Comparing element %d with %d\n",start,end,ileft,iright);
+        #endif
+                  if (compar(cbase+ileft*size,cbase+iright*size,private)>0)
+                    {
+                      memcpy(workarray+i*size,cbase+iright*size,size);
+                      iright++;
+                    }
+                  else
+                    {
+                      memcpy(workarray+i*size,cbase+ileft*size,size);
+                      ileft++;
+                    }
+                }
+            }
+          /* Copy result back. */
+          memcpy(cbase+start*size,workarray,(end-start)*size);
+        }
+    }
+}
+
+
+void Ptngc_merge_sort(void *base, size_t nmemb, size_t size,
+        int (*compar)(const void *v1,const void *v2,const void *private),
+        void *private)
+{
+  char *warr=warnmalloc(nmemb*size);
+  ms_inner(base,size,0,nmemb,compar,private,warr);
+  free(warr);
+}
+
+
+#ifdef TEST
+
+static int compint(const void *v1, const void *v2,const void *private)
+{
+  const int *i1=(const int *)v1;
+  const int *i2=(const int *)v2;
+  if (*i1<*i2)
+    return -1;
+  else if (*i1>*i2)
+    return 1;
+  else
+    return 0;
+}
+
+static int qcompint(const void *v1, const void *v2)
+{
+  return compint(v1,v2,NULL);
+}
+
+#define N 1000000
+int main()
+{
+  int *arr=warnmalloc(N*sizeof *arr);
+  int i;
+  for (i=0; i<N; i++)
+    scanf("%d",arr+i);
+#if 1
+  merge_sort(arr,N,sizeof *arr,compint,NULL);
+#else
+  qsort(arr,N,sizeof *arr,qcompint);
+#endif
+  for (i=0; i<N; i++)
+    printf("%d %d\n",i,arr[i]);
+  return 0;
+}
+#endif
diff --git a/src/external/tng_io/src/compression/mtf.c b/src/external/tng_io/src/compression/mtf.c
new file mode 100644 (file)
index 0000000..d6eaf30
--- /dev/null
@@ -0,0 +1,254 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdlib.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/mtf.h"
+
+/* "Partial" MTF. Byte based. */
+/* Move to front coding.
+   Acceptable inputs are max 8 bits (0-0xFF) */
+static void comp_conv_to_mtf_byte(unsigned char *vals, int nvals,
+                                  unsigned char *valsmtf)
+{
+  int i;
+  /* Indices into a linked list */
+  int list[256];
+  int dict[256];
+  /* Head of the linked list */
+  int head;
+  for (i=0; i<256; i++)
+    dict[i]=i;
+  for (i=0; i<255; i++)
+    list[i]=i+1;
+  list[255]=-1; /* end. */
+  head=0;
+  for (i=0; i<nvals; i++)
+    {
+      int v=(int)vals[i];
+      /* Find how early in the dict the value is */
+      int ptr=head;
+      int oldptr=-1;
+      int r=0;
+      while (dict[ptr]!=v)
+        {
+          oldptr=ptr;
+          ptr=list[ptr];
+          r++;
+        }
+      valsmtf[i]=(unsigned char)r;
+      /* Move it to front in list */
+      /* Is it the head? Then it is already at the front. */
+      if (oldptr!=-1)
+        {
+          /* Remove it from inside the list */
+          list[oldptr]=list[ptr];
+          /* Move it to the front. */
+          list[ptr]=head;
+          head=ptr;
+        }
+    }
+}
+
+void Ptngc_comp_conv_to_mtf_partial(unsigned int *vals, int nvals,
+                              unsigned int *valsmtf)
+{
+  unsigned char *tmp=warnmalloc(nvals*2);
+  int i, j;
+  for (i=0; i<nvals; i++)
+    valsmtf[i]=0U;
+  for (j=0; j<3; j++)
+    {
+      for (i=0; i<nvals; i++)
+        tmp[i]=(unsigned char)((vals[i]>>(8*j))&0xFF);
+      comp_conv_to_mtf_byte(tmp,nvals,tmp+nvals);
+      for (i=0; i<nvals; i++)
+        valsmtf[i]|=(((unsigned int)(tmp[nvals+i]))<<(8*j));
+    }
+  free(tmp);
+}
+
+void Ptngc_comp_conv_to_mtf_partial3(unsigned int *vals, int nvals,
+                               unsigned char *valsmtf)
+{
+  unsigned char *tmp=warnmalloc(nvals);
+  int i, j;
+  for (j=0; j<3; j++)
+    {
+      for (i=0; i<nvals; i++)
+        tmp[i]=(unsigned char)((vals[i]>>(8*j))&0xFF);
+      comp_conv_to_mtf_byte(tmp,nvals,valsmtf+j*nvals);
+    }
+  free(tmp);
+}
+
+/* Move to front decoding */
+static void comp_conv_from_mtf_byte(unsigned char *valsmtf, int nvals,
+                             unsigned char *vals)
+{
+  int i;
+  /* Indices into a linked list */
+  int list[256];
+  int dict[256];
+  /* Head of the linked list */
+  int head;
+  for (i=0; i<256; i++)
+    dict[i]=i;
+  for (i=0; i<255; i++)
+    list[i]=i+1;
+  list[255]=-1; /* end. */
+  head=0;
+  for (i=0; i<nvals; i++)
+    {
+      int r=(int)valsmtf[i];
+      /* Find value at position r in the list */
+      int ptr=head;
+      int oldptr=-1;
+      int cnt=0;
+      while (cnt<r)
+        {
+          oldptr=ptr;
+          ptr=list[ptr];
+          cnt++;
+        }
+      vals[i]=(unsigned int)dict[ptr];
+      /* Move it to front in list */
+      /* Is it the head? Then it is already at the front. */
+      if (oldptr!=-1)
+        {
+          /* Remove it from inside the list */
+          list[oldptr]=list[ptr];
+          /* Move it to the front. */
+          list[ptr]=head;
+          head=ptr;
+        }
+    }
+}
+
+void Ptngc_comp_conv_from_mtf_partial(unsigned int *valsmtf, int nvals,
+                                unsigned int *vals)
+{
+  unsigned char *tmp=warnmalloc(nvals*2);
+  int i, j;
+  for (i=0; i<nvals; i++)
+    vals[i]=0U;
+  for (j=0; j<3; j++)
+    {
+      for (i=0; i<nvals; i++)
+        tmp[i]=(unsigned char)((valsmtf[i]>>(8*j))&0xFF);
+      comp_conv_from_mtf_byte(tmp,nvals,tmp+nvals);
+      for (i=0; i<nvals; i++)
+        vals[i]|=(((unsigned int)(tmp[nvals+i]))<<(8*j));
+    }
+  free(tmp);
+}
+
+void Ptngc_comp_conv_from_mtf_partial3(unsigned char *valsmtf, int nvals,
+                                 unsigned int *vals)
+{
+  unsigned char *tmp=warnmalloc(nvals);
+  int i, j;
+  for (i=0; i<nvals; i++)
+    vals[i]=0U;
+  for (j=0; j<3; j++)
+    {
+      comp_conv_from_mtf_byte(valsmtf+j*nvals,nvals,tmp);
+      for (i=0; i<nvals; i++)
+        vals[i]|=(((unsigned int)(tmp[i]))<<(8*j));
+    }
+  free(tmp);
+}
+
+/* Move to front coding.
+   Acceptable inputs are max 24 bits (0-0xFFFFFF) */
+void Ptngc_comp_conv_to_mtf(unsigned int *vals, int nvals,
+                      unsigned int *dict, int ndict,
+                      unsigned int *valsmtf)
+{
+  int i;
+  /* Indices into a linked list */
+  int *list=warnmalloc(ndict*sizeof *list);
+  /* Head of the linked list */
+  int head;
+  for (i=0; i<ndict-1; i++)
+    list[i]=i+1;
+  list[ndict-1]=-1; /* end. */
+  head=0;
+  for (i=0; i<nvals; i++)
+    {
+      int v=vals[i];
+      /* Find how early in the dict the value is */
+      int ptr=head;
+      int oldptr=-1;
+      int r=0;
+      while (dict[ptr]!=v)
+        {
+          oldptr=ptr;
+          ptr=list[ptr];
+          r++;
+        }
+      valsmtf[i]=r;
+      /* Move it to front in list */
+      /* Is it the head? Then it is already at the front. */
+      if (oldptr!=-1)
+        {
+          /* Remove it from inside the list */
+          list[oldptr]=list[ptr];
+          /* Move it to the front. */
+          list[ptr]=head;
+          head=ptr;
+        }
+    }
+  free(list);
+}
+
+/* Move to front decoding */
+void Ptngc_comp_conv_from_mtf(unsigned int *valsmtf, int nvals,
+                        unsigned int *dict, int ndict,
+                        unsigned int *vals)
+{
+  int i;
+  /* Indices into a linked list */
+  int *list=warnmalloc(ndict*sizeof *list);
+  /* Head of the linked list */
+  int head;
+  for (i=0; i<ndict-1; i++)
+    list[i]=i+1;
+  list[ndict-1]=-1; /* end. */
+  head=0;
+  for (i=0; i<nvals; i++)
+    {
+      int r=valsmtf[i];
+      /* Find value at position r in the list */
+      int ptr=head;
+      int oldptr=-1;
+      int cnt=0;
+      while (cnt<r)
+        {
+          oldptr=ptr;
+          ptr=list[ptr];
+          cnt++;
+        }
+      vals[i]=dict[ptr];
+      /* Move it to front in list */
+      /* Is it the head? Then it is already at the front. */
+      if (oldptr!=-1)
+        {
+          /* Remove it from inside the list */
+          list[oldptr]=list[ptr];
+          /* Move it to the front. */
+          list[ptr]=head;
+          head=ptr;
+        }
+    }
+  free(list);
+}
+
diff --git a/src/external/tng_io/src/compression/rle.c b/src/external/tng_io/src/compression/rle.c
new file mode 100644 (file)
index 0000000..adaee65
--- /dev/null
@@ -0,0 +1,99 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include "../../include/compression/rle.h"
+
+static void add_rle(unsigned int *rle,
+                    int v,int nsim,
+                    int *j,int min_rle)
+{
+  if (nsim>min_rle)
+    {
+      /* Insert run-length */
+      unsigned int run=((unsigned int)nsim);
+      while (run>1)
+        {
+          if (run&0x1U)
+            rle[(*j)++]=1;
+          else
+            rle[(*j)++]=0;
+          run>>=1;
+        }
+      nsim=1;
+    }
+  while (nsim--)
+    rle[(*j)++]=v+2;
+}
+
+/* Run length encoding.
+   Acceptable inputs are about 16 bits (0-0xFFFF)
+   If input is 0-N output will be be values of 0-(N+2) */
+void Ptngc_comp_conv_to_rle(unsigned int *vals, int nvals,
+                      unsigned int *rle, int *nrle,
+                      int min_rle)
+{
+  int i;
+  int j=0;
+  int nsim=0;
+  int v=-1;
+  for (i=0; i<nvals; i++)
+    {
+      if (!nsim)
+        {
+          v=vals[i];
+          nsim=1;
+        }
+      else
+        {
+          if (v==vals[i])
+            nsim++;
+          else
+            {
+              add_rle(rle,v,nsim,&j,min_rle);
+              nsim=1;
+              v=vals[i];
+            }
+        }
+    }
+  if (nsim!=0)
+    add_rle(rle,v,nsim,&j,min_rle);
+  *nrle=j;
+}
+
+void Ptngc_comp_conv_from_rle(unsigned int *rle,
+                        unsigned int *vals, int nvals)
+{
+  int i=0;
+  int j=0;
+  while (i<nvals)
+    {
+      int k;
+      unsigned int len=0;
+      unsigned int mask=0x1;
+      unsigned int v=rle[j++];
+      unsigned int hasrle=0;
+      while (v<2)
+        {
+          if (v)
+            len|=mask;
+          mask<<=1;
+          hasrle=1;
+          v=rle[j++];
+        }
+      if (!hasrle)
+        len=1;
+      else
+        len|=mask;
+      for (k=0; k<(int)len; k++)
+        vals[i++]=v-2;
+    }
+}
+
diff --git a/src/external/tng_io/src/compression/tng_compress.c b/src/external/tng_io/src/compression/tng_compress.c
new file mode 100644 (file)
index 0000000..6174e8f
--- /dev/null
@@ -0,0 +1,1891 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "../../include/compression/tng_compress.h"
+#include "../../include/compression/coder.h"
+#include "../../include/compression/fixpoint.h"
+
+/* Please see tng_compress.h for info on how to call these routines. */
+
+/* This becomes TNGP for positions (little endian) and TNGV for velocities. In ASCII. */
+#define MAGIC_INT_POS 0x50474E54
+#define MAGIC_INT_VEL 0x56474E54
+
+#define SPEED_DEFAULT 2 /* Default to relatively fast compression. For very good compression it makes sense to
+                           choose speed=4 or speed=5 */
+
+#define PRECISION(hi,lo) (Ptngc_i32x2_to_d(hi,lo))
+
+#define MAX_FVAL 2147483647.
+
+static int verify_input_data(double *x, int natoms, int nframes, double precision)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        if (fabs(x[iframe*natoms*3+i*3+j]/precision+0.5)>=MAX_FVAL)
+          goto error;
+  return 0;
+ error:
+#if 0
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        if (fabs(x[iframe*natoms*3+i*3+j]/precision+0.5)>=MAX_FVAL)
+          printf("ERROR. Too large value: %d %d %d: %g %g %g\n",iframe,i,j,x[iframe*natoms*3+i*3+j],precision,x[iframe*natoms*3+i*3+j]/precision/MAX_FVAL);
+#endif
+  return 1;
+}
+
+static int verify_input_data_float(float *x, int natoms, int nframes, float precision)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        if (fabs(x[iframe*natoms*3+i*3+j]/precision+0.5)>=MAX_FVAL)
+          goto error;
+  return 0;
+ error:
+#if 0
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        if (fabs(x[iframe*natoms*3+i*3+j]/precision+0.5)>=MAX_FVAL)
+          printf("ERROR. Too large value: %d %d %d: %g %g %g\n",iframe,i,j,x[iframe*natoms*3+i*3+j],precision,x[iframe*natoms*3+i*3+j]/precision/MAX_FVAL);
+#endif
+  return 1;
+}
+
+static int quantize(double *x, int natoms, int nframes,
+                     double precision,
+                     int *quant)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        quant[iframe*natoms*3+i*3+j]=(int)floor((x[iframe*natoms*3+i*3+j]/precision)+0.5);
+  return verify_input_data(x,natoms,nframes,precision);
+}
+
+static int quantize_float(float *x, int natoms, int nframes,
+                           float precision,
+                           int *quant)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        quant[iframe*natoms*3+i*3+j]=(int)floor((x[iframe*natoms*3+i*3+j]/precision)+0.5);
+  return verify_input_data_float(x,natoms,nframes,precision);
+}
+
+static void quant_inter_differences(int *quant, int natoms, int nframes,
+                                    int *quant_inter)
+{
+  int iframe, i, j;
+  /* The first frame is used for absolute positions. */
+  for (i=0; i<natoms; i++)
+    for (j=0; j<3; j++)
+      quant_inter[i*3+j]=quant[i*3+j];
+  /* For all other frames, the difference to the previous frame is used. */
+  for (iframe=1; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        quant_inter[iframe*natoms*3+i*3+j]=quant[iframe*natoms*3+i*3+j]-quant[(iframe-1)*natoms*3+i*3+j];
+}
+
+static void quant_intra_differences(int *quant, int natoms, int nframes,
+                                    int *quant_intra)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    {
+      /* The first atom is used with its absolute position. */
+      for (j=0; j<3; j++)
+        quant_intra[iframe*natoms*3+j]=quant[iframe*natoms*3+j];
+      /* For all other atoms the intraframe differences are computed. */
+      for (i=1; i<natoms; i++)
+        for (j=0; j<3; j++)
+          quant_intra[iframe*natoms*3+i*3+j]=quant[iframe*natoms*3+i*3+j]-quant[iframe*natoms*3+(i-1)*3+j];
+    }
+}
+
+static void unquantize(double *x, int natoms, int nframes,
+                       double precision,
+                       int *quant)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        x[iframe*natoms*3+i*3+j]=(double)quant[iframe*natoms*3+i*3+j]*precision;
+}
+
+static void unquantize_float(float *x, int natoms, int nframes,
+                             float precision,
+                             int *quant)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (i=0; i<natoms; i++)
+      for (j=0; j<3; j++)
+        x[iframe*natoms*3+i*3+j]=(float)quant[iframe*natoms*3+i*3+j]*precision;
+}
+
+static void unquantize_inter_differences(double *x, int natoms, int nframes,
+                                         double precision,
+                                         int *quant)
+{
+  int iframe, i, j;
+  for (i=0; i<natoms; i++)
+    for (j=0; j<3; j++)
+      {
+        int q=quant[i*3+j]; /* First value. */
+        x[i*3+j]=(double)q*precision;
+        for (iframe=1; iframe<nframes; iframe++)
+          {
+            q+=quant[iframe*natoms*3+i*3+j];
+            x[iframe*natoms*3+i*3+j]=(double)q*precision;
+          }
+      }
+}
+
+static void unquantize_inter_differences_float(float *x, int natoms, int nframes,
+                                               float precision,
+                                               int *quant)
+{
+  int iframe, i, j;
+  for (i=0; i<natoms; i++)
+    for (j=0; j<3; j++)
+      {
+        int q=quant[i*3+j]; /* First value. */
+        x[i*3+j]=(float)q*precision;
+        for (iframe=1; iframe<nframes; iframe++)
+          {
+            q+=quant[iframe*natoms*3+i*3+j];
+            x[iframe*natoms*3+i*3+j]=(float)q*precision;
+          }
+      }
+}
+
+static void unquantize_inter_differences_int(int *x, int natoms, int nframes,
+                                             int *quant)
+{
+  int iframe, i, j;
+  for (i=0; i<natoms; i++)
+    for (j=0; j<3; j++)
+      {
+        int q=quant[i*3+j]; /* First value. */
+        x[i*3+j]=q;
+        for (iframe=1; iframe<nframes; iframe++)
+          {
+            q+=quant[iframe*natoms*3+i*3+j];
+            x[iframe*natoms*3+i*3+j]=q;
+          }
+      }
+}
+
+/* In frame update required for the initial frame if intra-frame
+   compression was used. */
+static void unquant_intra_differences_first_frame(int *quant, int natoms)
+{
+  int i,j;
+  for (j=0; j<3; j++)
+    {
+      int q=quant[j];
+      for (i=1; i<natoms; i++)
+        {
+          q+=quant[i*3+j];
+          quant[i*3+j]=q;
+        }
+    }
+#if 0
+  for (j=0; j<3; j++)
+    for (i=0; i<natoms; i++)
+      {
+        printf("UQ: %d %d %d: %d\n",0,j,i,quant[i*3+j]);
+      }
+#endif
+}
+
+static void unquantize_intra_differences(double *x, int natoms, int nframes,
+                                         double precision,
+                                         int *quant)
+{
+  int iframe, i, j;
+#if 0
+  printf("UQ precision=%g\n",precision);
+#endif
+  for (iframe=0; iframe<nframes; iframe++)
+    for (j=0; j<3; j++)
+      {
+        int q=quant[iframe*natoms*3+j];
+        x[iframe*natoms*3+j]=(double)q*precision;
+        for (i=1; i<natoms; i++)
+          {
+            q+=quant[iframe*natoms*3+i*3+j];
+            x[iframe*natoms*3+i*3+j]=(double)q*precision;
+          }
+      }
+}
+
+static void unquantize_intra_differences_float(float *x, int natoms, int nframes,
+                                               float precision,
+                                               int *quant)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (j=0; j<3; j++)
+      {
+        int q=quant[iframe*natoms*3+j];
+        x[iframe*natoms*3+j]=(float)q*precision;
+        for (i=1; i<natoms; i++)
+          {
+            q+=quant[iframe*natoms*3+i*3+j];
+            x[iframe*natoms*3+i*3+j]=(float)q*precision;
+          }
+      }
+}
+
+static void unquantize_intra_differences_int(int *x, int natoms, int nframes,
+                                             int *quant)
+{
+  int iframe, i, j;
+  for (iframe=0; iframe<nframes; iframe++)
+    for (j=0; j<3; j++)
+      {
+        int q=quant[iframe*natoms*3+j];
+        x[iframe*natoms*3+j]=q;
+        for (i=1; i<natoms; i++)
+          {
+            q+=quant[iframe*natoms*3+i*3+j];
+            x[iframe*natoms*3+i*3+j]=q;
+          }
+      }
+}
+
+/* Buffer num 8 bit bytes into buffer location buf */
+static void bufferfix(unsigned char *buf, fix_t v, int num)
+{
+  /* Store in little endian format. */
+  unsigned char c; /* at least 8 bits. */
+  c=(unsigned char)(v & 0xFFU);
+  while (num--)
+    {
+      *buf++=c;
+      v >>= 8;
+      c=(unsigned char)(v & 0xFFU);
+    }
+}
+
+static fix_t readbufferfix(unsigned char *buf, int num)
+{
+  unsigned char b;
+  int shift=0;
+  fix_t f=0UL;
+  int cnt=0;
+  do
+    {
+      b=buf[cnt++];
+      f |= ((fix_t)b & 0xFF)<<shift;
+      shift+=8;
+    } while (--num);
+  return f;
+}
+
+/* Perform position compression from the quantized data. */
+static void compress_quantized_pos(int *quant, int *quant_inter, int *quant_intra,
+                                   int natoms, int nframes,
+                                   int speed,
+                                   int initial_coding, int initial_coding_parameter,
+                                   int coding, int coding_parameter,
+                                   fix_t prec_hi, fix_t prec_lo,
+                                   int *nitems,
+                                   char *data)
+{
+  int bufloc=0;
+  char *datablock=NULL;
+  int length=0;
+  /* Information needed for decompression. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)MAGIC_INT_POS,4);
+  bufloc+=4;
+  /* Number of atoms. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)natoms,4);
+  bufloc+=4;
+  /* Number of frames. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)nframes,4);
+  bufloc+=4;
+  /* Initial coding. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)initial_coding,4);
+  bufloc+=4;
+  /* Initial coding parameter. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)initial_coding_parameter,4);
+  bufloc+=4;
+  /* Coding. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)coding,4);
+  bufloc+=4;
+  /* Coding parameter. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)coding_parameter,4);
+  bufloc+=4;
+  /* Precision. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,prec_lo,4);
+  bufloc+=4;
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,prec_hi,4);
+  bufloc+=4;
+  /* The initial frame */
+  if ((initial_coding==TNG_COMPRESS_ALGO_POS_XTC2) ||
+      (initial_coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE) ||
+      (initial_coding==TNG_COMPRESS_ALGO_POS_XTC3))
+    {
+      struct coder *coder=Ptngc_coder_init();
+      length=natoms*3;
+      datablock=(char*)Ptngc_pack_array(coder,quant,&length,
+                                            initial_coding,initial_coding_parameter,natoms,speed);
+      Ptngc_coder_deinit(coder);
+    }
+  else if ((initial_coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA) ||
+           (initial_coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTRA))
+    {
+      struct coder *coder=Ptngc_coder_init();
+      length=natoms*3;
+      datablock=(char*)Ptngc_pack_array(coder,quant_intra,&length,
+                                            initial_coding,initial_coding_parameter,natoms,speed);
+      Ptngc_coder_deinit(coder);
+    }
+  /* Block length. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)length,4);
+  bufloc+=4;
+  /* The actual data block. */
+  if (data)
+    memcpy(data+bufloc,datablock,length);
+  free(datablock);
+  bufloc+=length;
+  /* The remaining frames */
+  if (nframes>1)
+    {
+      datablock=NULL;
+      /* Inter-frame compression? */
+      if ((coding==TNG_COMPRESS_ALGO_POS_STOPBIT_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTER))
+        {
+          struct coder *coder=Ptngc_coder_init();
+          length=natoms*3*(nframes-1);
+          datablock=(char*)Ptngc_pack_array(coder,quant_inter+natoms*3,&length,
+                                                coding,coding_parameter,natoms,speed);
+          Ptngc_coder_deinit(coder);
+        }
+      /* One-to-one compression? */
+      else if ((coding==TNG_COMPRESS_ALGO_POS_XTC2) ||
+               (coding==TNG_COMPRESS_ALGO_POS_XTC3) ||
+               (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE))
+        {
+          struct coder *coder=Ptngc_coder_init();
+          length=natoms*3*(nframes-1);
+          datablock=(char*)Ptngc_pack_array(coder,quant+natoms*3,&length,
+                                                coding,coding_parameter,natoms,speed);
+          Ptngc_coder_deinit(coder);
+        }
+      /* Intra-frame compression? */
+      else if ((coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA) ||
+               (coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTRA))
+        {
+          struct coder *coder=Ptngc_coder_init();
+          length=natoms*3*(nframes-1);
+          datablock=(char*)Ptngc_pack_array(coder,quant_intra+natoms*3,&length,
+                                                coding,coding_parameter,natoms,speed);
+          Ptngc_coder_deinit(coder);
+        }
+      /* Block length. */
+      if (data)
+        bufferfix((unsigned char*)data+bufloc,(fix_t)length,4);
+      bufloc+=4;
+      if (data)
+        memcpy(data+bufloc,datablock,length);
+      free(datablock);
+      bufloc+=length;
+    }
+  *nitems=bufloc;
+}
+
+/* Perform velocity compression from vel into the data block */
+static void compress_quantized_vel(int *quant, int *quant_inter,
+                                   int natoms, int nframes,
+                                   int speed,
+                                   int initial_coding, int initial_coding_parameter,
+                                   int coding, int coding_parameter,
+                                   fix_t prec_hi, fix_t prec_lo,
+                                   int *nitems,
+                                   char *data)
+{
+  int bufloc=0;
+  char *datablock=NULL;
+  int length;
+  /* Information needed for decompression. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)MAGIC_INT_VEL,4);
+  bufloc+=4;
+  /* Number of atoms. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)natoms,4);
+  bufloc+=4;
+  /* Number of frames. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)nframes,4);
+  bufloc+=4;
+  /* Initial coding. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)initial_coding,4);
+  bufloc+=4;
+  /* Initial coding parameter. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)initial_coding_parameter,4);
+  bufloc+=4;
+  /* Coding. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)coding,4);
+  bufloc+=4;
+  /* Coding parameter. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)coding_parameter,4);
+  bufloc+=4;
+  /* Precision. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,prec_lo,4);
+  bufloc+=4;
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,prec_hi,4);
+  bufloc+=4;
+
+  length=natoms*3;
+  /* The initial frame */
+  if ((initial_coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE) ||
+      (initial_coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE) ||
+      (initial_coding==TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE))
+    {
+      struct coder *coder=Ptngc_coder_init();
+      datablock=(char*)Ptngc_pack_array(coder,quant,&length,
+                                            initial_coding,initial_coding_parameter,natoms,speed);
+      Ptngc_coder_deinit(coder);
+    }
+  /* Block length. */
+  if (data)
+    bufferfix((unsigned char*)data+bufloc,(fix_t)length,4);
+  bufloc+=4;
+  /* The actual data block. */
+  if (data && datablock)
+    {
+      memcpy(data+bufloc,datablock,length);
+      free(datablock);
+      bufloc+=length;
+    }
+  /* The remaining frames */
+  if (nframes>1)
+    {
+      datablock=NULL;
+      /* Inter-frame compression? */
+      if ((coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_VEL_BWLZH_INTER))
+        {
+          struct coder *coder=Ptngc_coder_init();
+          length=natoms*3*(nframes-1);
+          datablock=(char*)Ptngc_pack_array(coder,quant_inter+natoms*3,&length,
+                                                coding,coding_parameter,natoms,speed);
+          Ptngc_coder_deinit(coder);
+        }
+      /* One-to-one compression? */
+      else if ((coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE) ||
+               (coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE) ||
+               (coding==TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE))
+        {
+          struct coder *coder=Ptngc_coder_init();
+          length=natoms*3*(nframes-1);
+          datablock=(char*)Ptngc_pack_array(coder,quant+natoms*3,&length,
+                                                coding,coding_parameter,natoms,speed);
+          Ptngc_coder_deinit(coder);
+        }
+      /* Block length. */
+      if (data)
+        bufferfix((unsigned char*)data+bufloc,(fix_t)length,4);
+      bufloc+=4;
+      if (data)
+        memcpy(data+bufloc,datablock,length);
+      free(datablock);
+      bufloc+=length;
+    }
+  *nitems=bufloc;
+}
+
+static int determine_best_coding_stop_bits(struct coder *coder,int *input, int *length,
+                                           int *coding_parameter, int natoms)
+{
+  int bits;
+  unsigned char *packed;
+  int best_length=0;
+  int new_parameter=-1;
+  int io_length;
+  for (bits=1; bits<20; bits++)
+    {
+      io_length=*length;
+      packed=Ptngc_pack_array(coder,input,&io_length,
+                                  TNG_COMPRESS_ALGO_STOPBIT,bits,natoms,0);
+      if (packed)
+        {
+          if ((new_parameter==-1) || (io_length<best_length))
+            {
+              new_parameter=bits;
+              best_length=io_length;
+            }
+          free(packed);
+        }
+    }
+  if (new_parameter==-1)
+    return 1;
+
+  *coding_parameter=new_parameter;
+  *length=best_length;
+  return 0;
+}
+
+static int determine_best_coding_triple(struct coder *coder,int *input, int *length,
+                                        int *coding_parameter, int natoms)
+{
+  int bits;
+  unsigned char *packed;
+  int best_length=0;
+  int new_parameter=-1;
+  int io_length;
+  for (bits=1; bits<20; bits++)
+    {
+      io_length=*length;
+      packed=Ptngc_pack_array(coder,input,&io_length,
+                                  TNG_COMPRESS_ALGO_TRIPLET,bits,natoms,0);
+      if (packed)
+        {
+          if ((new_parameter==-1) || (io_length<best_length))
+            {
+              new_parameter=bits;
+              best_length=io_length;
+            }
+          free(packed);
+        }
+    }
+  if (new_parameter==-1)
+    return 1;
+
+  *coding_parameter=new_parameter;
+  *length=best_length;
+  return 0;
+}
+
+static void determine_best_pos_initial_coding(int *quant, int *quant_intra, int natoms, int speed,
+                                              fix_t prec_hi, fix_t prec_lo,
+                                              int *initial_coding, int *initial_coding_parameter)
+{
+  if (*initial_coding==-1)
+    {
+      /* Determine all parameters automatically */
+      int best_coding;
+      int best_coding_parameter;
+      int best_code_size;
+      int current_coding;
+      int current_coding_parameter;
+      int current_code_size;
+      struct coder *coder;
+      /* Start with XTC2, it should always work. */
+      current_coding=TNG_COMPRESS_ALGO_POS_XTC2;
+      current_coding_parameter=0;
+      compress_quantized_pos(quant,NULL,quant_intra,natoms,1,speed,
+                             current_coding,current_coding_parameter,
+                             0,0,prec_hi,prec_lo,&current_code_size,NULL);
+      best_coding=current_coding;
+      best_coding_parameter=current_coding_parameter;
+      best_code_size=current_code_size;
+
+      /* Determine best parameter for triplet intra. */
+      current_coding=TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3;
+      current_coding_parameter=0;
+      if (!determine_best_coding_triple(coder,quant_intra,&current_code_size,&current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Determine best parameter for triplet one-to-one. */
+      current_coding=TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3;
+      current_coding_parameter=0;
+      if (!determine_best_coding_triple(coder,quant,&current_code_size,&current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      if (speed>=2)
+        {
+          current_coding=TNG_COMPRESS_ALGO_POS_XTC3;
+          current_coding_parameter=0;
+          compress_quantized_pos(quant,NULL,quant_intra,natoms,1,speed,
+                                 current_coding,current_coding_parameter,
+                                 0,0,prec_hi,prec_lo,&current_code_size,NULL);
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      /* Test BWLZH intra */
+      if (speed>=6)
+        {
+          current_coding=TNG_COMPRESS_ALGO_POS_BWLZH_INTRA;
+          current_coding_parameter=0;
+          compress_quantized_pos(quant,NULL,quant_intra,natoms,1,speed,
+                                 current_coding,current_coding_parameter,
+                                 0,0,prec_hi,prec_lo,&current_code_size,NULL);
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      *initial_coding=best_coding;
+      *initial_coding_parameter=best_coding_parameter;
+    }
+  else
+    {
+      if (*initial_coding_parameter==-1)
+        {
+          if ((*initial_coding==TNG_COMPRESS_ALGO_POS_XTC2) ||
+              (*initial_coding==TNG_COMPRESS_ALGO_POS_XTC3) ||
+              (*initial_coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTRA))
+            *initial_coding_parameter=0;
+          else if (*initial_coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA)
+            {
+              struct coder *coder=Ptngc_coder_init();
+              int current_code_size=natoms*3;
+              determine_best_coding_triple(coder,quant_intra,&current_code_size,initial_coding_parameter,natoms);
+              Ptngc_coder_deinit(coder);
+            }
+          else if (*initial_coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE)
+            {
+              struct coder *coder=Ptngc_coder_init();
+              int current_code_size=natoms*3;
+              determine_best_coding_triple(coder,quant,&current_code_size,initial_coding_parameter,natoms);
+              Ptngc_coder_deinit(coder);
+            }
+        }
+    }
+}
+
+static void determine_best_pos_coding(int *quant, int *quant_inter, int *quant_intra, int natoms, int nframes, int speed,
+                                      fix_t prec_hi, fix_t prec_lo,
+                                      int *coding, int *coding_parameter)
+{
+  if (*coding==-1)
+    {
+      /* Determine all parameters automatically */
+      int best_coding;
+      int best_coding_parameter;
+      int best_code_size;
+      int current_coding;
+      int current_coding_parameter;
+      int current_code_size;
+      int initial_code_size;
+      struct coder *coder;
+      /* Always use XTC2 for the initial coding. */
+      compress_quantized_pos(quant,quant_inter,quant_intra,natoms,1,speed,
+                             TNG_COMPRESS_ALGO_POS_XTC2,0,
+                             0,0,
+                             prec_hi,prec_lo,&initial_code_size,NULL);
+      /* Start with XTC2, it should always work. */
+      current_coding=TNG_COMPRESS_ALGO_POS_XTC2;
+      current_coding_parameter=0;
+      compress_quantized_pos(quant,quant_inter,quant_intra,natoms,nframes,speed,
+                             TNG_COMPRESS_ALGO_POS_XTC2,0,
+                             current_coding,current_coding_parameter,
+                             prec_hi,prec_lo,&current_code_size,NULL);
+      best_coding=current_coding;
+      best_coding_parameter=current_coding_parameter;
+      best_code_size=current_code_size-initial_code_size; /* Correct for the use of XTC2 for the first frame. */
+
+      /* Determine best parameter for stopbit interframe coding. */
+      current_coding=TNG_COMPRESS_ALGO_POS_STOPBIT_INTER;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      if (!determine_best_coding_stop_bits(coder,quant_inter+natoms*3,&current_code_size,
+                                           &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Determine best parameter for triplet interframe coding. */
+      current_coding=TNG_COMPRESS_ALGO_POS_TRIPLET_INTER;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      if (!determine_best_coding_triple(coder,quant_inter+natoms*3,&current_code_size,
+                                        &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Determine best parameter for triplet intraframe coding. */
+      current_coding=TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      if (!determine_best_coding_triple(coder,quant_intra+natoms*3,&current_code_size,
+                                        &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Determine best parameter for triplet one-to-one coding. */
+      current_coding=TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      if (!determine_best_coding_triple(coder,quant+natoms*3,&current_code_size,
+                                        &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Test BWLZH inter */
+      if (speed>=4)
+        {
+          current_coding=TNG_COMPRESS_ALGO_POS_BWLZH_INTER;
+          current_coding_parameter=0;
+          compress_quantized_pos(quant,quant_inter,quant_intra,natoms,nframes,speed,
+                                 TNG_COMPRESS_ALGO_POS_XTC2,0,
+                                 current_coding,current_coding_parameter,
+                                 prec_hi,prec_lo,&current_code_size,NULL);
+          current_code_size-=initial_code_size; /* Correct for the use of XTC2 for the first frame. */
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+
+      /* Test BWLZH intra */
+      if (speed>=6)
+        {
+          current_coding=TNG_COMPRESS_ALGO_POS_BWLZH_INTRA;
+          current_coding_parameter=0;
+          compress_quantized_pos(quant,quant_inter,quant_intra,natoms,nframes,speed,
+                                 TNG_COMPRESS_ALGO_POS_XTC2,0,
+                                 current_coding,current_coding_parameter,
+                                 prec_hi,prec_lo,&current_code_size,NULL);
+          current_code_size-=initial_code_size; /* Correct for the use of XTC2 for the first frame. */
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      *coding=best_coding;
+      *coding_parameter=best_coding_parameter;
+    }
+  else if (*coding_parameter==-1)
+    {
+      if ((*coding==TNG_COMPRESS_ALGO_POS_XTC2) ||
+          (*coding==TNG_COMPRESS_ALGO_POS_XTC3) ||
+          (*coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTER) ||
+          (*coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTRA))
+        *coding_parameter=0;
+      else if (*coding==TNG_COMPRESS_ALGO_POS_STOPBIT_INTER)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_stop_bits(coder,quant_inter+natoms*3,&current_code_size,
+                                          coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTER)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_triple(coder,quant_inter+natoms*3,&current_code_size,
+                                       coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_triple(coder,quant_intra+natoms*3,&current_code_size,
+                                       coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_triple(coder,quant+natoms*3,&current_code_size,
+                                       coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+    }
+}
+
+static void determine_best_vel_initial_coding(int *quant, int natoms, int speed,
+                                              fix_t prec_hi, fix_t prec_lo,
+                                              int *initial_coding, int *initial_coding_parameter)
+{
+  if (*initial_coding==-1)
+    {
+      /* Determine all parameters automatically */
+      int best_coding=-1;
+      int best_coding_parameter=-1;
+      int best_code_size=-1;
+      int current_coding;
+      int current_coding_parameter;
+      int current_code_size;
+      struct coder *coder;
+      /* Start to determine best parameter for stopbit one-to-one. */
+      current_coding=TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE;
+      current_code_size=natoms*3;
+      current_coding_parameter=0;
+      coder=Ptngc_coder_init();
+      if (!determine_best_coding_stop_bits(coder,quant,&current_code_size,
+                                           &current_coding_parameter,natoms))
+        {
+          best_coding=current_coding;
+          best_coding_parameter=current_coding_parameter;
+          best_code_size=current_code_size;
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Determine best parameter for triplet one-to-one. */
+      current_coding=TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE;
+      coder=Ptngc_coder_init();
+      current_code_size=natoms*3;
+      current_coding_parameter=0;
+      if (!determine_best_coding_triple(coder,quant,&current_code_size,&current_coding_parameter,natoms))
+        {
+          if ((best_coding==-1) || (current_code_size<best_code_size))
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+              best_code_size=current_code_size;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Test BWLZH one-to-one */
+      if (speed>=4)
+        {
+          current_coding=TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE;
+          current_coding_parameter=0;
+          compress_quantized_vel(quant,NULL,natoms,1,speed,
+                                 current_coding,current_coding_parameter,
+                                 0,0,prec_hi,prec_lo,&current_code_size,NULL);
+          if ((best_coding==-1) || (current_code_size<best_code_size))
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      *initial_coding=best_coding;
+      *initial_coding_parameter=best_coding_parameter;
+    }
+  else if (*initial_coding_parameter==-1)
+    {
+      if (*initial_coding==TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE)
+        *initial_coding_parameter=0;
+      else if (*initial_coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3;
+          determine_best_coding_stop_bits(coder,quant,&current_code_size,
+                                          initial_coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*initial_coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3;
+          determine_best_coding_triple(coder,quant,&current_code_size,initial_coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+    }
+}
+
+static void determine_best_vel_coding(int *quant, int *quant_inter, int natoms, int nframes, int speed,
+                                      fix_t prec_hi, fix_t prec_lo,
+                                      int *coding, int *coding_parameter)
+{
+  if (*coding==-1)
+    {
+      /* Determine all parameters automatically */
+      int best_coding;
+      int best_coding_parameter;
+      int best_code_size;
+      int current_coding;
+      int current_coding_parameter;
+      int current_code_size;
+      int initial_code_size;
+      int initial_numbits=5;
+      struct coder *coder;
+      /* Use stopbits one-to-one coding for the initial coding. */
+      compress_quantized_vel(quant,NULL,natoms,1,speed,
+                             TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE,initial_numbits,
+                             0,0,prec_hi,prec_lo,&initial_code_size,NULL);
+
+      /* Test stopbit one-to-one */
+      current_coding=TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE;
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      coder=Ptngc_coder_init();
+      determine_best_coding_stop_bits(coder,quant+natoms*3,&current_code_size,
+                                      &current_coding_parameter,natoms);
+      Ptngc_coder_deinit(coder);
+      best_coding=current_coding;
+      best_code_size=current_code_size;
+      best_coding_parameter=current_coding_parameter;
+
+      /* Test triplet interframe */
+      current_coding=TNG_COMPRESS_ALGO_VEL_TRIPLET_INTER;
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      coder=Ptngc_coder_init();
+      if (!determine_best_coding_triple(coder,quant_inter+natoms*3,&current_code_size,
+                                        &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_code_size=current_code_size;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Test triplet one-to-one */
+      current_coding=TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE;
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      coder=Ptngc_coder_init();
+      if (!determine_best_coding_triple(coder,quant+natoms*3,&current_code_size,
+                                        &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_code_size=current_code_size;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      /* Test stopbit interframe */
+      current_coding=TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER;
+      current_code_size=natoms*3*(nframes-1);
+      current_coding_parameter=0;
+      coder=Ptngc_coder_init();
+      if (!determine_best_coding_stop_bits(coder,quant_inter+natoms*3,&current_code_size,
+                                           &current_coding_parameter,natoms))
+        {
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_code_size=current_code_size;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      Ptngc_coder_deinit(coder);
+
+      if (speed>=4)
+        {
+          /* Test BWLZH inter */
+          current_coding=TNG_COMPRESS_ALGO_VEL_BWLZH_INTER;
+          current_coding_parameter=0;
+          compress_quantized_vel(quant,quant_inter,natoms,nframes,speed,
+                                 TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE,initial_numbits,
+                                 current_coding,current_coding_parameter,
+                                 prec_hi,prec_lo,&current_code_size,NULL);
+          current_code_size-=initial_code_size; /* Correct for the initial frame */
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_code_size=current_code_size;
+              best_coding_parameter=current_coding_parameter;
+            }
+
+          /* Test BWLZH one-to-one */
+          current_coding=TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE;
+          current_coding_parameter=0;
+          compress_quantized_vel(quant,quant_inter,natoms,nframes,speed,
+                                 TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE,initial_numbits,
+                                 current_coding,current_coding_parameter,
+                                 prec_hi,prec_lo,&current_code_size,NULL);
+          current_code_size-=initial_code_size; /* Correct for the initial frame */
+          if (current_code_size<best_code_size)
+            {
+              best_coding=current_coding;
+              best_coding_parameter=current_coding_parameter;
+            }
+        }
+      *coding=best_coding;
+      *coding_parameter=best_coding_parameter;
+    }
+  else if (*coding_parameter==-1)
+    {
+      if ((*coding==TNG_COMPRESS_ALGO_VEL_BWLZH_INTER) ||
+          (*coding==TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE))
+        *coding_parameter=0;
+      else if (*coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_stop_bits(coder,quant+natoms*3,&current_code_size,
+                                          coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_INTER)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_triple(coder,quant_inter+natoms*3,&current_code_size,
+                                       coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_triple(coder,quant+natoms*3,&current_code_size,
+                                       coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+      else if (*coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER)
+        {
+          struct coder *coder=Ptngc_coder_init();
+          int current_code_size=natoms*3*(nframes-1);
+          determine_best_coding_stop_bits(coder,quant_inter+natoms*3,&current_code_size,
+                                          coding_parameter,natoms);
+          Ptngc_coder_deinit(coder);
+        }
+    }
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos_int(int *pos, int natoms, int nframes,
+                                             unsigned long prec_hi, unsigned long prec_lo,
+                                             int speed,int *algo,
+                                             int *nitems)
+{
+  char *data=malloc(natoms*nframes*14+11*4); /* 12 bytes are required to store 4 32 bit integers
+                                               This is 17% extra. The final 11*4 is to store information
+                                               needed for decompression. */
+  int *quant=pos; /* Already quantized positions. */
+  int *quant_intra=malloc(natoms*nframes*3*sizeof *quant_intra);
+  int *quant_inter=malloc(natoms*nframes*3*sizeof *quant_inter);
+
+  int initial_coding, initial_coding_parameter;
+  int coding, coding_parameter;
+  if (speed==0)
+    speed=SPEED_DEFAULT; /* Set the default speed. */
+  /* Boundaries of speed. */
+  if (speed<1)
+    speed=1;
+  if (speed>6)
+    speed=6;
+  initial_coding=algo[0];
+  initial_coding_parameter=algo[1];
+  coding=algo[2];
+  coding_parameter=algo[3];
+
+  quant_inter_differences(quant,natoms,nframes,quant_inter);
+  quant_intra_differences(quant,natoms,nframes,quant_intra);
+
+  /* If any of the above codings / coding parameters are == -1, the optimal parameters must be found */
+  if (initial_coding==-1)
+    {
+      initial_coding_parameter=-1;
+      determine_best_pos_initial_coding(quant,quant_intra,natoms,speed,prec_hi,prec_lo,
+                                        &initial_coding,&initial_coding_parameter);
+    }
+  else if (initial_coding_parameter==-1)
+    {
+      determine_best_pos_initial_coding(quant,quant_intra,natoms,speed,prec_hi,prec_lo,
+                                        &initial_coding,&initial_coding_parameter);
+    }
+
+  if (nframes==1)
+    {
+      coding=0;
+      coding_parameter=0;
+    }
+
+  if (nframes>1)
+    {
+      if (coding==-1)
+        {
+          coding_parameter=-1;
+          determine_best_pos_coding(quant,quant_inter,quant_intra,natoms,nframes,speed,prec_hi,prec_lo,
+                                    &coding,&coding_parameter);
+        }
+      else if (coding_parameter==-1)
+        {
+          determine_best_pos_coding(quant,quant_inter,quant_intra,natoms,nframes,speed,prec_hi,prec_lo,
+                                    &coding,&coding_parameter);
+        }
+    }
+
+  compress_quantized_pos(quant,quant_inter,quant_intra,natoms,nframes,speed,
+                         initial_coding,initial_coding_parameter,
+                         coding,coding_parameter,
+                         prec_hi,prec_lo,nitems,data);
+  free(quant_inter);
+  free(quant_intra);
+  if (algo[0]==-1)
+    algo[0]=initial_coding;
+  if (algo[1]==-1)
+    algo[1]=initial_coding_parameter;
+  if (algo[2]==-1)
+    algo[2]=coding;
+  if (algo[3]==-1)
+    algo[3]=coding_parameter;
+  return data;
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos(double *pos, int natoms, int nframes,
+                                         double desired_precision,
+                                         int speed,int *algo,
+                                         int *nitems)
+{
+  int *quant=malloc(natoms*nframes*3*sizeof *quant);
+  char *data;
+  fix_t prec_hi, prec_lo;
+  Ptngc_d_to_i32x2(desired_precision,&prec_hi,&prec_lo);
+
+  if (quantize(pos,natoms,nframes,PRECISION(prec_hi,prec_lo),quant))
+    data=NULL; /* Error occured. Too large input values. */
+  else
+    data=tng_compress_pos_int(quant,natoms,nframes,prec_hi,prec_lo,speed,algo,nitems);
+  free(quant);
+  return data;
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos_float(float *pos, int natoms, int nframes,
+                                               float desired_precision,
+                                               int speed,int *algo,
+                                               int *nitems)
+{
+  int *quant=malloc(natoms*nframes*3*sizeof *quant);
+  char *data;
+  fix_t prec_hi, prec_lo;
+  Ptngc_d_to_i32x2((double)desired_precision,&prec_hi,&prec_lo);
+
+  if (quantize_float(pos,natoms,nframes,(float)PRECISION(prec_hi,prec_lo),quant))
+    data=NULL; /* Error occured. Too large input values. */
+  else
+    data=tng_compress_pos_int(quant,natoms,nframes,prec_hi,prec_lo,speed,algo,nitems);
+  free(quant);
+  return data;
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos_find_algo(double *pos, int natoms, int nframes,
+                                                   double desired_precision,
+                                                   int speed,
+                                                   int *algo,
+                                                   int *nitems)
+{
+  algo[0]=-1;
+  algo[1]=-1;
+  algo[2]=-1;
+  algo[3]=-1;
+  return tng_compress_pos(pos,natoms,nframes,desired_precision,speed,algo,nitems);
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos_float_find_algo(float *pos, int natoms, int nframes,
+                                                         float desired_precision,
+                                                         int speed,
+                                                         int *algo,
+                                                         int *nitems)
+{
+  algo[0]=-1;
+  algo[1]=-1;
+  algo[2]=-1;
+  algo[3]=-1;
+  return tng_compress_pos_float(pos,natoms,nframes,desired_precision,speed,algo,nitems);
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos_int_find_algo(int *pos, int natoms, int nframes,
+                                                       unsigned long prec_hi, unsigned long prec_lo,
+                                                       int speed,int *algo,
+                                                       int *nitems)
+{
+  algo[0]=-1;
+  algo[1]=-1;
+  algo[2]=-1;
+  algo[3]=-1;
+  return tng_compress_pos_int(pos,natoms,nframes,prec_hi,prec_lo,speed,algo,nitems);
+}
+
+
+
+int DECLSPECDLLEXPORT tng_compress_nalgo(void)
+{
+  return 4; /* There are currently four parameters required:
+
+ 1) The compression algorithm for the first frame (initial_coding).
+ 2) One parameter to the algorithm for the first frame (the initial coding parameter).
+ 3) The compression algorithm for the remaining frames (coding).
+ 4) One parameter to the algorithm for the remaining frames (the coding parameter). */
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel_int(int *vel, int natoms, int nframes,
+                                             unsigned long prec_hi, unsigned long prec_lo,
+                                             int speed, int *algo,
+                                             int *nitems)
+{
+  char *data=malloc(natoms*nframes*14+11*4); /* 12 bytes are required to store 4 32 bit integers
+                                               This is 17% extra. The final 11*4 is to store information
+                                               needed for decompression. */
+  int *quant=vel;
+  int *quant_inter=malloc(natoms*nframes*3*sizeof *quant_inter);
+
+  int initial_coding, initial_coding_parameter;
+  int coding, coding_parameter;
+  if (speed==0)
+    speed=SPEED_DEFAULT; /* Set the default speed. */
+  /* Boundaries of speed. */
+  if (speed<1)
+    speed=1;
+  if (speed>6)
+    speed=6;
+  initial_coding=algo[0];
+  initial_coding_parameter=algo[1];
+  coding=algo[2];
+  coding_parameter=algo[3];
+
+  quant_inter_differences(quant,natoms,nframes,quant_inter);
+
+  /* If any of the above codings / coding parameters are == -1, the optimal parameters must be found */
+  if (initial_coding==-1)
+    {
+      initial_coding_parameter=-1;
+      determine_best_vel_initial_coding(quant,natoms,speed,prec_hi,prec_lo,
+                                        &initial_coding,&initial_coding_parameter);
+    }
+  else if (initial_coding_parameter==-1)
+    {
+      determine_best_vel_initial_coding(quant,natoms,speed,prec_hi,prec_lo,
+                                        &initial_coding,&initial_coding_parameter);
+    }
+
+  if (nframes==1)
+    {
+      coding=0;
+      coding_parameter=0;
+    }
+
+  if (nframes>1)
+    {
+      if (coding==-1)
+        {
+          coding_parameter=-1;
+          determine_best_vel_coding(quant,quant_inter,natoms,nframes,speed,prec_hi,prec_lo,
+                                    &coding,&coding_parameter);
+        }
+      else if (coding_parameter==-1)
+        {
+          determine_best_vel_coding(quant,quant_inter,natoms,nframes,speed,prec_hi,prec_lo,
+                                    &coding,&coding_parameter);
+        }
+    }
+
+  compress_quantized_vel(quant,quant_inter,natoms,nframes,speed,
+                         initial_coding,initial_coding_parameter,
+                         coding,coding_parameter,
+                         prec_hi,prec_lo,nitems,data);
+  free(quant_inter);
+  if (algo[0]==-1)
+    algo[0]=initial_coding;
+  if (algo[1]==-1)
+    algo[1]=initial_coding_parameter;
+  if (algo[2]==-1)
+    algo[2]=coding;
+  if (algo[3]==-1)
+    algo[3]=coding_parameter;
+  return data;
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel(double *vel, int natoms, int nframes,
+                                         double desired_precision,
+                                         int speed, int *algo,
+                                         int *nitems)
+{
+  int *quant=malloc(natoms*nframes*3*sizeof *quant);
+  char *data;
+  fix_t prec_hi, prec_lo;
+  Ptngc_d_to_i32x2(desired_precision,&prec_hi,&prec_lo);
+  if (quantize(vel,natoms,nframes,PRECISION(prec_hi,prec_lo),quant))
+    data=NULL; /* Error occured. Too large input values. */
+  else
+    data=tng_compress_vel_int(quant,natoms,nframes,prec_hi,prec_lo,speed,algo,nitems);
+  free(quant);
+  return data;
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel_float(float *vel, int natoms, int nframes,
+                                               float desired_precision,
+                                               int speed, int *algo,
+                                               int *nitems)
+{
+  int *quant=malloc(natoms*nframes*3*sizeof *quant);
+  char *data;
+  fix_t prec_hi, prec_lo;
+  Ptngc_d_to_i32x2((double)desired_precision,&prec_hi,&prec_lo);
+  if (quantize_float(vel,natoms,nframes,(float)PRECISION(prec_hi,prec_lo),quant))
+    data=NULL; /* Error occured. Too large input values. */
+  else
+    data=tng_compress_vel_int(quant,natoms,nframes,prec_hi,prec_lo,speed,algo,nitems);
+  free(quant);
+  return data;
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel_find_algo(double *vel, int natoms, int nframes,
+                                                   double desired_precision,
+                                                   int speed,
+                                                   int *algo,
+                                                   int *nitems)
+{
+  algo[0]=-1;
+  algo[1]=-1;
+  algo[2]=-1;
+  algo[3]=-1;
+  return tng_compress_vel(vel,natoms,nframes,desired_precision,speed,algo,nitems);
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel_float_find_algo(float *vel, int natoms, int nframes,
+                                                         float desired_precision,
+                                                         int speed,
+                                                         int *algo,
+                                                         int *nitems)
+{
+  algo[0]=-1;
+  algo[1]=-1;
+  algo[2]=-1;
+  algo[3]=-1;
+  return tng_compress_vel_float(vel,natoms,nframes,desired_precision,speed,algo,nitems);
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel_int_find_algo(int *vel, int natoms, int nframes,
+                                                       unsigned long prec_hi, unsigned long prec_lo,
+                                                       int speed,
+                                                       int *algo,
+                                                       int *nitems)
+{
+  algo[0]=-1;
+  algo[1]=-1;
+  algo[2]=-1;
+  algo[3]=-1;
+  return tng_compress_vel_int(vel,natoms,nframes,prec_hi,prec_lo,speed,algo,nitems);
+}
+
+int DECLSPECDLLEXPORT tng_compress_inquire(char *data,int *vel, int *natoms,
+                                            int *nframes, double *precision,
+                                            int *algo)
+{
+  int bufloc=0;
+  fix_t prec_hi, prec_lo;
+  int initial_coding, initial_coding_parameter;
+  int coding, coding_parameter;
+  int magic_int;
+  magic_int=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  if (magic_int==MAGIC_INT_POS)
+    *vel=0;
+  else if (magic_int==MAGIC_INT_VEL)
+    *vel=1;
+  else
+    return 1;
+  /* Number of atoms. */
+  *natoms=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Number of frames. */
+  *nframes=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Initial coding. */
+  initial_coding=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Initial coding parameter. */
+  initial_coding_parameter=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Coding. */
+  coding=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Coding parameter. */
+  coding_parameter=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Precision. */
+  prec_lo=readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  prec_hi=readbufferfix((unsigned char *)data+bufloc,4);
+  *precision=PRECISION(prec_hi, prec_lo);
+  algo[0]=initial_coding;
+  algo[1]=initial_coding_parameter;
+  algo[2]=coding;
+  algo[3]=coding_parameter;
+  return 0;
+}
+
+static int tng_compress_uncompress_pos_gen(char *data,double *posd,float *posf,int *posi,unsigned long *prec_hi, unsigned long *prec_lo)
+{
+  int bufloc=0;
+  int length;
+  int natoms, nframes;
+  int initial_coding, initial_coding_parameter;
+  int coding, coding_parameter;
+  int *quant=NULL;
+  struct coder *coder=NULL;
+  int rval=0;
+  int magic_int;
+  /* Magic integer for positions */
+  magic_int=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  if (magic_int!=MAGIC_INT_POS)
+    {
+      rval=1;
+      goto error;
+    }
+  /* Number of atoms. */
+  natoms=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Number of frames. */
+  nframes=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Initial coding. */
+  initial_coding=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Initial coding parameter. */
+  initial_coding_parameter=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Coding. */
+  coding=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Coding parameter. */
+  coding_parameter=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Precision. */
+  *prec_lo=readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  *prec_hi=readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Allocate the memory for the quantized positions */
+  quant=malloc(natoms*nframes*3*sizeof *quant);
+  /* The data block length. */
+  length=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* The initial frame */
+  coder=Ptngc_coder_init();
+  rval=Ptngc_unpack_array(coder,(unsigned char*)data+bufloc,quant,natoms*3,
+                         initial_coding,initial_coding_parameter,natoms);
+  Ptngc_coder_deinit(coder);
+  if (rval)
+    goto error;
+  /* Skip past the actual data block. */
+  bufloc+=length;
+  /* Obtain the actual positions for the initial block. */
+  if ((initial_coding==TNG_COMPRESS_ALGO_POS_XTC2) ||
+      (initial_coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE) ||
+      (initial_coding==TNG_COMPRESS_ALGO_POS_XTC3))
+    {
+      if (posd)
+        unquantize(posd,natoms,1,PRECISION(*prec_hi,*prec_lo),quant);
+      else if (posf)
+        unquantize_float(posf,natoms,1,(float)PRECISION(*prec_hi,*prec_lo),quant);
+      else if (posi)
+        memcpy(posi,quant,natoms*3*sizeof *posi);
+    }
+  else if ((initial_coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA) ||
+           (initial_coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTRA))
+    {
+      if (posd)
+        unquantize_intra_differences(posd,natoms,1,PRECISION(*prec_hi,*prec_lo),quant);
+      else if (posf)
+        unquantize_intra_differences_float(posf,natoms,1,(float)PRECISION(*prec_hi,*prec_lo),quant);
+      else if (posi)
+        unquantize_intra_differences_int(posi,natoms,1,quant);
+      unquant_intra_differences_first_frame(quant,natoms);
+    }
+  /* The remaining frames. */
+  if (nframes>1)
+    {
+      bufloc+=4;
+      coder=Ptngc_coder_init();
+      rval=Ptngc_unpack_array(coder,(unsigned char *)data+bufloc,quant+natoms*3,(nframes-1)*natoms*3,
+                                  coding,coding_parameter,natoms);
+      Ptngc_coder_deinit(coder);
+      if (rval)
+        goto error;
+      if ((coding==TNG_COMPRESS_ALGO_POS_STOPBIT_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTER))
+        {
+          /* This requires that the first frame is already in one-to-one format, even if intra-frame
+             compression was done there. Therefore the unquant_intra_differences_first_frame should be called
+             before to convert it correctly. */
+          if (posd)
+            unquantize_inter_differences(posd,natoms,nframes,PRECISION(*prec_hi,*prec_lo),quant);
+          else if (posf)
+            unquantize_inter_differences_float(posf,natoms,nframes,(float)PRECISION(*prec_hi,*prec_lo),quant);
+          else if (posi)
+            unquantize_inter_differences_int(posi,natoms,nframes,quant);
+        }
+      else if ((coding==TNG_COMPRESS_ALGO_POS_XTC2) ||
+               (coding==TNG_COMPRESS_ALGO_POS_XTC3) ||
+               (coding==TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE))
+        {
+          if (posd)
+            unquantize(posd+natoms*3,natoms,nframes-1,PRECISION(*prec_hi,*prec_lo),quant+natoms*3);
+          else if (posf)
+            unquantize_float(posf+natoms*3,natoms,nframes-1,(float)PRECISION(*prec_hi,*prec_lo),quant+natoms*3);
+          else if (posi)
+            memcpy(posi+natoms*3,quant+natoms*3,natoms*3*(nframes-1)*sizeof *posi);
+        }
+      else if ((coding==TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA) ||
+               (coding==TNG_COMPRESS_ALGO_POS_BWLZH_INTRA))
+        {
+          if (posd)
+            unquantize_intra_differences(posd+natoms*3,natoms,nframes-1,PRECISION(*prec_hi,*prec_lo),quant+natoms*3);
+          else if (posf)
+            unquantize_intra_differences_float(posf+natoms*3,natoms,nframes-1,(float)PRECISION(*prec_hi,*prec_lo),quant+natoms*3);
+          else if (posi)
+            unquantize_intra_differences_int(posi+natoms*3,natoms,nframes-1,quant+natoms*3);
+        }
+    }
+ error:
+  free(quant);
+  return rval;
+}
+
+static int tng_compress_uncompress_pos(char *data,double *pos)
+{
+  unsigned long prec_hi, prec_lo;
+  return tng_compress_uncompress_pos_gen(data,pos,NULL,NULL,&prec_hi,&prec_lo);
+}
+
+static int tng_compress_uncompress_pos_float(char *data,float *pos)
+{
+  unsigned long prec_hi, prec_lo;
+  return tng_compress_uncompress_pos_gen(data,NULL,pos,NULL,&prec_hi,&prec_lo);
+}
+
+static int tng_compress_uncompress_pos_int(char *data,int *pos, unsigned long *prec_hi, unsigned long *prec_lo)
+{
+  return tng_compress_uncompress_pos_gen(data,NULL,NULL,pos,prec_hi,prec_lo);
+}
+
+static int tng_compress_uncompress_vel_gen(char *data,double *veld,float *velf,int *veli,unsigned long *prec_hi, unsigned long *prec_lo)
+{
+  int bufloc=0;
+  int length;
+  int natoms, nframes;
+  int initial_coding, initial_coding_parameter;
+  int coding, coding_parameter;
+  int *quant=NULL;
+  struct coder *coder=NULL;
+  int rval=0;
+  int magic_int;
+  /* Magic integer for velocities */
+  magic_int=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  if (magic_int!=MAGIC_INT_VEL)
+    {
+      rval=1;
+      goto error;
+    }
+  /* Number of atoms. */
+  natoms=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Number of frames. */
+  nframes=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Initial coding. */
+  initial_coding=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Initial coding parameter. */
+  initial_coding_parameter=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Coding. */
+  coding=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Coding parameter. */
+  coding_parameter=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Precision. */
+  *prec_lo=readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  *prec_hi=readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* Allocate the memory for the quantized positions */
+  quant=malloc(natoms*nframes*3*sizeof *quant);
+  /* The data block length. */
+  length=(int)readbufferfix((unsigned char *)data+bufloc,4);
+  bufloc+=4;
+  /* The initial frame */
+  coder=Ptngc_coder_init();
+  rval=Ptngc_unpack_array(coder,(unsigned char*)data+bufloc,quant,natoms*3,
+                         initial_coding,initial_coding_parameter,natoms);
+  Ptngc_coder_deinit(coder);
+  if (rval)
+    goto error;
+  /* Skip past the actual data block. */
+  bufloc+=length;
+  /* Obtain the actual positions for the initial block. */
+  if ((initial_coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE) ||
+      (initial_coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE) ||
+      (initial_coding==TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE))
+    {
+      if (veld)
+        unquantize(veld,natoms,1,PRECISION(*prec_hi,*prec_lo),quant);
+      else if (velf)
+        unquantize_float(velf,natoms,1,(float)PRECISION(*prec_hi,*prec_lo),quant);
+      else if (veli)
+        memcpy(veli,quant,natoms*3*sizeof *veli);
+    }
+  /* The remaining frames. */
+  if (nframes>1)
+    {
+      bufloc+=4;
+      coder=Ptngc_coder_init();
+      rval=Ptngc_unpack_array(coder,(unsigned char *)data+bufloc,quant+natoms*3,(nframes-1)*natoms*3,
+                                  coding,coding_parameter,natoms);
+      Ptngc_coder_deinit(coder);
+      if (rval)
+        goto error;
+      /* Inter-frame compression? */
+      if ((coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER) ||
+          (coding==TNG_COMPRESS_ALGO_VEL_BWLZH_INTER))
+        {
+          /* This requires that the first frame is already in one-to-one format. */
+          if (veld)
+            unquantize_inter_differences(veld,natoms,nframes,PRECISION(*prec_hi,*prec_lo),quant);
+          else if (velf)
+            unquantize_inter_differences_float(velf,natoms,nframes,(float)PRECISION(*prec_hi,*prec_lo),quant);
+          else if (veli)
+            unquantize_inter_differences_int(veli,natoms,nframes,quant);
+        }
+      /* One-to-one compression? */
+      else if ((coding==TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE) ||
+               (coding==TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE) ||
+               (coding==TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE))
+        {
+          if (veld)
+            unquantize(veld+natoms*3,natoms,nframes-1,PRECISION(*prec_hi,*prec_lo),quant+natoms*3);
+          else if (velf)
+            unquantize_float(velf+natoms*3,natoms,nframes-1,(float)PRECISION(*prec_hi,*prec_lo),quant+natoms*3);
+          else if (veli)
+            memcpy(veli+natoms*3,quant+natoms*3,natoms*3*(nframes-1)*sizeof *veli);
+        }
+    }
+ error:
+  free(quant);
+  return rval;
+}
+
+static int tng_compress_uncompress_vel(char *data,double *vel)
+{
+  unsigned long prec_hi, prec_lo;
+  return tng_compress_uncompress_vel_gen(data,vel,NULL,NULL,&prec_hi,&prec_lo);
+}
+
+static int tng_compress_uncompress_vel_float(char *data,float *vel)
+{
+  unsigned long prec_hi, prec_lo;
+  return tng_compress_uncompress_vel_gen(data,NULL,vel,NULL,&prec_hi,&prec_lo);
+}
+
+static int tng_compress_uncompress_vel_int(char *data,int *vel, unsigned long *prec_hi, unsigned long *prec_lo)
+{
+  return tng_compress_uncompress_vel_gen(data,NULL,NULL,vel,prec_hi,prec_lo);
+}
+
+/* Uncompresses any tng compress block, positions or velocities. It determines whether it is positions or velocities from the data buffer. The return value is 0 if ok, and 1 if not.
+*/
+int DECLSPECDLLEXPORT tng_compress_uncompress(char *data,double *posvel)
+{
+  int magic_int;
+  magic_int=(int)readbufferfix((unsigned char *)data,4);
+  if (magic_int==MAGIC_INT_POS)
+    return tng_compress_uncompress_pos(data,posvel);
+  else if (magic_int==MAGIC_INT_VEL)
+    return tng_compress_uncompress_vel(data,posvel);
+  else
+    return 1;
+}
+
+int DECLSPECDLLEXPORT tng_compress_uncompress_float(char *data,float *posvel)
+{
+  int magic_int;
+  magic_int=(int)readbufferfix((unsigned char *)data,4);
+  if (magic_int==MAGIC_INT_POS)
+    return tng_compress_uncompress_pos_float(data,posvel);
+  else if (magic_int==MAGIC_INT_VEL)
+    return tng_compress_uncompress_vel_float(data,posvel);
+  else
+    return 1;
+}
+
+int DECLSPECDLLEXPORT tng_compress_uncompress_int(char *data,int *posvel, unsigned long *prec_hi, unsigned long *prec_lo)
+{
+  int magic_int;
+  magic_int=(int)readbufferfix((unsigned char *)data,4);
+  if (magic_int==MAGIC_INT_POS)
+    return tng_compress_uncompress_pos_int(data,posvel,prec_hi,prec_lo);
+  else if (magic_int==MAGIC_INT_VEL)
+    return tng_compress_uncompress_vel_int(data,posvel,prec_hi,prec_lo);
+  else
+    return 1;
+}
+
+void DECLSPECDLLEXPORT tng_compress_int_to_double(int *posvel_int,unsigned long prec_hi, unsigned long prec_lo,
+                                                  int natoms,int nframes,
+                                                  double *posvel_double)
+{
+  unquantize(posvel_double,natoms,nframes,PRECISION(prec_hi,prec_lo),posvel_int);
+}
+
+void DECLSPECDLLEXPORT tng_compress_int_to_float(int *posvel_int,unsigned long prec_hi, unsigned long prec_lo,
+                                                 int natoms,int nframes,
+                                                 float *posvel_float)
+{
+  unquantize_float(posvel_float,natoms,nframes,(float)PRECISION(prec_hi,prec_lo),posvel_int);
+}
+
+static char *compress_algo_pos[TNG_COMPRESS_ALGO_MAX]={
+  "Positions invalid algorithm",
+  "Positions stopbits interframe",
+  "Positions triplet interframe",
+  "Positions triplet intraframe",
+  "Positions invalid algorithm",
+  "Positions XTC2",
+  "Positions invalid algorithm",
+  "Positions triplet one to one",
+  "Positions BWLZH interframe",
+  "Positions BWLZH intraframe",
+  "Positions XTC3"
+};
+
+static char *compress_algo_vel[TNG_COMPRESS_ALGO_MAX]={
+  "Velocities invalid algorithm",
+  "Velocities stopbits one to one",
+  "Velocities triplet interframe",
+  "Velocities triplet one to one",
+  "Velocities invalid algorithm",
+  "Velocities invalid algorithm",
+  "Velocities stopbits interframe",
+  "Velocities invalid algorithm",
+  "Velocities BWLZH interframe",
+  "Velocities BWLZH one to one",
+  "Velocities invalid algorithm"
+};
+
+char DECLSPECDLLEXPORT *tng_compress_initial_pos_algo(int *algo)
+{
+  int i=algo[0];
+  if (i<0)
+    i=0;
+  if (i>=TNG_COMPRESS_ALGO_MAX)
+    i=0;
+  return compress_algo_pos[i];
+}
+
+char DECLSPECDLLEXPORT *tng_compress_pos_algo(int *algo)
+{
+  int i=algo[2];
+  if (i<0)
+    i=0;
+  if (i>=TNG_COMPRESS_ALGO_MAX)
+    i=0;
+  return compress_algo_pos[i];
+}
+
+char DECLSPECDLLEXPORT *tng_compress_initial_vel_algo(int *algo)
+{
+  int i=algo[0];
+  if (i<0)
+    i=0;
+  if (i>=TNG_COMPRESS_ALGO_MAX)
+    i=0;
+  return compress_algo_vel[i];
+}
+
+char DECLSPECDLLEXPORT *tng_compress_vel_algo(int *algo)
+{
+  int i=algo[2];
+  if (i<0)
+    i=0;
+  if (i>=TNG_COMPRESS_ALGO_MAX)
+    i=0;
+  return compress_algo_vel[i];
+}
diff --git a/src/external/tng_io/src/compression/vals16.c b/src/external/tng_io/src/compression/vals16.c
new file mode 100644 (file)
index 0000000..c4ed635
--- /dev/null
@@ -0,0 +1,71 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include "../../include/compression/vals16.h"
+
+/* Coding 32 bit ints in sequences of 16 bit ints. Worst case
+   the output is 3*nvals long. */
+void Ptngc_comp_conv_to_vals16(unsigned int *vals,int nvals,
+                         unsigned int *vals16, int *nvals16)
+{
+  int i;
+  int j=0;
+  for (i=0; i<nvals; i++)
+    {
+      if (vals[i]<=0x7FFFU)
+        vals16[j++]=vals[i];
+      else
+        {
+          unsigned int lo=(vals[i]&0x7FFFU)|0x8000U;
+          unsigned int hi=vals[i]>>15;
+          vals16[j++]=lo;
+          if (hi<=0x7FFFU)
+            vals16[j++]=hi;
+          else
+            {
+              unsigned int lohi=(hi&0x7FFFU)|0x8000U;
+              unsigned int hihi=hi>>15;
+              vals16[j++]=lohi;
+              vals16[j++]=hihi;
+            }
+        }
+    }
+#if 0
+  /* Test that things that detect that this is bad really works. */
+  vals16[0]=0;
+#endif
+  *nvals16=j;
+}
+
+void Ptngc_comp_conv_from_vals16(unsigned int *vals16,int nvals16,
+                           unsigned int *vals, int *nvals)
+{
+  int i=0;
+  int j=0;
+  while (i<nvals16)
+    {
+      if (vals16[i]<=0x7FFFU)
+        vals[j++]=vals16[i++];
+      else
+        {
+          unsigned int lo=vals16[i++];
+          unsigned int hi=vals16[i++];
+          if (hi<=0x7FFFU)
+            vals[j++]=(lo&0x7FFFU)|(hi<<15);
+          else
+            {
+              unsigned int hihi=vals16[i++];
+              vals[j++]=(lo&0x7FFFU)|((hi&0x7FFFU)<<15)|(hihi<<30);
+            }
+        }
+    }
+  *nvals=j;
+}
diff --git a/src/external/tng_io/src/compression/warnmalloc.c b/src/external/tng_io/src/compression/warnmalloc.c
new file mode 100644 (file)
index 0000000..36f0d9f
--- /dev/null
@@ -0,0 +1,37 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../../include/compression/tng_compress.h"
+#include "../../include/compression/warnmalloc.h"
+
+void DECLSPECDLLEXPORT *Ptngc_warnmalloc_x(size_t size, char *file, int line)
+{
+  void *mem=malloc(size);
+  if (!mem)
+    {
+      fprintf(stderr,"TRAJNG ERROR: Could not allocate memory of size %lu at %s:%d\n",(unsigned long) size,file,line);
+      exit(EXIT_FAILURE);
+    }
+  return mem;
+}
+
+void DECLSPECDLLEXPORT *Ptngc_warnrealloc_x(void *old, size_t size, char *file, int line)
+{
+  void *mem=realloc(old,size);
+  if (!mem)
+    {
+      fprintf(stderr,"TRAJNG ERROR: Could not allocate memory of size %lu at %s:%d\n",(unsigned long) size,file,line);
+      exit(EXIT_FAILURE);
+    }
+  return mem;
+}
diff --git a/src/external/tng_io/src/compression/widemuldiv.c b/src/external/tng_io/src/compression/widemuldiv.c
new file mode 100644 (file)
index 0000000..7d03c57
--- /dev/null
@@ -0,0 +1,229 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../../include/compression/tng_compress.h"
+
+/* 64 bit integers are not required in this part of the program. But
+   they improve the running speed if present. If 64 bit integers are
+   available define the symbol HAVE64BIT. It should get automatically
+   defined by the defines in my64bit.h */
+#include "../../include/compression/my64bit.h"
+
+#include "../../include/compression/widemuldiv.h"
+
+#ifndef TRAJNG_X86_GCC_INLINE_MULDIV
+#if defined(__GNUC__) && defined(__i386__)
+#define TRAJNG_X86_GCC_INLINE_MULDIV
+#endif /* gcc & i386 */
+#if defined(__GNUC__) && defined(__x86_64__)
+#define TRAJNG_X86_GCC_INLINE_MULDIV
+#endif /* gcc & x86_64 */
+#endif /* TRAJNG X86 GCC INLINE MULDIV */
+
+/* Multiply two 32 bit unsigned integers returning a 64 bit unsigned value (in two integers) */
+void Ptngc_widemul(unsigned int i1, unsigned int i2, unsigned int *ohi, unsigned int *olo)
+{
+#if defined(TRAJNG_X86_GCC_INLINE_MULDIV)
+  __asm__ __volatile__ ("mull %%edx\n\t"
+                        : "=a" (i1), "=d" (i2)
+                        : "a" (i1),"d" (i2)
+                        : "cc");
+  *ohi=i2;
+  *olo=i1;
+#else /* TRAJNG X86 GCC INLINE MULDIV */
+
+#ifdef HAVE64BIT
+  my_uint64_t res= ((my_uint64_t)i1) * ((my_uint64_t)i2);
+  *olo=res & 0xFFFFFFFFU;
+  *ohi=(res>>32) & 0xFFFFFFFFU;
+#else /* HAVE64BIT */
+
+  unsigned int bits=16;
+  unsigned int L_m=(1<<bits)-1; /* Lower bits mask. */
+
+  unsigned int a_U,a_L; /* upper and lower half of a */
+  unsigned int b_U,b_L; /* upper and lower half of b */
+
+  unsigned int x_UU,x_UL,x_LU,x_LL; /* temporary storage. */
+  unsigned int x,x_U,x_L; /* temporary storage. */
+
+  /* Extract partial ints */
+  a_L=i1 & L_m;
+  a_U=i1>>bits;
+  b_L=i2 & L_m;
+  b_U=i2>>bits;
+
+  /* Do a*a=>2a multiply where a is half number of bits in an int */
+  x=a_L*b_L;
+  x_LL=x & L_m;
+  x_LU=x>>bits;
+
+  x=a_U*b_L;
+  x_LU+=x & L_m;
+  x_UL=x>>bits;
+
+  x=a_L*b_U;
+  x_LU+=x & L_m;
+  x_UL+=x>>bits;
+
+  x=a_U*b_U;
+  x_UL+=x & L_m;
+  x_UU=x>>bits;
+
+  /* Combine results */
+  x_UL+=x_LU>>bits;
+  x_LU&=L_m;
+  x_UU+=x_UL>>bits;
+  x_UL&=L_m;
+
+  x_U=(x_UU<<bits)|x_UL;
+  x_L=(x_LU<<bits)|x_LL;
+
+  /* Write back results */
+  *ohi=x_U;
+  *olo=x_L;
+#endif /* HAVE64BIT */
+#endif /* TRAJNG X86 GCC INLINE MULDIV */
+}
+
+/* Divide a 64 bit unsigned value in hi:lo with the 32 bit value i and
+   return the result in result and the remainder in remainder */
+void Ptngc_widediv(unsigned int hi, unsigned int lo, unsigned int i, unsigned int *result, unsigned int *remainder)
+{
+#if defined(TRAJNG_X86_GCC_INLINE_MULDIV)
+  __asm__ __volatile__ ("divl %%ecx\n\t"
+                        : "=a" (lo), "=d" (hi)
+                        : "a" (lo),"d" (hi), "c" (i)
+                        : "cc");
+  *result=lo;
+  *remainder=hi;
+#else /* TRAJNG X86 GCC INLINE MULDIV */
+#ifdef HAVE64BIT
+  my_uint64_t v= (((my_uint64_t)hi)<<32) | ((my_uint64_t)lo);
+  my_uint64_t res=v/((my_uint64_t)i);
+  my_uint64_t rem=v-res*((my_uint64_t)i);
+  *result=(unsigned int)res;
+  *remainder=(unsigned int)rem;
+#else /* HAVE64BIT */
+  unsigned int res;
+  unsigned int rmask;
+  unsigned int s_U,s_L;
+  unsigned int bits=16U;
+  unsigned int bits2=bits*2U;
+  unsigned int hibit=bits2-1U;
+  unsigned int hibit_mask=1U<<hibit;
+  unsigned int allbits=(hibit_mask-1U)|hibit_mask;
+
+  /* Do division. */
+  rmask=hibit_mask;
+  res=0;
+  s_U=i>>(bits2-hibit);
+  s_L=(i<<hibit)&0xFFFFFFFFU;
+  while (rmask)
+    {
+      if ((s_U<hi) || ((s_U==hi) && (s_L<=lo)))
+        {
+          /* Subtract */
+          hi-=s_U;
+          if (s_L>lo)
+            {
+              unsigned int t;
+              hi--; /* Borrow */
+              t=allbits-s_L;
+              lo+=t+1;
+            }
+          else
+            lo-=s_L;
+
+          /* Set bit. */
+          res|=rmask;
+        }
+      rmask>>=1;
+      s_L>>=1;
+      if (s_U & 1U)
+        s_L|=hibit_mask;
+      s_U>>=1;
+    }
+  *remainder=lo;
+  *result=res;
+#endif /* HAVE64BIT */
+#endif /* TRAJNG X86 GCC INLINE MULDIV */
+}
+
+/* Add a unsigned int to a largeint. j determines which value in the
+   largeint to add v1 to. */
+static void largeint_add_gen(unsigned int v1, unsigned int *largeint, int n, int j)
+{
+  /* Add with carry. unsigned ints in C wrap modulo 2**bits when "overflowed". */
+  unsigned int v2=(v1+largeint[j])&0xFFFFFFFFU; /* Add and cap at 32 bits */
+  unsigned int carry=0;
+  if ((((unsigned int)-1)&0xFFFFFFFFU) -v1<largeint[j])
+    carry=1;
+  largeint[j]=v2;
+  j++;
+  while ((j<n) && carry)
+    {
+      v2=(largeint[j]+carry)&0xFFFFFFFFU;
+      carry=0;
+      if ((((unsigned int)-1)&0xFFFFFFFFU) -1<largeint[j])
+        carry=1;
+      largeint[j]=v2;
+      j++;
+    }
+}
+
+/* Add a unsigned int to a largeint. */
+void Ptngc_largeint_add(unsigned int v1, unsigned int *largeint, int n)
+{
+  largeint_add_gen(v1,largeint,n,0);
+}
+
+/* Multiply v1 with largeint_in and return result in largeint_out */
+void Ptngc_largeint_mul(unsigned int v1, unsigned int *largeint_in, unsigned int *largeint_out, int n)
+{
+  int i;
+  for (i=0; i<n; i++)
+    largeint_out[i]=0U;
+  for (i=0; i<n; i++)
+    {
+      if (largeint_in[i]!=0U)
+        {
+          unsigned int lo,hi;
+          Ptngc_widemul(v1,largeint_in[i],&hi,&lo); /* 32x32->64 mul */
+          largeint_add_gen(lo,largeint_out,n,i);
+          if (i+1<n)
+            largeint_add_gen(hi,largeint_out,n,i+1);
+        }
+    }
+}
+
+/* Return the remainder from dividing largeint_in with v1. Result of the division is returned in largeint_out */
+unsigned int Ptngc_largeint_div(unsigned int v1, unsigned int *largeint_in, unsigned int *largeint_out, int n)
+{
+  unsigned int result,remainder=0;
+  int i;
+  unsigned int hi, lo;
+  /* Boot */
+  hi=0U;
+  i=n;
+  while (i)
+    {
+      lo=largeint_in[i-1];
+      Ptngc_widediv(hi,lo,v1,&result,&remainder);
+      largeint_out[i-1]=result;
+      hi=remainder;
+      i--;
+    }
+  return remainder;
+}
diff --git a/src/external/tng_io/src/compression/xtc2.c b/src/external/tng_io/src/compression/xtc2.c
new file mode 100644 (file)
index 0000000..bbd45e9
--- /dev/null
@@ -0,0 +1,1499 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+/* This code is heavily influenced by
+   http://hpcv100.rc.rug.nl/xdrf.html
+   Based on coordinate compression (c) by Frans van Hoesel.
+   and GROMACS xtc files (http://www.gromacs.org)
+   (c) Copyright (c) Erik Lindahl, David van der Spoel
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "../../include/compression/coder.h"
+#include "../../include/compression/widemuldiv.h"
+#include "../../include/compression/warnmalloc.h"
+
+/* Generated by gen_magic.py */
+#define MAX_MAGIC  92
+
+static unsigned int magic[MAX_MAGIC]={
+2U,  3U,  4U,  5U,
+6U,  8U,  10U,  12U,
+16U,  20U,  25U,  32U,
+40U,  50U,  64U,  80U,
+101U,  128U,  161U,  203U,
+256U,  322U,  406U,  512U,
+645U,  812U,  1024U,  1290U,
+1625U,  2048U,  2580U,  3250U,
+4096U,  5160U,  6501U,  8192U,
+10321U,  13003U,  16384U,  20642U,
+26007U,  32768U,  41285U,  52015U,
+65536U,  82570U,  104031U,  131072U,
+165140U,  208063U,  262144U,  330280U,
+416127U,  524288U,  660561U,  832255U,
+1048576U,  1321122U,  1664510U,  2097152U,
+2642245U,  3329021U,  4194304U,  5284491U,
+6658042U,  8388608U,  10568983U,  13316085U,
+16777216U,  21137967U,  26632170U,  33554432U,
+42275935U,  53264340U,  67108864U,  84551870U,
+106528681U,  134217728U,  169103740U,  213057362U,
+268435456U,  338207481U,  426114725U,  536870912U,
+676414963U,  852229450U,  1073741824U,  1352829926U,
+1704458900U,  2147483648U,  2705659852U,  3408917801U,
+};
+
+static unsigned int magic_bits[MAX_MAGIC][8]={
+{ 3,  6,  9,  12,  15,  18,  21,  24,  },
+{ 5,  10,  15,  20,  24,  29,  34,  39,  },
+{ 6,  12,  18,  24,  30,  36,  42,  48,  },
+{ 7,  14,  21,  28,  35,  42,  49,  56,  },
+{ 8,  16,  24,  32,  39,  47,  55,  63,  },
+{ 9,  18,  27,  36,  45,  54,  63,  72,  },
+{ 10,  20,  30,  40,  50,  60,  70,  80,  },
+{ 11,  22,  33,  44,  54,  65,  76,  87,  },
+{ 12,  24,  36,  48,  60,  72,  84,  97,  },
+{ 13,  26,  39,  52,  65,  78,  91,  104,  },
+{ 14,  28,  42,  56,  70,  84,  98,  112,  },
+{ 15,  30,  45,  60,  75,  90,  105,  120,  },
+{ 16,  32,  48,  64,  80,  96,  112,  128,  },
+{ 17,  34,  51,  68,  85,  102,  119,  136,  },
+{ 18,  36,  54,  72,  90,  108,  127,  144,  },
+{ 19,  38,  57,  76,  95,  114,  133,  152,  },
+{ 20,  40,  60,  80,  100,  120,  140,  160,  },
+{ 21,  42,  63,  84,  105,  127,  147,  168,  },
+{ 22,  44,  66,  88,  110,  132,  154,  176,  },
+{ 23,  46,  69,  92,  115,  138,  161,  184,  },
+{ 24,  48,  72,  97,  120,  144,  168,  192,  },
+{ 25,  50,  75,  100,  125,  150,  175,  200,  },
+{ 26,  52,  78,  104,  130,  156,  182,  208,  },
+{ 27,  54,  81,  108,  135,  162,  190,  216,  },
+{ 28,  56,  84,  112,  140,  168,  196,  224,  },
+{ 29,  58,  87,  116,  145,  174,  203,  232,  },
+{ 30,  60,  90,  120,  150,  180,  210,  240,  },
+{ 31,  62,  93,  124,  155,  186,  217,  248,  },
+{ 32,  64,  96,  128,  160,  192,  224,  256,  },
+{ 33,  66,  99,  132,  165,  198,  231,  264,  },
+{ 34,  68,  102,  136,  170,  204,  238,  272,  },
+{ 35,  70,  105,  140,  175,  210,  245,  280,  },
+{ 36,  72,  108,  144,  180,  216,  252,  288,  },
+{ 37,  74,  111,  148,  185,  222,  259,  296,  },
+{ 38,  76,  114,  152,  190,  228,  266,  304,  },
+{ 39,  78,  117,  157,  195,  234,  273,  312,  },
+{ 40,  80,  120,  160,  200,  240,  280,  320,  },
+{ 41,  82,  123,  164,  205,  246,  287,  328,  },
+{ 42,  84,  127,  168,  210,  252,  294,  336,  },
+{ 43,  86,  129,  172,  215,  258,  301,  344,  },
+{ 44,  88,  132,  176,  220,  264,  308,  352,  },
+{ 45,  90,  135,  180,  225,  270,  315,  360,  },
+{ 46,  92,  138,  184,  230,  276,  322,  368,  },
+{ 47,  94,  141,  188,  235,  282,  329,  376,  },
+{ 48,  97,  144,  192,  240,  288,  336,  384,  },
+{ 49,  98,  147,  196,  245,  294,  343,  392,  },
+{ 50,  100,  150,  200,  250,  300,  350,  400,  },
+{ 52,  102,  153,  204,  255,  306,  357,  408,  },
+{ 52,  104,  156,  208,  260,  312,  364,  416,  },
+{ 53,  106,  159,  212,  265,  318,  371,  424,  },
+{ 54,  108,  162,  216,  270,  324,  378,  432,  },
+{ 55,  110,  165,  220,  275,  330,  385,  440,  },
+{ 56,  112,  168,  224,  280,  336,  392,  448,  },
+{ 57,  114,  172,  228,  285,  342,  399,  456,  },
+{ 58,  116,  174,  232,  290,  348,  406,  464,  },
+{ 59,  118,  177,  236,  295,  354,  413,  472,  },
+{ 60,  120,  180,  240,  300,  360,  420,  480,  },
+{ 61,  122,  183,  244,  305,  366,  427,  488,  },
+{ 62,  124,  186,  248,  310,  372,  434,  496,  },
+{ 63,  127,  190,  252,  315,  378,  442,  505,  },
+{ 64,  128,  192,  256,  320,  384,  448,  512,  },
+{ 65,  130,  195,  260,  325,  390,  455,  520,  },
+{ 66,  132,  198,  264,  330,  396,  462,  528,  },
+{ 67,  134,  201,  268,  335,  402,  469,  536,  },
+{ 68,  136,  204,  272,  340,  408,  476,  544,  },
+{ 69,  138,  207,  276,  345,  414,  483,  552,  },
+{ 70,  140,  210,  280,  350,  420,  490,  560,  },
+{ 71,  142,  213,  284,  355,  426,  497,  568,  },
+{ 72,  144,  216,  288,  360,  432,  505,  576,  },
+{ 73,  146,  219,  292,  365,  438,  511,  584,  },
+{ 74,  148,  222,  296,  370,  444,  518,  592,  },
+{ 75,  150,  225,  300,  375,  451,  525,  600,  },
+{ 76,  152,  228,  304,  380,  456,  532,  608,  },
+{ 77,  154,  231,  308,  385,  462,  539,  616,  },
+{ 78,  157,  234,  312,  390,  469,  546,  625,  },
+{ 79,  158,  237,  316,  395,  474,  553,  632,  },
+{ 80,  160,  240,  320,  400,  480,  560,  640,  },
+{ 81,  162,  243,  324,  406,  486,  568,  648,  },
+{ 82,  164,  246,  328,  410,  492,  574,  656,  },
+{ 83,  166,  249,  332,  415,  498,  581,  664,  },
+{ 84,  168,  252,  336,  420,  505,  588,  672,  },
+{ 85,  170,  255,  340,  425,  510,  595,  680,  },
+{ 86,  172,  258,  344,  430,  516,  602,  688,  },
+{ 87,  174,  261,  348,  435,  522,  609,  696,  },
+{ 88,  176,  264,  352,  440,  528,  616,  704,  },
+{ 89,  178,  267,  356,  445,  534,  623,  712,  },
+{ 90,  180,  270,  360,  451,  540,  631,  720,  },
+{ 91,  182,  273,  364,  455,  546,  637,  728,  },
+{ 92,  184,  276,  368,  460,  552,  644,  736,  },
+{ 94,  187,  279,  373,  466,  558,  651,  745,  },
+{ 94,  188,  282,  376,  470,  564,  658,  752,  },
+{ 95,  190,  285,  380,  475,  570,  665,  760,  },
+};
+
+
+static const double iflipgaincheck=0.89089871814033927; /*  1./(2**(1./6)) */
+
+
+/* Difference in indices used for determining whether to store as large or small */
+#define QUITE_LARGE 3
+#define IS_LARGE 6
+
+#if 0
+#define SHOWIT
+#endif
+
+int Ptngc_magic(unsigned int i)
+{
+  return magic[i];
+}
+
+int Ptngc_find_magic_index(unsigned int maxval)
+{
+  int i=0;
+  while (magic[i]<=maxval)
+    i++;
+  return i;
+}
+
+static unsigned int positive_int(int item)
+{
+  int s=0;
+  if (item>0)
+    s=1+(item-1)*2;
+  else if (item<0)
+    s=2+(-item-1)*2;
+  return s;
+}
+
+static int unpositive_int(int val)
+{
+  int s=(val+1)/2;
+  if ((val%2)==0)
+    s=-s;
+  return s;
+}
+
+
+/* Sequence instructions */
+#define INSTR_DEFAULT 0
+#define INSTR_BASE_RUNLENGTH 1
+#define INSTR_ONLY_LARGE 2
+#define INSTR_ONLY_SMALL 3
+#define INSTR_LARGE_BASE_CHANGE 4
+#define INSTR_FLIP 5
+#define INSTR_LARGE_RLE 6
+
+#define MAXINSTR 7
+
+#ifdef SHOWIT
+static char *instrnames[MAXINSTR]={
+  "large+small",
+  "base+run",
+  "large",
+  "small",
+  "large base change",
+  "flip",
+  "large rle",
+};
+#endif
+
+/* Bit patterns in the compressed code stream: */
+
+static const int seq_instr[MAXINSTR][2]=
+  {
+    { 1,1 }, /* 1 : one large atom + runlength encoded small integers. Use same settings as before. */
+    { 0,2 }, /* 00 : set base and runlength in next four bits (x). base (increase/keep/decrease)=x%3-1. runlength=1+x/3.
+                      The special value 1111 in the four bits means runlength=6 and base change=0 */
+    { 4,4 }, /* 0100 : next only a large atom comes. */
+    { 5,4 }, /* 0101 : next only runlength encoded small integers. Use same settings as before. */
+    { 6,4 }, /* 0110 : Large change in base. Change is encoded in the
+                following 2 bits. change direction (sign) is the first
+                bit. The next bit +1 is the actual change. This
+                allows the change of up to +/- 2 indices. */
+    { 14,5 }, /* 01110 : flip whether integers should be modified to compress water better */
+    { 15,5 }, /* 01111 : Large rle. The next 4 bits encode how many
+               large atoms are in the following sequence: 3-18. (2 is
+               more efficiently coded with two large instructions. */
+ };
+
+static void write_instruction(struct coder *coder,int instr,unsigned char **output_ptr)
+{
+  Ptngc_writebits(coder,seq_instr[instr][0],seq_instr[instr][1],output_ptr);
+#ifdef SHOWIT
+  fprintf(stderr,"INSTR: %s (%d bits)\n",instrnames[instr],seq_instr[instr][1]);
+#endif
+}
+
+static unsigned int readbits(unsigned char **ptr, int *bitptr, int nbits)
+{
+  unsigned int val=0U;
+  unsigned int extract_mask=0x80U>>*bitptr;
+  unsigned char thisval=**ptr;
+#ifdef SHOWIT
+  fprintf(stderr,"Read nbits=%d val=",nbits);
+#endif
+  while (nbits--)
+    {
+      val<<=1;
+      val|=((extract_mask & thisval)!=0);
+      *bitptr=(*bitptr)+1;
+      extract_mask>>=1;
+      if (!extract_mask)
+        {
+          extract_mask=0x80U;
+          *ptr=(*ptr)+1;
+          *bitptr=0;
+          if (nbits)
+            thisval=**ptr;
+        }
+    }
+#ifdef SHOWIT
+  fprintf(stderr,"%x\n",val);
+#endif
+  return val;
+}
+
+static void readmanybits(unsigned char **ptr, int *bitptr, int nbits, unsigned char *buffer)
+{
+  while (nbits>=8)
+    {
+      *buffer++=readbits(ptr,bitptr,8);
+      nbits-=8;
+#ifdef SHOWIT
+      fprintf(stderr,"Read value %02x\n",buffer[-1]);
+#endif
+    }
+  if (nbits)
+    {
+      *buffer++=readbits(ptr,bitptr,nbits);
+#ifdef SHOWIT
+      fprintf(stderr,"Read value %02x\n",buffer[-1]);
+#endif
+    }
+}
+
+static int read_instruction(unsigned char **ptr, int *bitptr)
+{
+  int instr=-1;
+  unsigned int bits=readbits(ptr,bitptr,1);
+  if (bits)
+    instr=INSTR_DEFAULT;
+  else
+    {
+      bits=readbits(ptr,bitptr,1);
+      if (!bits)
+        instr=INSTR_BASE_RUNLENGTH;
+      else
+        {
+          bits=readbits(ptr,bitptr,2);
+          if (bits==0)
+            instr=INSTR_ONLY_LARGE;
+          else if (bits==1)
+            instr=INSTR_ONLY_SMALL;
+          else if (bits==2)
+            instr=INSTR_LARGE_BASE_CHANGE;
+          else if (bits==3)
+            {
+              bits=readbits(ptr,bitptr,1);
+              if (bits==0)
+                instr=INSTR_FLIP;
+              else
+                instr=INSTR_LARGE_RLE;
+            }
+        }
+    }
+  return instr;
+}
+
+/* Modifies three integer values for better compression of water */
+static void swap_ints(int *in, int *out)
+{
+  out[0]=in[0]+in[1];
+  out[1]=-in[1];
+  out[2]=in[1]+in[2];
+}
+
+static void swap_is_better(int *input, int *minint, int *sum_normal, int *sum_swapped)
+{
+  int normal_max=0;
+  int swapped_max=0;
+  int i,j;
+  int normal[3];
+  int swapped[3];
+  for (i=0; i<3; i++)
+    {
+      normal[0]=input[i]-minint[i];
+      normal[1]=input[3+i]-input[i]; /* minint[i]-minint[i] cancels out */
+      normal[2]=input[6+i]-input[3+i]; /* minint[i]-minint[i] cancels out */
+      swap_ints(normal,swapped);
+      for (j=1; j<3; j++)
+        {
+          if (positive_int(normal[j])>(unsigned int)normal_max)
+            normal_max=positive_int(normal[j]);
+          if (positive_int(swapped[j])>(unsigned int)swapped_max)
+            swapped_max=positive_int(swapped[j]);
+        }
+    }
+  if (normal_max==0)
+    normal_max=1;
+  if (swapped_max==0)
+    swapped_max=1;
+  *sum_normal=normal_max;
+  *sum_swapped=swapped_max;
+}
+
+static void swapdecide(struct coder *coder, int *input,int *swapatoms, int *large_index, int *minint, unsigned char **output_ptr)
+{
+  int didswap=0;
+  int normal,swapped;
+  (void)large_index;
+  swap_is_better(input,minint,&normal,&swapped);
+  /* We have to determine if it is worth to change the behaviour.
+     If diff is positive it means that it is worth something to
+     swap. But it costs 4 bits to do the change. If we assume that
+     we gain 0.17 bit by the swap per value, and the runlength>2
+     for four molecules in a row, we gain something. So check if we
+     gain at least 0.17 bits to even attempt the swap.
+  */
+#ifdef SHOWIT
+  fprintf(stderr,"Trying Flip: %g %g\n",(double)swapped/normal, (double)normal/swapped);
+#endif
+  if (((swapped<normal) && (fabs((double)swapped/normal)<iflipgaincheck)) ||
+      ((normal<swapped) && (fabs((double)normal/swapped)<iflipgaincheck)))
+    {
+      if (swapped<normal)
+        {
+          if (!*swapatoms)
+            {
+              *swapatoms=1;
+              didswap=1;
+            }
+        }
+      else
+        {
+          if (*swapatoms)
+            {
+              *swapatoms=0;
+              didswap=1;
+            }
+        }
+    }
+  if (didswap)
+    {
+#ifdef SHOWIT
+      fprintf(stderr,"Flip. Swapatoms is now %d\n",*swapatoms);
+#endif
+      write_instruction(coder,INSTR_FLIP,output_ptr);
+    }
+}
+
+/* Compute number of bits required to store values using three different bases in the index array */
+static int compute_magic_bits(int *index)
+{
+  unsigned int largeint[4];
+  unsigned int largeint_tmp[4];
+  int i,j,onebit;
+  for (i=0; i<4; i++)
+    largeint[i]=0U;
+  for (i=0; i<3; i++)
+    {
+      if (i!=0)
+        {
+          /* We must do the multiplication of the largeint with the integer base */
+          Ptngc_largeint_mul(magic[index[i]],largeint,largeint_tmp,4);
+          for (j=0; j<4; j++)
+            largeint[j]=largeint_tmp[j];
+        }
+      Ptngc_largeint_add(magic[index[i]]-1,largeint,4);
+    }
+  /* Find last bit. */
+#if 0
+  printf("Largeint is %u %u %u\n",largeint[0],largeint[1],largeint[2]);
+#endif
+  onebit=0;
+  for (i=0; i<3; i++)
+    for (j=0; j<32; j++)
+      if (largeint[i]&(1U<<j))
+        onebit=i*32+j+1;
+  return onebit;
+}
+
+/* Convert a sequence of (hopefully) small positive integers
+   using the base pointed to by index (x base, y base and z base can be different).
+   The largest number of integers supported is 18 (29 to handle/detect overflow) */
+static void trajcoder_base_compress(int *input, int n, int *index, unsigned char *result)
+{
+  unsigned int largeint[19];
+  unsigned int largeint_tmp[19];
+  int i,j;
+  for (i=0; i<19; i++)
+    largeint[i]=0U;
+
+  for (i=0; i<n; i++)
+    {
+      if (i!=0)
+        {
+          /* We must do the multiplication of the largeint with the integer base */
+          Ptngc_largeint_mul(magic[index[i%3]],largeint,largeint_tmp,19);
+          for (j=0; j<19; j++)
+            largeint[j]=largeint_tmp[j];
+        }
+      Ptngc_largeint_add(input[i],largeint,19);
+    }
+  if (largeint[18])
+    {
+      fprintf(stderr,"TRAJNG: BUG! Overflow in compression detected.\n");
+      exit(EXIT_FAILURE);
+    }
+#if 0
+#ifdef SHOWIT
+  for (i=0; i<19; i++)
+    fprintf(stderr,"Largeint[%d]=0x%x\n",i,largeint[i]);
+#endif
+#endif
+  /* Convert the largeint to a sequence of bytes. */
+  for (i=0; i<18; i++)
+    {
+      int shift=0;
+      for (j=0; j<4; j++)
+        {
+          result[i*4+j]=(largeint[i]>>shift) & 0xFFU;
+          shift+=8;
+        }
+    }
+}
+
+/* The opposite of base_compress. */
+static void trajcoder_base_decompress(unsigned char *input, int n, int *index, int *output)
+{
+  unsigned int largeint[19];
+  unsigned int largeint_tmp[19];
+  int i,j;
+  /* Convert the sequence of bytes to a largeint. */
+  for (i=0; i<18; i++)
+    {
+      int shift=0;
+      largeint[i]=0U;
+      for (j=0; j<4; j++)
+        {
+          largeint[i]|=((unsigned int)input[i*4+j])<<shift;
+          shift+=8;
+        }
+    }
+  largeint[18]=0U;
+#if 0
+#ifdef SHOWIT
+  for (i=0; i<19; i++)
+    fprintf(stderr,"Largeint[%d]=0x%x\n",i,largeint[i]);
+#endif
+#endif
+  for (i=n-1; i>=0; i--)
+    {
+      unsigned int remainder=Ptngc_largeint_div(magic[index[i%3]],largeint,largeint_tmp,19);
+#if 0
+#ifdef SHOWIT
+      fprintf(stderr,"Remainder: %u\n",remainder);
+#endif
+#endif
+#if 0
+      for (j=0; j<19; j++)
+        largeint[j]=largeint_tmp[j];
+#endif
+      memcpy(largeint,largeint_tmp,19*sizeof *largeint);
+      output[i]=remainder;
+    }
+}
+
+/* It is "large" if we have to increase the small index quite a
+   bit. Not so much to be rejected by the not very large check
+   later. */
+static int is_quite_large(int *input, int small_index, int max_large_index)
+{
+  int is=0;
+  int i;
+  if (small_index+QUITE_LARGE>=max_large_index)
+    is=1;
+  else
+    {
+      for (i=0; i<3; i++)
+        if (positive_int(input[i])>magic[small_index+QUITE_LARGE])
+          {
+            is=1;
+            break;
+          }
+    }
+  return is;
+}
+
+#ifdef SHOWIT
+int nbits_sum;
+int nvalues_sum;
+#endif
+
+static void write_three_large(struct coder *coder, int *encode_ints, int *large_index, int nbits, unsigned char *compress_buffer, unsigned char **output_ptr)
+{
+  trajcoder_base_compress(encode_ints,3,large_index,compress_buffer);
+  Ptngc_writemanybits(coder,compress_buffer,nbits,output_ptr);
+#ifdef SHOWIT
+  fprintf(stderr,"nbits=%d (%g)\n",nbits,nbits/3.);
+  nbits_sum+=nbits;
+  nvalues_sum+=3;
+  fprintf(stderr,"Large: %d %d %d\n",encode_ints[0],encode_ints[1],encode_ints[2]);
+#endif
+}
+
+static void insert_batch(int *input_ptr, int ntriplets_left, int *prevcoord,int *minint, int *encode_ints, int startenc, int *nenc)
+{
+  int nencode=startenc*3;
+  int tmp_prevcoord[3];
+
+  tmp_prevcoord[0]=prevcoord[0];
+  tmp_prevcoord[1]=prevcoord[1];
+  tmp_prevcoord[2]=prevcoord[2];
+
+  if (startenc)
+    {
+      int i;
+      for (i=0; i<startenc; i++)
+        {
+          tmp_prevcoord[0]+=encode_ints[i*3];
+          tmp_prevcoord[1]+=encode_ints[i*3+1];
+          tmp_prevcoord[2]+=encode_ints[i*3+2];
+#ifdef SHOWIT
+          fprintf(stderr,"%6d: %6d %6d %6d\t\t%6d %6d %6d\t\t%6d %6d %6d\n",i*3,
+                  tmp_prevcoord[0],tmp_prevcoord[1],tmp_prevcoord[2],
+                  encode_ints[i*3],
+                  encode_ints[i*3+1],
+                  encode_ints[i*3+2],
+                  positive_int(encode_ints[i*3]),
+                  positive_int(encode_ints[i*3+1]),
+                  positive_int(encode_ints[i*3+2]));
+#endif
+        }
+    }
+
+#ifdef SHOWIT
+  fprintf(stderr,"New batch\n");
+#endif
+  while ((nencode<21) && (nencode<ntriplets_left*3))
+    {
+      encode_ints[nencode]=input_ptr[nencode]-minint[0]-tmp_prevcoord[0];
+      encode_ints[nencode+1]=input_ptr[nencode+1]-minint[1]-tmp_prevcoord[1];
+      encode_ints[nencode+2]=input_ptr[nencode+2]-minint[2]-tmp_prevcoord[2];
+#ifdef SHOWIT
+      fprintf(stderr,"%6d: %6d %6d %6d\t\t%6d %6d %6d\t\t%6d %6d %6d\n",nencode,
+              input_ptr[nencode]-minint[0],
+              input_ptr[nencode+1]-minint[1],
+              input_ptr[nencode+2]-minint[2],
+              encode_ints[nencode],
+              encode_ints[nencode+1],
+              encode_ints[nencode+2],
+              positive_int(encode_ints[nencode]),
+              positive_int(encode_ints[nencode+1]),
+              positive_int(encode_ints[nencode+2]));
+#endif
+      tmp_prevcoord[0]=input_ptr[nencode]-minint[0];
+      tmp_prevcoord[1]=input_ptr[nencode+1]-minint[1];
+      tmp_prevcoord[2]=input_ptr[nencode+2]-minint[2];
+      nencode+=3;
+    }
+  *nenc=nencode;
+}
+
+static void flush_large(struct coder *coder, int *has_large, int *has_large_ints, int n,
+                        int *large_index, int large_nbits, unsigned char *compress_buffer,
+                        unsigned char **output_ptr)
+{
+  int i;
+  if (n<3)
+    {
+      for (i=0; i<n; i++)
+        {
+          write_instruction(coder,INSTR_ONLY_LARGE,output_ptr);
+          write_three_large(coder,has_large_ints+i*3,large_index,large_nbits,compress_buffer,output_ptr);
+        }
+    }
+  else
+    {
+#if 0
+      printf("CODING RLE: %d\n",n);
+#endif
+      write_instruction(coder,INSTR_LARGE_RLE,output_ptr);
+      Ptngc_writebits(coder,n-3,4,output_ptr); /* Number of large atoms in this sequence: 3 to 18 */
+      for (i=0; i<n; i++)
+        write_three_large(coder,has_large_ints+i*3,large_index,large_nbits,compress_buffer,output_ptr);
+    }
+  if (((*has_large)-n)!=0)
+    {
+      int j;
+      for (i=0; i<(*has_large)-n; i++)
+        for (j=0; j<3; j++)
+          has_large_ints[i*3+j]=has_large_ints[(i+n)*3+j];
+    }
+  *has_large=(*has_large)-n; /* Number of remaining large atoms in buffer */
+}
+
+static void buffer_large(struct coder *coder, int *has_large, int *has_large_ints, int *new_large_ints,
+                        int *large_index, int large_nbits, unsigned char *compress_buffer,
+                        unsigned char **output_ptr)
+{
+  /* If it is full we must write them all. */
+  if (*has_large==18)
+    flush_large(coder,has_large,has_large_ints, *has_large,
+                large_index,large_nbits,compress_buffer,output_ptr); /* Flush them all. */
+  has_large_ints[(*has_large)*3]=new_large_ints[0];
+  has_large_ints[(*has_large)*3+1]=new_large_ints[1];
+  has_large_ints[(*has_large)*3+2]=new_large_ints[2];
+  *has_large=(*has_large)+1;
+}
+
+
+unsigned char *Ptngc_pack_array_xtc2(struct coder *coder,int *input, int *length)
+{
+  unsigned char *output=NULL;
+  unsigned char *output_ptr=NULL;
+  int i,ienc,j;
+  int output_length=0;
+  /* Pack triplets. */
+  int ntriplets=*length/3;
+  int intmax;
+  int max_small;
+  int large_index[3];
+  int max_large_index;
+  int large_nbits;
+  int small_index;
+  int small_idx[3];
+  int minint[3],maxint[3];
+  int has_large=0;
+  int has_large_ints[54]; /* Large cache. Up to 18 large atoms. */
+  int prevcoord[3];
+  int runlength=0; /* Initial runlength. "Stupidly" set to zero for
+                      simplicity and explicity */
+  int swapatoms=0; /* Initial guess is that we should not swap the
+                      first two atoms in each large+small
+                      transition */
+  int didswap; /* Whether swapping was actually done. */
+  int *input_ptr=input;
+  int encode_ints[21]; /* Up to 3 large + 18 small ints can be encoded at once */
+  int nencode;
+  unsigned char compress_buffer[18*4]; /* Holds compressed result for 3 large ints or up to 18 small ints. */
+  int ntriplets_left=ntriplets;
+  int refused=0;
+#ifdef SHOWIT
+  nbits_sum=0;
+  nvalues_sum=0;
+#endif
+  /* Allocate enough memory for output */
+  output=warnmalloc(8* *length*sizeof *output);
+  output_ptr=output;
+
+  maxint[0]=minint[0]=input[0];
+  maxint[1]=minint[1]=input[1];
+  maxint[2]=minint[2]=input[2];
+
+  for (i=1; i<ntriplets; i++)
+    for (j=0; j<3; j++)
+      {
+        if (input[i*3+j]>maxint[j])
+          maxint[j]=input[i*3+j];
+        if (input[i*3+j]<minint[j])
+          minint[j]=input[i*3+j];
+      }
+
+  large_index[0]=Ptngc_find_magic_index(maxint[0]-minint[0]+1);
+  large_index[1]=Ptngc_find_magic_index(maxint[1]-minint[1]+1);
+  large_index[2]=Ptngc_find_magic_index(maxint[2]-minint[2]+1);
+  large_nbits=compute_magic_bits(large_index);
+  max_large_index=large_index[0];
+  if (large_index[1]>max_large_index)
+    max_large_index=large_index[1];
+  if (large_index[2]>max_large_index)
+    max_large_index=large_index[2];
+
+#ifdef SHOWIT
+  for (j=0; j<3; j++)
+    fprintf(stderr,"minint[%d]=%d. maxint[%d]=%d large_index[%d]=%d value=%d\n",j,minint[j],j,maxint[j],
+            j,large_index[j],magic[large_index[j]]);
+  fprintf(stderr,"large_nbits=%d\n",large_nbits);
+#endif
+
+
+  /* Guess initial small index */
+  small_index=max_large_index/2;
+
+  /* Find the largest value that is not large. Not large is half index of
+     large. */
+  max_small=magic[small_index];
+  intmax=0;
+  for (i=0; i<*length; i++)
+    {
+      int item=input[i];
+      int s=positive_int(item);
+      if (s>intmax)
+        if (s<max_small)
+          intmax=s;
+    }
+  /* This value is not critical, since if I guess wrong, the code will
+     just insert instructions to increase this value. */
+  small_index=Ptngc_find_magic_index(intmax);
+#ifdef SHOWIT
+  fprintf(stderr,"initial_small intmax=%d. small_index=%d value=%d\n",intmax,small_index,magic[small_index]);
+#endif
+
+  /* Store min integers */
+  coder->pack_temporary_bits=32;
+  coder->pack_temporary=positive_int(minint[0]);
+  Ptngc_out8bits(coder,&output_ptr);
+  coder->pack_temporary_bits=32;
+  coder->pack_temporary=positive_int(minint[1]);
+  Ptngc_out8bits(coder,&output_ptr);
+  coder->pack_temporary_bits=32;
+  coder->pack_temporary=positive_int(minint[2]);
+  Ptngc_out8bits(coder,&output_ptr);
+  /* Store max indices */
+  coder->pack_temporary_bits=8;
+  coder->pack_temporary=large_index[0];
+  Ptngc_out8bits(coder,&output_ptr);
+  coder->pack_temporary_bits=8;
+  coder->pack_temporary=large_index[1];
+  Ptngc_out8bits(coder,&output_ptr);
+  coder->pack_temporary_bits=8;
+  coder->pack_temporary=large_index[2];
+  Ptngc_out8bits(coder,&output_ptr);
+  /* Store initial small index */
+  coder->pack_temporary_bits=8;
+  coder->pack_temporary=small_index;
+  Ptngc_out8bits(coder,&output_ptr);
+
+#if 0
+#ifdef SHOWIT
+  for (i=0; i<ntriplets_left; i++)
+    fprintf(stderr,"VALUE:%d %6d %6d %6d\n",
+            i,
+            input_ptr[i*3],
+            input_ptr[i*3+1],
+            input_ptr[i*3+2]);
+#endif
+#endif
+
+  /* Initial prevcoord is the minimum integers. */
+  prevcoord[0]=minint[0];
+  prevcoord[1]=minint[1];
+  prevcoord[2]=minint[2];
+
+  while (ntriplets_left)
+    {
+      if (ntriplets_left<0)
+        {
+          fprintf(stderr,"TRAJNG: BUG! ntriplets_left<0!\n");
+          exit(EXIT_FAILURE);
+        }
+      /* If only less than three atoms left we just write them all as large integers. Here no swapping is done! */
+      if (ntriplets_left<3)
+        {
+          for (ienc=0; ienc<ntriplets_left; ienc++)
+            {
+              int jenc;
+              for (jenc=0; jenc<3; jenc++)
+                encode_ints[jenc]=input_ptr[ienc*3+jenc]-minint[jenc];
+              buffer_large(coder,&has_large,has_large_ints,encode_ints,large_index,large_nbits,compress_buffer,&output_ptr);
+              input_ptr+=3;
+              ntriplets_left--;
+            }
+          flush_large(coder,&has_large,has_large_ints,has_large,large_index,large_nbits,compress_buffer,&output_ptr);
+        }
+      else
+        {
+          int min_runlength=0;
+          int largest_required_base;
+          int largest_runlength_base;
+          int largest_required_index;
+          int largest_runlength_index;
+          int new_runlength;
+          int new_small_index;
+          int iter_runlength;
+          int iter_small_index;
+          int rle_index_dep;
+          didswap=0;
+          /* Insert the next batch of integers to be encoded into the buffer */
+#ifdef SHOWIT
+          fprintf(stderr,"Initial batch\n");
+#endif
+          insert_batch(input_ptr,ntriplets_left,prevcoord,minint,encode_ints,0,&nencode);
+
+          /* First we must decide if the next value is large (does not reasonably fit in current small encoding)
+             Also, if we have not written any values yet, we must begin by writing a large atom. */
+          if ((input_ptr==input) || (is_quite_large(encode_ints,small_index,max_large_index)) || (refused))
+            {
+              /* If any of the next two atoms are large we should probably write them as large and not swap them */
+              int no_swap=0;
+              if ((is_quite_large(encode_ints+3,small_index,max_large_index)) || (is_quite_large(encode_ints+6,small_index,max_large_index)))
+                no_swap=1;
+              if (!no_swap)
+                {
+                  /* Next we must decide if we should swap the first
+                     two values. */
+#if 1
+                  swapdecide(coder,input_ptr,&swapatoms,large_index,minint,&output_ptr);
+#else
+                  swapatoms=0;
+#endif
+                  /* If we should do the integer swapping manipulation we should do it now */
+                  if (swapatoms)
+                    {
+                      didswap=1;
+                      for (i=0; i<3; i++)
+                        {
+                          int in[3], out[3];
+                          in[0]=input_ptr[i]-minint[i];
+                          in[1]=input_ptr[3+i]-input_ptr[i]; /* minint[i]-minint[i] cancels out */
+                          in[2]=input_ptr[6+i]-input_ptr[3+i]; /* minint[i]-minint[i] cancels out */
+                          swap_ints(in,out);
+                          encode_ints[i]=out[0];
+                          encode_ints[3+i]=out[1];
+                          encode_ints[6+i]=out[2];
+                        }
+                      /* We have swapped atoms, so the minimum run-length is 2 */
+#ifdef SHOWIT
+                      fprintf(stderr,"Swap atoms results in:\n");
+                      for (i=0; i<3; i++)
+                        fprintf(stderr,"%d: %6d %6d %6d\t\t%6d %6d %6d\n",i*3,
+                                encode_ints[i*3],
+                                encode_ints[i*3+1],
+                                encode_ints[i*3+2],
+                                positive_int(encode_ints[i*3]),
+                                positive_int(encode_ints[i*3+1]),
+                                positive_int(encode_ints[i*3+2]));
+
+#endif
+                      min_runlength=2;
+                    }
+                }
+              if ((swapatoms) && (didswap))
+                {
+                  for (ienc=0; ienc<3; ienc++)
+                    prevcoord[ienc]=encode_ints[ienc];
+                }
+              else
+                {
+                  for (ienc=0; ienc<3; ienc++)
+                    prevcoord[ienc]=input_ptr[ienc]-minint[ienc];
+                }
+              /* Cache large value for later possible combination with
+                 a sequence of small integers. */
+              buffer_large(coder,&has_large,has_large_ints,prevcoord,large_index,large_nbits,compress_buffer,&output_ptr);
+#ifdef SHOWIT
+              fprintf(stderr,"Prevcoord after packing of large: %d %d %d\n",
+                      prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+
+              /* We have written a large integer so we have one less atoms to worry about */
+              input_ptr+=3;
+              ntriplets_left--;
+
+              refused=0;
+
+              /* Insert the next batch of integers to be encoded into the buffer */
+#ifdef SHOWIT
+              fprintf(stderr,"Update batch due to large int.\n");
+#endif
+              if ((swapatoms) && (didswap))
+                {
+                  /* Keep swapped values. */
+                  for (i=0; i<2; i++)
+                    for (ienc=0; ienc<3; ienc++)
+                      encode_ints[i*3+ienc]=encode_ints[(i+1)*3+ienc];
+                }
+              insert_batch(input_ptr,ntriplets_left,prevcoord,minint,encode_ints,min_runlength,&nencode);
+            }
+          /* Here we should only have differences for the atom coordinates. */
+          /* Convert the ints to positive ints */
+          for (ienc=0; ienc<nencode; ienc++)
+            {
+              int pint=positive_int(encode_ints[ienc]);
+              encode_ints[ienc]=pint;
+            }
+          /* Now we must decide what base and runlength to do. If we have swapped atoms it will be at least 2.
+             If even the next atom is large, we will not do anything. */
+          largest_required_base=0;
+          /* Determine required base */
+          for (ienc=0; ienc<min_runlength*3; ienc++)
+            if (encode_ints[ienc]>largest_required_base)
+              largest_required_base=encode_ints[ienc];
+          /* Also compute what the largest base is for the current runlength setting! */
+          largest_runlength_base=0;
+          for (ienc=0; (ienc<runlength*3) && (ienc<nencode); ienc++)
+            if (encode_ints[ienc]>largest_runlength_base)
+              largest_runlength_base=encode_ints[ienc];
+
+          largest_required_index=Ptngc_find_magic_index(largest_required_base);
+          largest_runlength_index=Ptngc_find_magic_index(largest_runlength_base);
+
+          if (largest_required_index<largest_runlength_index)
+            {
+              new_runlength=min_runlength;
+              new_small_index=largest_required_index;
+            }
+          else
+            {
+              new_runlength=runlength;
+              new_small_index=largest_runlength_index;
+            }
+
+          /* Only allow increase of runlength wrt min_runlength */
+          if (new_runlength<min_runlength)
+            new_runlength=min_runlength;
+
+          /* If the current runlength is longer than the number of
+             triplets left stop it from being so. */
+          if (new_runlength>ntriplets_left)
+            new_runlength=ntriplets_left;
+
+          /* We must at least try to get some small integers going. */
+          if (new_runlength==0)
+            {
+              new_runlength=1;
+              new_small_index=small_index;
+            }
+
+          iter_runlength=new_runlength;
+          iter_small_index=new_small_index;
+
+          /* Iterate to find optimal encoding and runlength */
+#ifdef SHOWIT
+          fprintf(stderr,"Entering iterative loop.\n");
+          fflush(stderr);
+#endif
+
+          do {
+            new_runlength=iter_runlength;
+            new_small_index=iter_small_index;
+
+#ifdef SHOWIT
+            fprintf(stderr,"Test new_small_index=%d Base=%d\n",new_small_index,magic[new_small_index]);
+#endif
+            /* What is the largest runlength
+               we can do with the currently
+               selected encoding? Also the max supported runlength is 6 triplets! */
+            for (ienc=0; ienc<nencode && ienc<18; ienc++)
+              {
+                int test_index=Ptngc_find_magic_index(encode_ints[ienc]);
+                if (test_index>new_small_index)
+                  break;
+              }
+            if (ienc/3>new_runlength)
+              {
+                iter_runlength=ienc/3;
+#ifdef SHOWIT
+                fprintf(stderr,"I found a new possible runlength: %d\n",iter_runlength);
+#endif
+              }
+
+            /* How large encoding do we have to use? */
+            largest_runlength_base=0;
+            for (ienc=0; ienc<iter_runlength*3; ienc++)
+              if (encode_ints[ienc]>largest_runlength_base)
+                largest_runlength_base=encode_ints[ienc];
+            largest_runlength_index=Ptngc_find_magic_index(largest_runlength_base);
+            if (largest_runlength_index!=new_small_index)
+              {
+                iter_small_index=largest_runlength_index;
+#ifdef SHOWIT
+                fprintf(stderr,"I found a new possible small index: %d Base=%d\n",iter_small_index,magic[iter_small_index]);
+#endif
+              }
+          } while ((new_runlength!=iter_runlength) ||
+                   (new_small_index!=iter_small_index));
+
+#ifdef SHOWIT
+          fprintf(stderr,"Exit iterative loop.\n");
+          fflush(stderr);
+#endif
+
+          /* Verify that we got something good. We may have caught a
+             substantially larger atom. If so we should just bail
+             out and let the loop get on another lap. We may have a
+             minimum runlength though and then we have to fulfill
+             the request to write out these atoms! */
+          rle_index_dep=0;
+          if (new_runlength<3)
+            rle_index_dep=IS_LARGE;
+          else if (new_runlength<6)
+            rle_index_dep=QUITE_LARGE;
+          if ((min_runlength)
+              || ((new_small_index<small_index+IS_LARGE) && (new_small_index+rle_index_dep<max_large_index))
+#if 1
+              || (new_small_index+IS_LARGE<max_large_index)
+#endif
+)
+            {
+              int nbits;
+              if ((new_runlength!=runlength) || (new_small_index!=small_index))
+                {
+                  int change=new_small_index-small_index;
+
+                  if (new_small_index<=0)
+                    change=0;
+
+                  if (change<0)
+                    {
+                      int ixx;
+                      for (ixx=0; ixx<new_runlength; ixx++)
+                        {
+                          int rejected;
+                          do {
+                            int ixyz;
+                            double isum=0.; /* ints can be almost 32 bit so multiplication will overflow. So do doubles. */
+                            for (ixyz=0; ixyz<3; ixyz++)
+                              {
+                                /* encode_ints is already positive (and multiplied by 2 versus the original, just as magic ints) */
+                                double id=encode_ints[ixx*3+ixyz];
+                                isum+=id*id;
+                              }
+                            rejected=0;
+#ifdef SHOWIT
+                            fprintf(stderr,"Tested decrease %d of index: %g>=%g?\n",change,isum,(double)magic[small_index+change]*(double)magic[small_index+change]);
+#endif
+                            if (isum>(double)magic[small_index+change]*(double)magic[small_index+change])
+                              {
+#ifdef SHOWIT
+                                fprintf(stderr,"Rejected decrease %d of index due to length of vector: %g>=%g\n",change,isum,(double)magic[small_index+change]*(double)magic[small_index+change]);
+#endif
+                                rejected=1;
+                                change++;
+                              }
+                          } while ((change<0) && (rejected));
+                          if (change==0)
+                            break;
+                        }
+                    }
+
+                  /* If the only thing to do is to change the base by
+                     only one -1 it is probably not worth it. */
+                  if (!((change==-1) && (runlength==new_runlength)))
+                    {
+                      /* If we have a very short runlength we do not
+                         want to do large base changes.  It costs 6
+                         extra bits to do -2. We gain 2/3
+                         bits per value to decrease the index by -2,
+                         ie 2 bits, so to any changes down we must
+                         have a runlength of 3 or more to do it for
+                         one molecule! If we have several molecules we
+                         will gain of course, so not be so strict. */
+                      if ((change==-2) && (new_runlength<3))
+                        {
+                          if (runlength==new_runlength)
+                            change=0;
+                          else
+                            change=-1;
+#ifdef SHOWIT
+                          fprintf(stderr,"Rejected change by -2 due to too short runlenght. Change set to %d\n",change);
+#endif
+                        }
+
+                      /* First adjust base using large base change instruction (if necessary) */
+                      while ((change>1) || (change<-1) || ((new_runlength==6) && (change)))
+                        {
+                          unsigned int code=0U;
+                          int this_change=change;
+                          if (this_change>2)
+                            this_change=2;
+                          if (this_change<-2)
+                            this_change=-2;
+                          change-=this_change;
+#ifdef SHOWIT
+                          fprintf(stderr,"Large base change: %d.\n",this_change);
+#endif
+                          small_index+=this_change;
+                          if (this_change<0)
+                            {
+                              code|=2U;
+                              this_change=-this_change;
+                            }
+                          code|=(unsigned int)(this_change-1);
+                          write_instruction(coder,INSTR_LARGE_BASE_CHANGE,&output_ptr);
+                          Ptngc_writebits(coder,code,2,&output_ptr);
+                        }
+                      /* If there is still base change or runlength changes to do, we do them now. */
+                      if ((new_runlength!=runlength) || (change))
+                        {
+                          unsigned int ichange=(unsigned int)(change+1);
+                          unsigned int code=0U;
+                          unsigned int irun=(unsigned int)((new_runlength-1)*3);
+                          if (new_runlength==6)
+                            ichange=0; /* Means no change. The change has been taken care of explicitly by a large
+                                          base change instruction above. */
+                          code=ichange+irun;
+#ifdef SHOWIT
+                          fprintf(stderr,"Small base change: %d Runlength change: %d\n",change,new_runlength);
+#endif
+                          small_index+=change;
+                          write_instruction(coder,INSTR_BASE_RUNLENGTH,&output_ptr);
+                          Ptngc_writebits(coder,code,4,&output_ptr);
+                          runlength=new_runlength;
+                        }
+                    }
+#ifdef SHOWIT
+                  else
+                    fprintf(stderr,"Rejected base change due to only change==-1\n");
+#endif
+#ifdef SHOWIT
+                  fprintf(stderr,"Current small index: %d Base=%d\n",small_index,magic[small_index]);
+#endif
+                }
+              /* If we have a large previous integer we can combine it with a sequence of small ints. */
+              if (has_large)
+                {
+                  /* If swapatoms is set to 1 but we did actually not
+                     do any swapping, we must first write out the
+                     large atom and then the small. If swapatoms is 1
+                     and we did swapping we can use the efficient
+                     encoding. */
+                  if ((swapatoms) && (!didswap))
+                    {
+#ifdef SHOWIT
+                      fprintf(stderr,"Swapatoms was set to 1 but we did not do swapping!\n");
+                      fprintf(stderr,"Only one large integer.\n");
+#endif
+                      /* Flush all large atoms. */
+                      flush_large(coder,&has_large,has_large_ints,has_large,large_index,large_nbits,compress_buffer,&output_ptr);
+#ifdef SHOWIT
+                      fprintf(stderr,"Sequence of only small integers.\n");
+#endif
+                      write_instruction(coder,INSTR_ONLY_SMALL,&output_ptr);
+                    }
+                  else
+                    {
+
+#ifdef SHOWIT
+                      fprintf(stderr,"Sequence of one large and small integers (good compression).\n");
+#endif
+                      /* Flush all large atoms but one! */
+                      if (has_large>1)
+                        flush_large(coder,&has_large,has_large_ints,has_large-1,large_index,large_nbits,compress_buffer,&output_ptr);
+                      write_instruction(coder,INSTR_DEFAULT,&output_ptr);
+                      write_three_large(coder,has_large_ints,large_index,large_nbits,compress_buffer,&output_ptr);
+                      has_large=0;
+                    }
+                }
+              else
+                {
+#ifdef SHOWIT
+                  fprintf(stderr,"Sequence of only small integers.\n");
+#endif
+                  write_instruction(coder,INSTR_ONLY_SMALL,&output_ptr);
+                }
+              /* Base compress small integers using the current parameters. */
+              nbits=magic_bits[small_index][runlength-1];
+              /* The same base is used for the small changes. */
+              small_idx[0]=small_index;
+              small_idx[1]=small_index;
+              small_idx[2]=small_index;
+              trajcoder_base_compress(encode_ints,runlength*3,small_idx,compress_buffer);
+#ifdef SHOWIT
+              fprintf(stderr,"nbits=%d (%g)\n",nbits,nbits/(runlength*3.));
+              nbits_sum+=nbits;
+              nvalues_sum+=runlength*3;
+              fprintf(stderr,"Runlength encoded small integers. runlength=%d\n",runlength);
+#endif
+              /* write out base compressed small integers */
+              Ptngc_writemanybits(coder,compress_buffer,nbits,&output_ptr);
+#ifdef SHOWIT
+              for (ienc=0; ienc<runlength; ienc++)
+                fprintf(stderr,"Small: %d %d %d\n",
+                        encode_ints[ienc*3],
+                        encode_ints[ienc*3+1],
+                        encode_ints[ienc*3+2]);
+#endif
+              /* Update prevcoord. */
+              for (ienc=0; ienc<runlength; ienc++)
+                {
+#ifdef SHOWIT
+                  fprintf(stderr,"Prevcoord in packing: %d %d %d\n",
+                          prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+                  prevcoord[0]+=unpositive_int(encode_ints[ienc*3]);
+                  prevcoord[1]+=unpositive_int(encode_ints[ienc*3+1]);
+                  prevcoord[2]+=unpositive_int(encode_ints[ienc*3+2]);
+                }
+#ifdef SHOWIT
+              fprintf(stderr,"Prevcoord in packing: %d %d %d\n",
+                      prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+
+              input_ptr+=3*runlength;
+              ntriplets_left-=runlength;
+            }
+          else
+            {
+#ifdef SHOWIT
+              fprintf(stderr,"Refused value: %d old is %d max is %d\n",new_small_index,small_index,max_large_index);
+              fflush(stderr);
+#endif
+              refused=1;
+            }
+        }
+#ifdef SHOWIT
+      fprintf(stderr,"Number of triplets left is %d\n",ntriplets_left);
+#endif
+    }
+  /* If we have large previous integers we must flush them now. */
+  if (has_large)
+    flush_large(coder,&has_large,has_large_ints,has_large,large_index,large_nbits,compress_buffer,&output_ptr);
+  Ptngc_pack_flush(coder,&output_ptr);
+  output_length=(int)(output_ptr-output);
+#ifdef SHOWIT
+  fprintf(stderr,"Done block: nbits=%d nvalues=%d (%g)\n",nbits_sum,nvalues_sum,(double)nbits_sum/nvalues_sum);
+#endif
+  *length=output_length;
+  return output;
+}
+
+
+int Ptngc_unpack_array_xtc2(struct coder *coder,unsigned char *packed,int *output, int length)
+{
+  unsigned char *ptr=packed;
+  int bitptr=0;
+  int minint[3];
+  int large_index[3];
+  int small_index;
+  int prevcoord[3];
+  int ntriplets_left=length/3;
+  int swapatoms=0;
+  int runlength=0;
+  int large_nbits;
+  unsigned char compress_buffer[18*4]; /* Holds compressed result for 3 large ints or up to 18 small ints. */
+  int encode_ints[21]; /* Up to 3 large + 18 small ints can be encoded at once */
+  (void)coder;
+
+  /* Read min integers. */
+  minint[0]=unpositive_int(readbits(&ptr,&bitptr,32));
+  minint[1]=unpositive_int(readbits(&ptr,&bitptr,32));
+  minint[2]=unpositive_int(readbits(&ptr,&bitptr,32));
+  /* Read large indices */
+  large_index[0]=readbits(&ptr,&bitptr,8);
+  large_index[1]=readbits(&ptr,&bitptr,8);
+  large_index[2]=readbits(&ptr,&bitptr,8);
+  /* Read small index */
+  small_index=readbits(&ptr,&bitptr,8);
+
+  large_nbits=compute_magic_bits(large_index);
+
+#ifdef SHOWIT
+  fprintf(stderr,"Minimum integers: %d %d %d\n",minint[0],minint[1],minint[2]);
+  fprintf(stderr,"Large indices: %d %d %d\n",large_index[0],large_index[1],large_index[2]);
+  fprintf(stderr,"Small index: %d\n",small_index);
+  fprintf(stderr,"large_nbits=%d\n",large_nbits);
+#endif
+
+  /* Initial prevcoord is the minimum integers. */
+  prevcoord[0]=minint[0];
+  prevcoord[1]=minint[1];
+  prevcoord[2]=minint[2];
+
+  while (ntriplets_left)
+    {
+      int instr=read_instruction(&ptr,&bitptr);
+#ifdef SHOWIT
+      if ((instr>=0) && (instr<MAXINSTR))
+        fprintf(stderr,"Decoded instruction %s\n",instrnames[instr]);
+#endif
+      if ((instr==INSTR_DEFAULT) /* large+small */
+          || (instr==INSTR_ONLY_LARGE) /* only large */
+          || (instr==INSTR_ONLY_SMALL)) /* only small */
+        {
+          int large_ints[3]={0,0,0};
+          if (instr!=INSTR_ONLY_SMALL)
+            {
+              /* Clear the compress buffer. */
+              int i;
+              for (i=0; i<18*4; i++)
+                compress_buffer[i]=0;
+              /* Get the large value. */
+              readmanybits(&ptr,&bitptr,large_nbits,compress_buffer);
+              trajcoder_base_decompress(compress_buffer,3,large_index,encode_ints);
+              large_ints[0]=encode_ints[0];
+              large_ints[1]=encode_ints[1];
+              large_ints[2]=encode_ints[2];
+#ifdef SHOWIT
+              fprintf(stderr,"large ints: %d %d %d\n",large_ints[0],large_ints[1],large_ints[2]);
+#endif
+            }
+          if (instr!=INSTR_ONLY_LARGE)
+            {
+              int small_idx[3];
+              int i;
+              /* The same base is used for the small changes. */
+              small_idx[0]=small_index;
+              small_idx[1]=small_index;
+              small_idx[2]=small_index;
+              /* Clear the compress buffer. */
+              for (i=0; i<18*4; i++)
+                compress_buffer[i]=0;
+              /* Get the small values. */
+              readmanybits(&ptr,&bitptr,magic_bits[small_index][runlength-1],compress_buffer);
+              trajcoder_base_decompress(compress_buffer,3*runlength,small_idx,encode_ints);
+#ifdef SHOWIT
+              for (i=0; i<runlength; i++)
+                fprintf(stderr,"small ints: %d %d %d\n",encode_ints[i*3+0],encode_ints[i*3+1],encode_ints[i*3+2]);
+#endif
+            }
+          if (instr==INSTR_DEFAULT)
+            {
+              /* Check for swapped atoms */
+              if (swapatoms)
+                {
+                  /* Unswap the atoms. */
+                  int i;
+                  for (i=0; i<3; i++)
+                    {
+                      int in[3], out[3];
+                      in[0]=large_ints[i];
+                      in[1]=unpositive_int(encode_ints[i]);
+                      in[2]=unpositive_int(encode_ints[3+i]);
+                      swap_ints(in,out);
+                      large_ints[i]=out[0];
+                      encode_ints[i]=positive_int(out[1]);
+                      encode_ints[3+i]=positive_int(out[2]);
+                    }
+                }
+            }
+          /* Output result. */
+          if (instr!=INSTR_ONLY_SMALL)
+            {
+              /* Output large value */
+              *output++=large_ints[0]+minint[0];
+              *output++=large_ints[1]+minint[1];
+              *output++=large_ints[2]+minint[2];
+              prevcoord[0]=large_ints[0];
+              prevcoord[1]=large_ints[1];
+              prevcoord[2]=large_ints[2];
+#ifdef SHOWIT
+              fprintf(stderr,"Prevcoord after unpacking of large: %d %d %d\n",
+                      prevcoord[0],prevcoord[1],prevcoord[2]);
+              fprintf(stderr,"VALUE:%d %6d %6d %6d\n",
+                      length/3-ntriplets_left,
+                      prevcoord[0]+minint[0],
+                      prevcoord[1]+minint[1],
+                      prevcoord[2]+minint[2]);
+#endif
+              ntriplets_left--;
+            }
+          if (instr!=INSTR_ONLY_LARGE)
+            {
+              /* Output small values */
+              int i;
+#ifdef SHOWIT
+              fprintf(stderr,"Prevcoord before unpacking of small: %d %d %d\n",
+                          prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+              for (i=0; i<runlength; i++)
+                {
+                  int v[3];
+                  v[0]=unpositive_int(encode_ints[i*3]);
+                  v[1]=unpositive_int(encode_ints[i*3+1]);
+                  v[2]=unpositive_int(encode_ints[i*3+2]);
+                  prevcoord[0]+=v[0];
+                  prevcoord[1]+=v[1];
+                  prevcoord[2]+=v[2];
+#ifdef SHOWIT
+                  fprintf(stderr,"Prevcoord after unpacking of small: %d %d %d\n",
+                          prevcoord[0],prevcoord[1],prevcoord[2]);
+                  fprintf(stderr,"Unpacked small values: %6d %6d %6d\t\t%6d %6d %6d\n",v[0],v[1],v[2],prevcoord[0],prevcoord[1],prevcoord[2]);
+                  fprintf(stderr,"VALUE:%d %6d %6d %6d\n",
+                          length/3-(ntriplets_left-i),
+                          prevcoord[0]+minint[0],
+                          prevcoord[1]+minint[1],
+                          prevcoord[2]+minint[2]);
+#endif
+                  *output++=prevcoord[0]+minint[0];
+                  *output++=prevcoord[1]+minint[1];
+                  *output++=prevcoord[2]+minint[2];
+                }
+              ntriplets_left-=runlength;
+            }
+        }
+      else if (instr==INSTR_LARGE_RLE)
+        {
+          int i,j;
+          int large_ints[3];
+          /* How many large atoms in this sequence? */
+          int n=(int)readbits(&ptr,&bitptr,4)+3; /* 3-18 large atoms */
+          for (i=0; i<n; i++)
+            {
+              /* Clear the compress buffer. */
+              for (j=0; j<18*4; j++)
+                compress_buffer[j]=0;
+              /* Get the large value. */
+              readmanybits(&ptr,&bitptr,large_nbits,compress_buffer);
+              trajcoder_base_decompress(compress_buffer,3,large_index,encode_ints);
+              large_ints[0]=encode_ints[0];
+              large_ints[1]=encode_ints[1];
+              large_ints[2]=encode_ints[2];
+              /* Output large value */
+              *output++=large_ints[0]+minint[0];
+              *output++=large_ints[1]+minint[1];
+              *output++=large_ints[2]+minint[2];
+              prevcoord[0]=large_ints[0];
+              prevcoord[1]=large_ints[1];
+              prevcoord[2]=large_ints[2];
+            }
+          ntriplets_left-=n;
+        }
+      else if (instr==INSTR_BASE_RUNLENGTH)
+        {
+          unsigned int code=readbits(&ptr,&bitptr,4);
+          int change;
+          if (code==15)
+            {
+              change=0;
+              runlength=6;
+            }
+          else
+            {
+              int ichange=code%3;
+              runlength=code/3+1;
+              change=ichange-1;
+            }
+          small_index+=change;
+        }
+      else if (instr==INSTR_FLIP)
+        {
+          swapatoms=1-swapatoms;
+        }
+      else if (instr==INSTR_LARGE_BASE_CHANGE)
+        {
+          unsigned int ichange=readbits(&ptr,&bitptr,2);
+          int change=(int)(ichange&0x1U)+1;
+          if (ichange&0x2U)
+            change=-change;
+          small_index+=change;
+        }
+      else
+        {
+          fprintf(stderr,"TRAJNG: BUG! Encoded unknown instruction.\n");
+          exit(EXIT_FAILURE);
+        }
+#ifdef SHOWIT
+      fprintf(stderr,"Number of triplets left is %d\n",ntriplets_left);
+#endif
+    }
+  return 0;
+}
diff --git a/src/external/tng_io/src/compression/xtc3.c b/src/external/tng_io/src/compression/xtc3.c
new file mode 100644 (file)
index 0000000..95483b3
--- /dev/null
@@ -0,0 +1,1955 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+/* This code is heavily influenced by
+   http://hpcv100.rc.rug.nl/xdrf.html
+   Based on coordinate compression (c) by Frans van Hoesel.
+   and GROMACS xtc files (http://www.gromacs.org)
+   (c) Copyright (c) Erik Lindahl, David van der Spoel
+*/
+
+/* The cost estimates are ripped right out of xtc2.c, so take these
+   with a grain (truckload) of salt. */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "../../include/compression/warnmalloc.h"
+#include "../../include/compression/widemuldiv.h"
+#include "../../include/compression/bwlzh.h"
+
+static const double iflipgaincheck=0.89089871814033927; /*  1./(2**(1./6)) */
+
+#define MAX_LARGE_RLE 1024 /* Maximum number of large atoms for large RLE. */
+#define MAX_SMALL_RLE 12 /* Maximum number of small atoms in one group. */
+
+#define TRESHOLD_INTRA_INTER_DIRECT 1.5 /* How much larger can the direct
+                                           frame deltas for the small
+                                           triplets be and be accepted anyway
+                                           as better than the intra/inter frame
+                                           deltas. For better instructions/RLEs. */
+
+#define TRESHOLD_INTER_INTRA 5.0 /* How much larger can the intra
+                                    frame deltas for the small
+                                    triplets be and be accepted anyway
+                                    as better than the inter frame
+                                    deltas. */
+
+/* Difference in indices used for determining whether to store as
+   large or small. A fun detail in this compression algorithm is that
+   if everything works fine, large can often be smaller than small, or
+   at least not as large as is large in magic.c. This is a key idea of
+   xtc3. */
+#define QUITE_LARGE 3
+#define IS_LARGE 6
+
+#if 0
+#define SHOWIT
+#endif
+
+#if 0
+#define SHOWIT_LIGHT
+#endif
+
+/* These routines are in xtc2.c */
+int Ptngc_magic(unsigned int i);
+int Ptngc_find_magic_index(unsigned int maxval);
+
+static unsigned int positive_int(int item)
+{
+  int s=0;
+  if (item>0)
+    s=1+(item-1)*2;
+  else if (item<0)
+    s=2+(-item-1)*2;
+  return s;
+}
+
+static int unpositive_int(int val)
+{
+  int s=(val+1)/2;
+  if ((val%2)==0)
+    s=-s;
+  return s;
+}
+
+
+/* Sequence instructions */
+#define INSTR_DEFAULT 0U
+#define INSTR_SMALL_RUNLENGTH 1U
+#define INSTR_ONLY_LARGE 2U
+#define INSTR_ONLY_SMALL 3U
+#define INSTR_FLIP 4U
+#define INSTR_LARGE_RLE 5U
+#define INSTR_LARGE_DIRECT 6U
+#define INSTR_LARGE_INTRA_DELTA 7U
+#define INSTR_LARGE_INTER_DELTA 8U
+
+#define MAXINSTR 9
+
+struct xtc3_context
+{
+  unsigned int *instructions;
+  int ninstr, ninstr_alloc;
+  unsigned int *rle;
+  int nrle, nrle_alloc;
+  unsigned int *large_direct;
+  int nlargedir, nlargedir_alloc;
+  unsigned int *large_intra_delta;
+  int nlargeintra, nlargeintra_alloc;
+  unsigned int *large_inter_delta;
+  int nlargeinter, nlargeinter_alloc;
+  unsigned int *smallintra;
+  int nsmallintra, nsmallintra_alloc;
+  int minint[3],maxint[3];
+  int has_large;
+  int has_large_ints[MAX_LARGE_RLE*3]; /* Large cache. */
+  int has_large_type[MAX_LARGE_RLE]; /* What kind of type this large
+                                        int is. */
+  int current_large_type;
+};
+
+static void init_xtc3_context(struct xtc3_context *xtc3_context)
+{
+  xtc3_context->instructions=NULL;
+  xtc3_context->ninstr=0;
+  xtc3_context->ninstr_alloc=0;
+  xtc3_context->rle=NULL;
+  xtc3_context->nrle=0;
+  xtc3_context->nrle_alloc=0;
+  xtc3_context->large_direct=NULL;
+  xtc3_context->nlargedir=0;
+  xtc3_context->nlargedir_alloc=0;
+  xtc3_context->large_intra_delta=NULL;
+  xtc3_context->nlargeintra=0;
+  xtc3_context->nlargeintra_alloc=0;
+  xtc3_context->large_inter_delta=NULL;
+  xtc3_context->nlargeinter=0;
+  xtc3_context->nlargeinter_alloc=0;
+  xtc3_context->smallintra=NULL;
+  xtc3_context->nsmallintra=0;
+  xtc3_context->nsmallintra_alloc=0;
+  xtc3_context->has_large=0;
+  xtc3_context->current_large_type=0;
+}
+
+static void free_xtc3_context(struct xtc3_context *xtc3_context)
+{
+  free(xtc3_context->instructions);
+  free(xtc3_context->rle);
+  free(xtc3_context->large_direct);
+  free(xtc3_context->large_intra_delta);
+  free(xtc3_context->large_inter_delta);
+  free(xtc3_context->smallintra);
+}
+
+/* Modifies three integer values for better compression of water */
+static void swap_ints(int *in, int *out)
+{
+  out[0]=in[0]+in[1];
+  out[1]=-in[1];
+  out[2]=in[1]+in[2];
+}
+
+static void swap_is_better(int *input, int *minint, int *sum_normal, int *sum_swapped)
+{
+  int normal_max=0;
+  int swapped_max=0;
+  int i,j;
+  int normal[3];
+  int swapped[3];
+  for (i=0; i<3; i++)
+    {
+      normal[0]=input[i]-minint[i];
+      normal[1]=input[3+i]-input[i]; /* minint[i]-minint[i] cancels out */
+      normal[2]=input[6+i]-input[3+i]; /* minint[i]-minint[i] cancels out */
+      swap_ints(normal,swapped);
+      for (j=1; j<3; j++)
+        {
+          if (positive_int(normal[j])>(unsigned int)normal_max)
+            normal_max=positive_int(normal[j]);
+          if (positive_int(swapped[j])>(unsigned int)swapped_max)
+            swapped_max=positive_int(swapped[j]);
+        }
+    }
+  if (normal_max==0)
+    normal_max=1;
+  if (swapped_max==0)
+    swapped_max=1;
+  *sum_normal=normal_max;
+  *sum_swapped=swapped_max;
+}
+
+static void allocate_enough_memory(unsigned int **ptr, int *nele, int *nele_alloc)
+{
+  (*nele)++;
+  if (*nele>*nele_alloc)
+    {
+      *nele_alloc=*nele + *nele/2;
+      *ptr=warnrealloc(*ptr,*nele_alloc*sizeof **ptr);
+    }
+}
+
+static void insert_value_in_array(unsigned int **ptr, int *nele, int *nele_alloc,
+                                  unsigned int value,
+                                  char *arrayname)
+{
+#ifndef SHOWIT
+  (void)arrayname;
+#endif
+  allocate_enough_memory(ptr,nele,nele_alloc);
+#ifdef SHOWIT
+  fprintf(stderr,"Inserting value %u into array %s @ %d\n",value,arrayname,(*nele)-1);
+#endif
+  (*ptr)[(*nele)-1]=value;
+}
+
+
+
+static void swapdecide(struct xtc3_context *xtc3_context, int *input,int *swapatoms, int *large_index, int *minint)
+{
+  int didswap=0;
+  int normal,swapped;
+  (void)large_index;
+  swap_is_better(input,minint,&normal,&swapped);
+  /* We have to determine if it is worth to change the behaviour.
+     If diff is positive it means that it is worth something to
+     swap. But it costs 4 bits to do the change. If we assume that
+     we gain 0.17 bit by the swap per value, and the runlength>2
+     for four molecules in a row, we gain something. So check if we
+     gain at least 0.17 bits to even attempt the swap.
+  */
+#ifdef SHOWIT
+  fprintf(stderr,"Trying Flip: %g %g\n",(double)swapped/normal, (double)normal/swapped);
+#endif
+  if (((swapped<normal) && (fabs((double)swapped/normal)<iflipgaincheck)) ||
+      ((normal<swapped) && (fabs((double)normal/swapped)<iflipgaincheck)))
+    {
+      if (swapped<normal)
+        {
+          if (!*swapatoms)
+            {
+              *swapatoms=1;
+              didswap=1;
+            }
+        }
+      else
+        {
+          if (*swapatoms)
+            {
+              *swapatoms=0;
+              didswap=1;
+            }
+        }
+    }
+  if (didswap)
+    {
+#ifdef SHOWIT
+      fprintf(stderr,"Flip. Swapatoms is now %d\n",*swapatoms);
+#endif
+      insert_value_in_array(&xtc3_context->instructions,
+                            &xtc3_context->ninstr,
+                            &xtc3_context->ninstr_alloc,
+                            INSTR_FLIP,"instr");
+    }
+}
+
+/* It is "large" if we have to increase the small index quite a
+   bit. Not so much to be rejected by the not very large check
+   later. */
+static int is_quite_large(int *input, int small_index, int max_large_index)
+{
+  int is=0;
+  int i;
+  if (small_index+QUITE_LARGE>=max_large_index)
+    is=1;
+  else
+    {
+      for (i=0; i<3; i++)
+        if (positive_int(input[i])>(unsigned int)Ptngc_magic(small_index+QUITE_LARGE))
+          {
+            is=1;
+            break;
+          }
+    }
+  return is;
+}
+
+#ifdef SHOWIT
+int nbits_sum;
+int nvalues_sum;
+#endif
+
+static void insert_batch(int *input_ptr, int ntriplets_left, int *prevcoord, int *encode_ints, int startenc, int *nenc)
+{
+  int nencode=startenc*3;
+  int tmp_prevcoord[3];
+
+  tmp_prevcoord[0]=prevcoord[0];
+  tmp_prevcoord[1]=prevcoord[1];
+  tmp_prevcoord[2]=prevcoord[2];
+
+  if (startenc)
+    {
+      int i;
+      for (i=0; i<startenc; i++)
+        {
+          tmp_prevcoord[0]+=encode_ints[i*3];
+          tmp_prevcoord[1]+=encode_ints[i*3+1];
+          tmp_prevcoord[2]+=encode_ints[i*3+2];
+#ifdef SHOWIT
+          fprintf(stderr,"%6d: %6d %6d %6d\t\t%6d %6d %6d\t\t%6d %6d %6d\n",i*3,
+                  tmp_prevcoord[0],tmp_prevcoord[1],tmp_prevcoord[2],
+                  encode_ints[i*3],
+                  encode_ints[i*3+1],
+                  encode_ints[i*3+2],
+                  positive_int(encode_ints[i*3]),
+                  positive_int(encode_ints[i*3+1]),
+                  positive_int(encode_ints[i*3+2]));
+#endif
+        }
+    }
+
+#ifdef SHOWIT
+  fprintf(stderr,"New batch\n");
+#endif
+  while ((nencode<3+MAX_SMALL_RLE*3) && (nencode<ntriplets_left*3))
+    {
+      encode_ints[nencode]=input_ptr[nencode]-tmp_prevcoord[0];
+      encode_ints[nencode+1]=input_ptr[nencode+1]-tmp_prevcoord[1];
+      encode_ints[nencode+2]=input_ptr[nencode+2]-tmp_prevcoord[2];
+#ifdef SHOWIT
+      fprintf(stderr,"%6d: %6d %6d %6d\t\t%6d %6d %6d\t\t%6d %6d %6d\n",nencode,
+              input_ptr[nencode],
+              input_ptr[nencode+1],
+              input_ptr[nencode+2],
+              encode_ints[nencode],
+              encode_ints[nencode+1],
+              encode_ints[nencode+2],
+              positive_int(encode_ints[nencode]),
+              positive_int(encode_ints[nencode+1]),
+              positive_int(encode_ints[nencode+2]));
+#endif
+      tmp_prevcoord[0]=input_ptr[nencode];
+      tmp_prevcoord[1]=input_ptr[nencode+1];
+      tmp_prevcoord[2]=input_ptr[nencode+2];
+      nencode+=3;
+    }
+  *nenc=nencode;
+}
+
+static void large_instruction_change(struct xtc3_context *xtc3_context, int i)
+{
+  /* If the first large is of a different kind than the currently used we must
+     emit an "instruction" to change the large type. */
+  if (xtc3_context->has_large_type[i]!=xtc3_context->current_large_type)
+    {
+      unsigned int instr;
+      xtc3_context->current_large_type=xtc3_context->has_large_type[i];
+      if (xtc3_context->current_large_type==0)
+        instr=INSTR_LARGE_DIRECT;
+      else if (xtc3_context->current_large_type==1)
+        instr=INSTR_LARGE_INTRA_DELTA;
+      else
+        instr=INSTR_LARGE_INTER_DELTA;
+      insert_value_in_array(&xtc3_context->instructions,
+                            &xtc3_context->ninstr,
+                            &xtc3_context->ninstr_alloc,
+                            instr,"instr");
+    }
+}
+
+static void write_three_large(struct xtc3_context *xtc3_context,
+                              int i)
+{
+  int m;
+  if (xtc3_context->current_large_type==0)
+    {
+      for (m=0; m<3; m++)
+        insert_value_in_array(&xtc3_context->large_direct,
+                              &xtc3_context->nlargedir,
+                              &xtc3_context->nlargedir_alloc,
+                              xtc3_context->has_large_ints[i*3+m],"large direct");
+    }
+  else if (xtc3_context->current_large_type==1)
+    {
+      for (m=0; m<3; m++)
+        insert_value_in_array(&xtc3_context->large_intra_delta,
+                              &xtc3_context->nlargeintra,
+                              &xtc3_context->nlargeintra_alloc,
+                              xtc3_context->has_large_ints[i*3+m],"large intra");
+    }
+  else
+    {
+      for (m=0; m<3; m++)
+        insert_value_in_array(&xtc3_context->large_inter_delta,
+                              &xtc3_context->nlargeinter,
+                              &xtc3_context->nlargeinter_alloc,
+                              xtc3_context->has_large_ints[i*3+m],"large inter");
+    }
+}
+
+static void flush_large(struct xtc3_context *xtc3_context,
+                        int n) /* How many to flush. */
+{
+  int i;
+  i=0;
+  while (i<n)
+    {
+      int j,k;
+      /* If the first large is of a different kind than the currently used we must
+         emit an "instruction" to change the large type. */
+      large_instruction_change(xtc3_context,i);
+      /* How many large of the same kind in a row? */
+      for (j=0;
+           (i+j<n) &&
+             (xtc3_context->has_large_type[i+j]==xtc3_context->has_large_type[i]);
+           j++);
+      if (j<3)
+        {
+          for (k=0; k<j; k++)
+            {
+              insert_value_in_array(&xtc3_context->instructions,
+                                    &xtc3_context->ninstr,
+                                    &xtc3_context->ninstr_alloc,
+                                    INSTR_ONLY_LARGE,"instr");
+              write_three_large(xtc3_context,i+k);
+            }
+        }
+      else
+        {
+          insert_value_in_array(&xtc3_context->instructions,
+                                &xtc3_context->ninstr,
+                                &xtc3_context->ninstr_alloc,
+                                INSTR_LARGE_RLE,"instr");
+          insert_value_in_array(&xtc3_context->rle,
+                                &xtc3_context->nrle,
+                                &xtc3_context->nrle_alloc,
+                                (unsigned int)j,"rle (large)");
+          for (k=0; k<j; k++)
+            write_three_large(xtc3_context,i+k);
+        }
+      i+=j;
+    }
+  if ((xtc3_context->has_large-n)!=0)
+    {
+      int j;
+      for (i=0; i<xtc3_context->has_large-n; i++)
+        {
+          xtc3_context->has_large_type[i]=xtc3_context->has_large_type[i+n];
+          for (j=0; j<3; j++)
+            xtc3_context->has_large_ints[i*3+j]=xtc3_context->has_large_ints[(i+n)*3+j];
+        }
+    }
+  xtc3_context->has_large-=n; /* Number of remaining large atoms in buffer */
+}
+
+static double compute_intlen(unsigned int *ints)
+{
+  /* The largest value. */
+  unsigned int m=ints[0];
+  if (ints[1]>m)
+    m=ints[1];
+  if (ints[2]>m)
+    m=ints[2];
+  return (double)m;
+}
+
+static void buffer_large(struct xtc3_context *xtc3_context, int *input, int inpdata,
+                         int natoms, int intradelta_ok)
+{
+  unsigned int direct[3], intradelta[3]={0,}, interdelta[3]={0,};
+  double minlen;
+  int best_type;
+  int frame=inpdata/(natoms*3);
+  int atomframe=inpdata%(natoms*3);
+  /* If it is full we must write them all. */
+  if (xtc3_context->has_large==MAX_LARGE_RLE)
+    flush_large(xtc3_context,xtc3_context->has_large); /* Flush all. */
+  /* Find out which is the best choice for the large integer. Direct coding, or some
+     kind of delta coding? */
+  /* First create direct coding. */
+  direct[0]=(unsigned int)(input[inpdata]-xtc3_context->minint[0]);
+  direct[1]=(unsigned int)(input[inpdata+1]-xtc3_context->minint[1]);
+  direct[2]=(unsigned int)(input[inpdata+2]-xtc3_context->minint[2]);
+  minlen=compute_intlen(direct);
+  best_type=0; /* Direct. */
+#if 1
+  /* Then try intra coding if we can. */
+  if ((intradelta_ok) && (atomframe>=3))
+    {
+      double thislen;
+      intradelta[0]=positive_int(input[inpdata]-input[inpdata-3]);
+      intradelta[1]=positive_int(input[inpdata+1]-input[inpdata-2]);
+      intradelta[2]=positive_int(input[inpdata+2]-input[inpdata-1]);
+      thislen=compute_intlen(intradelta);
+      if (thislen*TRESHOLD_INTRA_INTER_DIRECT<minlen)
+        {
+          minlen=thislen;
+          best_type=1; /* Intra delta */
+        }
+    }
+#endif
+#if 1
+  /* Then try inter coding if we can. */
+  if (frame>0)
+    {
+      double thislen;
+      interdelta[0]=positive_int(input[inpdata]-input[inpdata-natoms*3]);
+      interdelta[1]=positive_int(input[inpdata+1]-input[inpdata-natoms*3+1]);
+      interdelta[2]=positive_int(input[inpdata+2]-input[inpdata-natoms*3+2]);
+      thislen=compute_intlen(interdelta);
+      if (thislen*TRESHOLD_INTRA_INTER_DIRECT<minlen)
+        {
+          best_type=2; /* Inter delta */
+        }
+    }
+#endif
+  xtc3_context->has_large_type[xtc3_context->has_large]=best_type;
+  if (best_type==0)
+    {
+      xtc3_context->has_large_ints[xtc3_context->has_large*3]=direct[0];
+      xtc3_context->has_large_ints[xtc3_context->has_large*3+1]=direct[1];
+      xtc3_context->has_large_ints[xtc3_context->has_large*3+2]=direct[2];
+    }
+  else if (best_type==1)
+    {
+      xtc3_context->has_large_ints[xtc3_context->has_large*3]=intradelta[0];
+      xtc3_context->has_large_ints[xtc3_context->has_large*3+1]=intradelta[1];
+      xtc3_context->has_large_ints[xtc3_context->has_large*3+2]=intradelta[2];
+    }
+  else if (best_type==2)
+    {
+      xtc3_context->has_large_ints[xtc3_context->has_large*3]=interdelta[0];
+      xtc3_context->has_large_ints[xtc3_context->has_large*3+1]=interdelta[1];
+      xtc3_context->has_large_ints[xtc3_context->has_large*3+2]=interdelta[2];
+    }
+  xtc3_context->has_large++;
+}
+
+static void output_int(unsigned char *output,int *outdata, unsigned int n)
+{
+  output[(*outdata)++]=((unsigned int)n)&0xFFU;
+  output[(*outdata)++]=(((unsigned int)n)>>8)&0xFFU;
+  output[(*outdata)++]=(((unsigned int)n)>>16)&0xFFU;
+  output[(*outdata)++]=(((unsigned int)n)>>24)&0xFFU;
+}
+
+#if 0
+static void printarray(unsigned int *a, int n, char *name)
+{
+  int i;
+  for (i=0; i<n; i++)
+    fprintf(stderr,"%u %s\n",a[i],name);
+}
+#endif
+
+/* The base_compress routine first compresses all x coordinates, then
+   y and finally z. The bases used for each can be different. The
+   MAXBASEVALS value determines how many coordinates are compressed
+   into a single number. Only resulting whole bytes are dealt with for
+   simplicity. MAXMAXBASEVALS is the insanely large value to accept
+   files written with that value. BASEINTERVAL determines how often a
+   new base is actually computed and stored in the output
+   file. MAXBASEVALS*BASEINTERVAL values are stored using the same
+   base in BASEINTERVAL different integers. Note that the primarily
+   the decompression using a large MAXBASEVALS becomes very slow. */
+#define MAXMAXBASEVALS 16384U
+#define MAXBASEVALS 24U
+#define BASEINTERVAL 8
+
+/* How many bytes are needed to store n values in base base */
+static int base_bytes(unsigned int base, int n)
+{
+  int i,j;
+  unsigned int largeint[MAXMAXBASEVALS+1];
+  unsigned int largeint_tmp[MAXMAXBASEVALS+1];
+  int numbytes=0;
+  for (i=0; i<n+1; i++)
+    largeint[i]=0U;
+  for (i=0; i<n; i++)
+    {
+      if (i!=0)
+        {
+          Ptngc_largeint_mul(base,largeint,largeint_tmp,n+1);
+          for (j=0; j<n+1; j++)
+            largeint[j]=largeint_tmp[j];
+        }
+      Ptngc_largeint_add(base-1U,largeint,n+1);
+    }
+  for (i=0; i<n; i++)
+    if (largeint[i])
+      for (j=0; j<4; j++)
+        if ((largeint[i]>>(j*8))&0xFFU)
+          numbytes=i*4+j+1;
+  return numbytes;
+}
+
+static void base_compress(unsigned int *data, int len, unsigned char *output, int *outlen)
+{
+  unsigned int largeint[MAXBASEVALS+1];
+  unsigned int largeint_tmp[MAXBASEVALS+1];
+  int ixyz, i;
+  unsigned int j;
+  int nwrittenout=0;
+  unsigned int numbytes=0;
+  /* Store the MAXBASEVALS value in the output. */
+  output[nwrittenout++]=(unsigned char)(MAXBASEVALS&0xFFU);
+  output[nwrittenout++]=(unsigned char)((MAXBASEVALS>>8)&0xFFU);
+  /* Store the BASEINTERVAL value in the output. */
+  output[nwrittenout++]=(unsigned char)(BASEINTERVAL&0xFFU);
+  for (ixyz=0; ixyz<3; ixyz++)
+    {
+      unsigned int base=0U;
+      int nvals=0;
+      int basegiven=0;
+      for (j=0; j<MAXBASEVALS+1; j++)
+        largeint[j]=0U;
+      for (i=ixyz; i<len; i+=3)
+        {
+         if (nvals==0)
+           {
+             int basecheckvals=0;
+             int k;
+             if (basegiven==0)
+               {
+                 base=0U;
+                 /* Find the largest value for this particular coordinate. */
+                 for (k=i; k<len; k+=3)
+                   {
+                     if (data[k]>base)
+                       base=data[k];
+                     basecheckvals++;
+                     if (basecheckvals==MAXBASEVALS*BASEINTERVAL)
+                       break;
+                   }
+                 /* The base is one larger than the largest values. */
+                 base++;
+                 if (base<2)
+                   base=2;
+                 /* Store the base in the output. */
+                 output[nwrittenout++]=(unsigned char)(base&0xFFU);
+                 output[nwrittenout++]=(unsigned char)((base>>8)&0xFFU);
+                 output[nwrittenout++]=(unsigned char)((base>>16)&0xFFU);
+                 output[nwrittenout++]=(unsigned char)((base>>24)&0xFFU);
+                 basegiven=BASEINTERVAL;
+                 /* How many bytes is needed to store MAXBASEVALS values using this base? */
+                 numbytes=base_bytes(base,MAXBASEVALS);
+               }
+             basegiven--;
+#ifdef SHOWIT
+             fprintf(stderr,"Base for %d is %u. I need %d bytes for %d values.\n",ixyz,base,numbytes,MAXBASEVALS);
+#endif
+           }
+          if (nvals!=0)
+            {
+              Ptngc_largeint_mul(base,largeint,largeint_tmp,MAXBASEVALS+1);
+              for (j=0; j<MAXBASEVALS+1; j++)
+                largeint[j]=largeint_tmp[j];
+            }
+          Ptngc_largeint_add(data[i],largeint,MAXBASEVALS+1);
+#ifdef SHOWIT
+          fprintf(stderr,"outputting value %u\n",data[i]);
+#endif
+          nvals++;
+          if (nvals==MAXBASEVALS)
+            {
+#ifdef SHOWIT
+              fprintf(stderr,"Writing largeint: ");
+#endif
+              for (j=0; j<numbytes; j++)
+                {
+                  int ilarge=j/4;
+                  int ibyte=j%4;
+                  output[nwrittenout++]=(unsigned char)((largeint[ilarge]>>(ibyte*8))&(0xFFU));
+#ifdef SHOWIT
+                  fprintf(stderr,"%02x",(unsigned int)output[nwrittenout-1]);
+#endif
+                }
+#ifdef SHOWIT
+              fprintf(stderr,"\n");
+#endif
+              nvals=0;
+              for (j=0; j<MAXBASEVALS+1; j++)
+                largeint[j]=0U;
+            }
+        }
+      if (nvals)
+        {
+          numbytes=base_bytes(base,nvals);
+#ifdef SHOWIT
+          fprintf(stderr,"Base for %d is %u. I need %d bytes for %d values.\n",ixyz,base,numbytes,nvals);
+#endif
+          for (j=0; j<numbytes; j++)
+            {
+              int ilarge=j/4;
+              int ibyte=j%4;
+              output[nwrittenout++]=(unsigned char)((largeint[ilarge]>>(ibyte*8))&(0xFFU));
+            }
+        }
+    }
+  *outlen=nwrittenout;
+}
+
+static void base_decompress(unsigned char *input, int len, unsigned int *output)
+{
+  unsigned int largeint[MAXMAXBASEVALS+1];
+  unsigned int largeint_tmp[MAXMAXBASEVALS+1];
+  int ixyz, i, j;
+  int maxbasevals=(int)((unsigned int)(input[0])|(((unsigned int)(input[1]))<<8));
+  int baseinterval=(int)input[2];
+  if (maxbasevals>(int)MAXMAXBASEVALS)
+    {
+      fprintf(stderr,"Read a larger maxbasevals value from the file than I can handle. Fix"
+              " by increasing MAXMAXBASEVALS to at least %d. Although, this is"
+              " probably a bug in TRAJNG, since MAXMAXBASEVALS should already be insanely large enough.\n",maxbasevals);
+      exit(EXIT_FAILURE);
+    }
+  input+=3;
+  for (ixyz=0; ixyz<3; ixyz++)
+    {
+      int numbytes=0;
+      int nvals_left=len/3;
+      int outvals=ixyz;
+      int basegiven=0;
+      unsigned int base=0U;
+#ifdef SHOWIT
+      fprintf(stderr,"Base for %d is %u. I need %d bytes for %d values.\n",ixyz,base,numbytes,maxbasevals);
+#endif
+      while (nvals_left)
+        {
+          int n;
+          if (basegiven==0)
+            {
+              base=(unsigned int)(input[0])|
+                (((unsigned int)(input[1]))<<8)|
+                (((unsigned int)(input[2]))<<16)|
+                (((unsigned int)(input[3]))<<24);
+              input+=4;
+              basegiven=baseinterval;
+              /* How many bytes is needed to store maxbasevals values using this base? */
+              numbytes=base_bytes(base,maxbasevals);
+            }
+          basegiven--;
+          if (nvals_left<maxbasevals)
+            {
+              numbytes=base_bytes(base,nvals_left);
+#ifdef SHOWIT
+              fprintf(stderr,"Base for %d is %u. I need %d bytes for %d values.\n",ixyz,base,numbytes,nvals_left);
+#endif
+            }
+          for (j=0; j<maxbasevals+1; j++)
+            largeint[j]=0U;
+#ifdef SHOWIT
+          fprintf(stderr,"Reading largeint: ");
+#endif
+          if (numbytes/4 < maxbasevals+1)
+            {
+              for (j=0; j<numbytes; j++)
+                {
+                  int ilarge=j/4;
+                  int ibyte=j%4;
+                  largeint[ilarge]|=((unsigned int)input[j])<<(ibyte*8);
+#ifdef SHOWIT
+                  fprintf(stderr,"%02x",(unsigned int)input[j]);
+#endif
+                }
+            }
+#ifdef SHOWIT
+          fprintf(stderr,"\n");
+#endif
+          input+=numbytes;
+          /* Do the long division required to get the output values. */
+          n=maxbasevals;
+          if (n>nvals_left)
+            n=nvals_left;
+          for (i=n-1; i>=0; i--)
+            {
+              output[outvals+i*3]=Ptngc_largeint_div(base,largeint,largeint_tmp,maxbasevals+1);
+              for (j=0; j<maxbasevals+1; j++)
+                largeint[j]=largeint_tmp[j];
+            }
+#ifdef SHOWIT
+          for (i=0; i<n; i++)
+            fprintf(stderr,"outputting value %u\n",output[outvals+i*3]);
+#endif
+          outvals+=n*3;
+          nvals_left-=n;
+        }
+    }
+}
+
+/* If a large proportion of the integers are large (More than 10\% are >14 bits) we return 0, otherwise 1 */
+static int heuristic_bwlzh(unsigned int *ints, int nints)
+{
+  int i,num;
+  num=0;
+  for (i=0; i<nints; i++)
+    if (ints[i]>=16384)
+      num++;
+  if (num>nints/10)
+    return 0;
+  else
+    return 1;
+}
+
+/* Speed selects how careful to try to find the most efficient compression. The BWLZH algo is expensive!
+   Speed <=2 always avoids BWLZH everywhere it is possible.
+   Speed 3 and 4 and 5 use heuristics (check proportion of large value). This should mostly be safe.
+   Speed 5 enables the LZ77 component of BWLZH.
+   Speed 6 always tests if BWLZH is better and if it is uses it. This can be very slow.
+ */
+unsigned char *Ptngc_pack_array_xtc3(int *input, int *length, int natoms, int speed)
+{
+  unsigned char *output=NULL;
+  int i,ienc,j;
+  int outdata=0;
+  /* Pack triplets. */
+  int ntriplets=*length/3;
+  int intmax;
+  int max_small;
+  int small_index;
+  int max_large_index;
+  int large_index[3];
+  int prevcoord[3];
+  int runlength=0; /* Initial runlength. "Stupidly" set to zero for
+                      simplicity and explicity */
+  int swapatoms=0; /* Initial guess is that we should not swap the
+                      first two atoms in each large+small
+                      transition */
+  int didswap; /* Whether swapping was actually done. */
+  int inpdata=0;
+  int encode_ints[3+MAX_SMALL_RLE*3]; /* Up to 3 large + 24 small ints can be encoded at once */
+  int nencode;
+  int ntriplets_left=ntriplets;
+  int refused=0;
+  unsigned char *bwlzh_buf=NULL;
+  int bwlzh_buf_len;
+  unsigned char *base_buf=NULL;
+  int base_buf_len;
+
+  struct xtc3_context xtc3_context;
+  init_xtc3_context(&xtc3_context);
+
+  xtc3_context.maxint[0]=xtc3_context.minint[0]=input[0];
+  xtc3_context.maxint[1]=xtc3_context.minint[1]=input[1];
+  xtc3_context.maxint[2]=xtc3_context.minint[2]=input[2];
+
+  /* Values of speed should be sane. */
+  if (speed<1)
+    speed=1;
+  if (speed>6)
+    speed=6;
+
+#ifdef SHOWIT
+  nbits_sum=0;
+  nvalues_sum=0;
+#endif
+  /* Allocate enough memory for output */
+  if (*length < 48)
+    output=warnmalloc(8*48*sizeof *output);
+  else
+    output=warnmalloc(8* *length*sizeof *output);
+
+
+  for (i=1; i<ntriplets; i++)
+    for (j=0; j<3; j++)
+      {
+        if (input[i*3+j]>xtc3_context.maxint[j])
+          xtc3_context.maxint[j]=input[i*3+j];
+        if (input[i*3+j]<xtc3_context.minint[j])
+          xtc3_context.minint[j]=input[i*3+j];
+      }
+
+  large_index[0]=Ptngc_find_magic_index(xtc3_context.maxint[0]-xtc3_context.minint[0]+1);
+  large_index[1]=Ptngc_find_magic_index(xtc3_context.maxint[1]-xtc3_context.minint[1]+1);
+  large_index[2]=Ptngc_find_magic_index(xtc3_context.maxint[2]-xtc3_context.minint[2]+1);
+  max_large_index=large_index[0];
+  if (large_index[1]>max_large_index)
+    max_large_index=large_index[1];
+  if (large_index[2]>max_large_index)
+    max_large_index=large_index[2];
+
+#ifdef SHOWIT
+  for (j=0; j<3; j++)
+    fprintf(stderr,"minint[%d]=%d. maxint[%d]=%d large_index[%d]=%d value=%d\n",j,xtc3_context.minint[j],j,xtc3_context.maxint[j],
+            j,large_index[j],Ptngc_magic(large_index[j]));
+#endif
+
+  /* Guess initial small index */
+  small_index=max_large_index/2;
+
+  /* Find the largest value that is not large. Not large is half index of
+     large. */
+  max_small=Ptngc_magic(small_index);
+  intmax=0;
+  for (i=0; i<*length; i++)
+    {
+      int item=input[i];
+      int s=positive_int(item);
+      if (s>intmax)
+        if (s<max_small)
+          intmax=s;
+    }
+  /* This value is not critical, since if I guess wrong, the code will
+     just insert instructions to increase this value. */
+  small_index=Ptngc_find_magic_index(intmax);
+#ifdef SHOWIT
+  fprintf(stderr,"initial small_index=%d value=%d\n",small_index,Ptngc_magic(small_index));
+#endif
+
+  output_int(output,&outdata,positive_int(xtc3_context.minint[0]));
+  output_int(output,&outdata,positive_int(xtc3_context.minint[1]));
+  output_int(output,&outdata,positive_int(xtc3_context.minint[2]));
+
+#if 0
+#ifdef SHOWIT
+  for (i=0; i<ntriplets_left; i++)
+    fprintf(stderr,"VALUE:%d %6d %6d %6d\n",
+            i,
+            input[inpdata+i*3],
+            input[inpdata+i*3+1],
+            input[inpdata+i*3+2]);
+#endif
+#endif
+
+  /* Initial prevcoord is the minimum integers. */
+  prevcoord[0]=xtc3_context.minint[0];
+  prevcoord[1]=xtc3_context.minint[1];
+  prevcoord[2]=xtc3_context.minint[2];
+
+  while (ntriplets_left)
+    {
+      if (ntriplets_left<0)
+        {
+          fprintf(stderr,"TRAJNG: BUG! ntriplets_left<0!\n");
+          exit(EXIT_FAILURE);
+        }
+      /* If only less than three atoms left we just write them all as large integers. Here no swapping is done! */
+      if (ntriplets_left<3)
+        {
+          for (ienc=0; ienc<ntriplets_left; ienc++)
+            {
+              buffer_large(&xtc3_context,input,inpdata,natoms,1);
+              inpdata+=3;
+              ntriplets_left--;
+            }
+          flush_large(&xtc3_context,xtc3_context.has_large); /* Flush all */
+        }
+      else
+        {
+          int min_runlength=0;
+          int largest_required_base;
+          int largest_runlength_base;
+          int largest_required_index;
+          int largest_runlength_index;
+          int new_runlength;
+          int new_small_index;
+          int iter_runlength;
+          int iter_small_index;
+          int rle_index_dep;
+          didswap=0;
+          /* Insert the next batch of integers to be encoded into the buffer */
+#ifdef SHOWIT
+          fprintf(stderr,"Initial batch\n");
+#endif
+          insert_batch(input+inpdata,ntriplets_left,prevcoord,encode_ints,0,&nencode);
+
+          /* First we must decide if the next value is large (does not reasonably fit in current small encoding)
+             Also, if we have not written any values yet, we must begin by writing a large atom. */
+          if ((inpdata==0) || (is_quite_large(encode_ints,small_index,max_large_index)) || (refused))
+            {
+              /* If any of the next two atoms are large we should probably write them as large and not swap them */
+              int no_swap=0;
+              if ((is_quite_large(encode_ints+3,small_index,max_large_index)) || (is_quite_large(encode_ints+6,small_index,max_large_index)))
+                no_swap=1;
+#if 1
+              if (!no_swap)
+                {
+                  /* If doing inter-frame coding results in smaller values we should not do any swapping either. */
+                  int frame=inpdata/(natoms*3);
+                  if (frame>0)
+                    {
+                      unsigned int delta[3], delta2[3];
+                      delta[0]=positive_int(input[inpdata+3]-input[inpdata-natoms*3+3]);
+                      delta[1]=positive_int(input[inpdata+4]-input[inpdata-natoms*3+4]);
+                      delta[2]=positive_int(input[inpdata+5]-input[inpdata-natoms*3+5]);
+                      delta2[0]=positive_int(encode_ints[3]);
+                      delta2[1]=positive_int(encode_ints[4]);
+                      delta2[2]=positive_int(encode_ints[5]);
+#ifdef SHOWIT
+                      fprintf(stderr,"A1: inter delta: %u %u %u. intra delta=%u %u %u\n",
+                              delta[0],delta[1],delta[2],
+                              delta2[0],delta2[1],delta2[2]);
+#endif
+                      if (compute_intlen(delta)*TRESHOLD_INTER_INTRA<compute_intlen(delta2))
+                        {
+                          delta[0]=positive_int(input[inpdata+6]-input[inpdata-natoms*3+6]);
+                          delta[1]=positive_int(input[inpdata+7]-input[inpdata-natoms*3+7]);
+                          delta[2]=positive_int(input[inpdata+8]-input[inpdata-natoms*3+8]);
+                          delta2[0]=positive_int(encode_ints[6]);
+                          delta2[1]=positive_int(encode_ints[7]);
+                          delta2[2]=positive_int(encode_ints[8]);
+#ifdef SHOWIT
+                          fprintf(stderr,"A2: inter delta: %u %u %u. intra delta=%u %u %u\n",
+                                  delta[0],delta[1],delta[2],
+                                  delta2[0],delta2[1],delta2[2]);
+#endif
+                          if (compute_intlen(delta)*TRESHOLD_INTER_INTRA<compute_intlen(delta2))
+                            {
+                              no_swap=1;
+#ifdef SHOWIT
+                              fprintf(stderr,"SETTING NO SWAP!\n");
+#endif
+                            }
+                        }
+                    }
+                }
+#endif
+              if (!no_swap)
+                {
+                  /* Next we must decide if we should swap the first
+                     two values. */
+#if 1
+                  swapdecide(&xtc3_context,input+inpdata,&swapatoms,large_index,xtc3_context.minint);
+#else
+                  swapatoms=0;
+#endif
+                  /* If we should do the integer swapping manipulation we should do it now */
+                  if (swapatoms)
+                    {
+                      didswap=1;
+                      for (i=0; i<3; i++)
+                        {
+                          int in[3], out[3];
+                          in[0]=input[inpdata+i];
+                          in[1]=input[inpdata+3+i]-input[inpdata+i];
+                          in[2]=input[inpdata+6+i]-input[inpdata+3+i];
+                          swap_ints(in,out);
+                          encode_ints[i]=out[0];
+                          encode_ints[3+i]=out[1];
+                          encode_ints[6+i]=out[2];
+                        }
+                      /* We have swapped atoms, so the minimum run-length is 2 */
+#ifdef SHOWIT
+                      fprintf(stderr,"Swap atoms results in:\n");
+                      for (i=0; i<3; i++)
+                        fprintf(stderr,"%d: %6d %6d %6d\t\t%6d %6d %6d\n",i*3,
+                                encode_ints[i*3],
+                                encode_ints[i*3+1],
+                                encode_ints[i*3+2],
+                                positive_int(encode_ints[i*3]),
+                                positive_int(encode_ints[i*3+1]),
+                                positive_int(encode_ints[i*3+2]));
+
+#endif
+                      min_runlength=2;
+                    }
+                }
+              /* Cache large value for later possible combination with
+                 a sequence of small integers. */
+              if ((swapatoms) && (didswap))
+                {
+                  buffer_large(&xtc3_context,input,inpdata+3,natoms,0); /* This is a swapped integer, so inpdata is one atom later and
+                                                                           intra coding is not ok. */
+                  for (ienc=0; ienc<3; ienc++)
+                    prevcoord[ienc]=input[inpdata+3+ienc];
+                }
+              else
+                {
+                  buffer_large(&xtc3_context,input,inpdata,natoms,1);
+                  for (ienc=0; ienc<3; ienc++)
+                    prevcoord[ienc]=input[inpdata+ienc];
+                }
+
+
+#ifdef SHOWIT
+              fprintf(stderr,"Prevcoord after packing of large: %d %d %d\n",
+                      prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+
+              /* We have written a large integer so we have one less atoms to worry about */
+              inpdata+=3;
+              ntriplets_left--;
+
+              refused=0;
+
+              /* Insert the next batch of integers to be encoded into the buffer */
+#ifdef SHOWIT
+              fprintf(stderr,"Update batch due to large int.\n");
+#endif
+              if ((swapatoms) && (didswap))
+                {
+                  /* Keep swapped values. */
+                  for (i=0; i<2; i++)
+                    for (ienc=0; ienc<3; ienc++)
+                      encode_ints[i*3+ienc]=encode_ints[(i+1)*3+ienc];
+                }
+              insert_batch(input+inpdata,ntriplets_left,prevcoord,encode_ints,min_runlength,&nencode);
+            }
+          /* Here we should only have differences for the atom coordinates. */
+          /* Convert the ints to positive ints */
+          for (ienc=0; ienc<nencode; ienc++)
+            {
+              int pint=positive_int(encode_ints[ienc]);
+              encode_ints[ienc]=pint;
+            }
+          /* Now we must decide what base and runlength to do. If we have swapped atoms it will be at least 2.
+             If even the next atom is large, we will not do anything. */
+          largest_required_base=0;
+          /* Determine required base */
+          for (ienc=0; ienc<min_runlength*3; ienc++)
+            if (encode_ints[ienc]>largest_required_base)
+              largest_required_base=encode_ints[ienc];
+          /* Also compute what the largest base is for the current runlength setting! */
+          largest_runlength_base=0;
+          for (ienc=0; (ienc<runlength*3) && (ienc<nencode); ienc++)
+            if (encode_ints[ienc]>largest_runlength_base)
+              largest_runlength_base=encode_ints[ienc];
+
+          largest_required_index=Ptngc_find_magic_index(largest_required_base);
+          largest_runlength_index=Ptngc_find_magic_index(largest_runlength_base);
+
+          if (largest_required_index<largest_runlength_index)
+            {
+              new_runlength=min_runlength;
+              new_small_index=largest_required_index;
+            }
+          else
+            {
+              new_runlength=runlength;
+              new_small_index=largest_runlength_index;
+            }
+
+          /* Only allow increase of runlength wrt min_runlength */
+          if (new_runlength<min_runlength)
+            new_runlength=min_runlength;
+
+          /* If the current runlength is longer than the number of
+             triplets left stop it from being so. */
+          if (new_runlength>ntriplets_left)
+            new_runlength=ntriplets_left;
+
+          /* We must at least try to get some small integers going. */
+          if (new_runlength==0)
+            {
+              new_runlength=1;
+              new_small_index=small_index;
+            }
+
+          iter_runlength=new_runlength;
+          iter_small_index=new_small_index;
+
+          /* Iterate to find optimal encoding and runlength */
+#ifdef SHOWIT
+          fprintf(stderr,"Entering iterative loop.\n");
+          fflush(stderr);
+#endif
+
+          do {
+            new_runlength=iter_runlength;
+            new_small_index=iter_small_index;
+
+#ifdef SHOWIT
+            fprintf(stderr,"Test new_small_index=%d Base=%d\n",new_small_index,Ptngc_magic(new_small_index));
+#endif
+            /* What is the largest runlength
+               we can do with the currently
+               selected encoding? Also the max supported runlength is MAX_SMALL_RLE triplets! */
+            for (ienc=0; ienc<nencode && ienc<MAX_SMALL_RLE*3; ienc++)
+              {
+                int test_index=Ptngc_find_magic_index(encode_ints[ienc]);
+                if (test_index>new_small_index)
+                  break;
+              }
+            if (ienc/3>new_runlength)
+              {
+                iter_runlength=ienc/3;
+#ifdef SHOWIT
+                fprintf(stderr,"I found a new possible runlength: %d\n",iter_runlength);
+#endif
+              }
+
+            /* How large encoding do we have to use? */
+            largest_runlength_base=0;
+            for (ienc=0; ienc<iter_runlength*3; ienc++)
+              if (encode_ints[ienc]>largest_runlength_base)
+                largest_runlength_base=encode_ints[ienc];
+            largest_runlength_index=Ptngc_find_magic_index(largest_runlength_base);
+            if (largest_runlength_index!=new_small_index)
+              {
+                iter_small_index=largest_runlength_index;
+#ifdef SHOWIT
+                fprintf(stderr,"I found a new possible small index: %d Base=%d\n",iter_small_index,Ptngc_magic(iter_small_index));
+#endif
+              }
+          } while ((new_runlength!=iter_runlength) ||
+                   (new_small_index!=iter_small_index));
+
+#ifdef SHOWIT
+          fprintf(stderr,"Exit iterative loop.\n");
+          fflush(stderr);
+#endif
+
+          /* Verify that we got something good. We may have caught a
+             substantially larger atom. If so we should just bail
+             out and let the loop get on another lap. We may have a
+             minimum runlength though and then we have to fulfill
+             the request to write out these atoms! */
+          rle_index_dep=0;
+          if (new_runlength<3)
+            rle_index_dep=IS_LARGE;
+          else if (new_runlength<6)
+            rle_index_dep=QUITE_LARGE;
+          if ((min_runlength)
+              || ((new_small_index<small_index+IS_LARGE) && (new_small_index+rle_index_dep<max_large_index))
+#if 1
+              || (new_small_index+IS_LARGE<max_large_index)
+#endif
+)
+            {
+              /* If doing inter-frame coding of large integers results
+                 in smaller values than the small value we should not
+                 produce a sequence of small values here. */
+              int frame=inpdata/(natoms*3);
+              int numsmaller=0;
+#if 1
+              if ((!swapatoms) && (frame>0))
+                {
+                  for (i=0; i<new_runlength; i++)
+                    {
+                      unsigned int delta[3];
+                      unsigned int delta2[3];
+                      delta[0]=positive_int(input[inpdata+i*3]-input[inpdata-natoms*3+i*3]);
+                      delta[1]=positive_int(input[inpdata+i*3+1]-input[inpdata-natoms*3+i*3+1]);
+                      delta[2]=positive_int(input[inpdata+i*3+2]-input[inpdata-natoms*3+i*3+2]);
+                      delta2[0]=positive_int(encode_ints[i*3]);
+                      delta2[1]=positive_int(encode_ints[i*3+1]);
+                      delta2[2]=positive_int(encode_ints[i*3+2]);
+                      if (compute_intlen(delta)*TRESHOLD_INTER_INTRA<compute_intlen(delta2))
+                        numsmaller++;
+                    }
+                }
+#endif
+              /* Most of the values should become smaller, otherwise
+                 we should encode them with intra coding. */
+              if ((!swapatoms) && (numsmaller>=2*new_runlength/3))
+                {
+                  /* Put all the values in large arrays, instead of the small array */
+                  if (new_runlength)
+                    {
+                      for (i=0; i<new_runlength; i++)
+                        buffer_large(&xtc3_context,input,inpdata+i*3,natoms,1);
+                      for (i=0; i<3; i++)
+                        prevcoord[i]=input[inpdata+(new_runlength-1)*3+i];
+                      inpdata+=3*new_runlength;
+                      ntriplets_left-=new_runlength;
+                    }
+                }
+              else
+                {
+                  if ((new_runlength!=runlength) || (new_small_index!=small_index))
+                    {
+                      int change=new_small_index-small_index;
+
+                      if (new_small_index<=0)
+                        change=0;
+
+                      if (change<0)
+                        {
+                          int ixx;
+                          for (ixx=0; ixx<new_runlength; ixx++)
+                            {
+                              int rejected;
+                              do {
+                                int ixyz;
+                                double isum=0.; /* ints can be almost 32 bit so multiplication will overflow. So do doubles. */
+                                for (ixyz=0; ixyz<3; ixyz++)
+                                  {
+                                    /* encode_ints is already positive (and multiplied by 2 versus the original, just as magic ints) */
+                                    double id=encode_ints[ixx*3+ixyz];
+                                    isum+=id*id;
+                                  }
+                                rejected=0;
+#ifdef SHOWIT
+                                fprintf(stderr,"Tested decrease %d of index: %g>=%g?\n",change,isum,(double)Ptngc_magic(small_index+change)*(double)Ptngc_magic(small_index+change));
+#endif
+                                if (isum>(double)Ptngc_magic(small_index+change)*(double)Ptngc_magic(small_index+change))
+                                  {
+#ifdef SHOWIT
+                                    fprintf(stderr,"Rejected decrease %d of index due to length of vector: %g>=%g\n",change,isum,(double)Ptngc_magic(small_index+change)*(double)Ptngc_magic(small_index+change));
+#endif
+                                    rejected=1;
+                                    change++;
+                                  }
+                              } while ((change<0) && (rejected));
+                              if (change==0)
+                                break;
+                            }
+                        }
+
+                      /* Always accept the new small indices here. */
+                      small_index=new_small_index;
+                      /* If we have a new runlength emit it */
+                      if (runlength!=new_runlength)
+                        {
+                          runlength=new_runlength;
+                          insert_value_in_array(&xtc3_context.instructions,
+                                                &xtc3_context.ninstr,
+                                                &xtc3_context.ninstr_alloc,
+                                                INSTR_SMALL_RUNLENGTH,"instr");
+                          insert_value_in_array(&xtc3_context.rle,
+                                                &xtc3_context.nrle,
+                                                &xtc3_context.nrle_alloc,
+                                                (unsigned int)runlength,"rle (small)");
+                        }
+#ifdef SHOWIT
+                      fprintf(stderr,"Current small index: %d Base=%d\n",small_index,Ptngc_magic(small_index));
+#endif
+                    }
+                  /* If we have a large previous integer we can combine it with a sequence of small ints. */
+                  if (xtc3_context.has_large)
+                    {
+                      /* If swapatoms is set to 1 but we did actually not
+                         do any swapping, we must first write out the
+                         large atom and then the small. If swapatoms is 1
+                         and we did swapping we can use the efficient
+                         encoding. */
+                      if ((swapatoms) && (!didswap))
+                        {
+#ifdef SHOWIT
+                          fprintf(stderr,"Swapatoms was set to 1 but we did not do swapping!\n");
+                          fprintf(stderr,"Only one large integer.\n");
+#endif
+                          /* Flush all large atoms. */
+                          flush_large(&xtc3_context,xtc3_context.has_large);
+#ifdef SHOWIT
+                          fprintf(stderr,"Sequence of only small integers.\n");
+#endif
+                          insert_value_in_array(&xtc3_context.instructions,
+                                                &xtc3_context.ninstr,
+                                                &xtc3_context.ninstr_alloc,
+                                                INSTR_ONLY_SMALL,"instr");
+                        }
+                      else
+                        {
+
+#ifdef SHOWIT
+                          fprintf(stderr,"Sequence of one large and small integers (good compression).\n");
+#endif
+                          /* Flush all large atoms but one! */
+                          if (xtc3_context.has_large>1)
+                            flush_large(&xtc3_context,xtc3_context.has_large-1);
+
+                          /* Here we must check if we should emit a large
+                             type change instruction. */
+                          large_instruction_change(&xtc3_context,0);
+
+                          insert_value_in_array(&xtc3_context.instructions,
+                                                &xtc3_context.ninstr,
+                                                &xtc3_context.ninstr_alloc,
+                                                INSTR_DEFAULT,"instr");
+
+                          write_three_large(&xtc3_context,0);
+                          xtc3_context.has_large=0;
+                        }
+                    }
+                  else
+                    {
+#ifdef SHOWIT
+                      fprintf(stderr,"Sequence of only small integers.\n");
+#endif
+                      insert_value_in_array(&xtc3_context.instructions,
+                                            &xtc3_context.ninstr,
+                                            &xtc3_context.ninstr_alloc,
+                                            INSTR_ONLY_SMALL,"instr");
+                    }
+                  /* Insert the small integers into the small integer array. */
+                  for (ienc=0; ienc<runlength*3; ienc++)
+                    insert_value_in_array(&xtc3_context.smallintra,
+                                          &xtc3_context.nsmallintra,
+                                          &xtc3_context.nsmallintra_alloc,
+                                          (unsigned int)encode_ints[ienc],"smallintra");
+
+#ifdef SHOWIT
+                  for (ienc=0; ienc<runlength; ienc++)
+                    fprintf(stderr,"Small: %d %d %d\n",
+                            encode_ints[ienc*3],
+                            encode_ints[ienc*3+1],
+                            encode_ints[ienc*3+2]);
+#endif
+                  /* Update prevcoord. */
+                  for (ienc=0; ienc<runlength; ienc++)
+                    {
+#ifdef SHOWIT
+                      fprintf(stderr,"Prevcoord in packing: %d %d %d\n",
+                              prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+                      prevcoord[0]+=unpositive_int(encode_ints[ienc*3]);
+                      prevcoord[1]+=unpositive_int(encode_ints[ienc*3+1]);
+                      prevcoord[2]+=unpositive_int(encode_ints[ienc*3+2]);
+                    }
+#ifdef SHOWIT
+                  fprintf(stderr,"Prevcoord in packing: %d %d %d\n",
+                          prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+
+                  inpdata+=3*runlength;
+                  ntriplets_left-=runlength;
+#if 1
+                }
+#endif
+            }
+          else
+            {
+#ifdef SHOWIT
+              fprintf(stderr,"Refused value: %d old is %d max is %d\n",new_small_index,small_index,max_large_index);
+              fflush(stderr);
+#endif
+              refused=1;
+            }
+        }
+#ifdef SHOWIT
+      fprintf(stderr,"Number of triplets left is %d\n",ntriplets_left);
+#endif
+    }
+
+  /* If we have large previous integers we must flush them now. */
+  if (xtc3_context.has_large)
+    flush_large(&xtc3_context,xtc3_context.has_large);
+
+  /* Now it is time to compress all the data in the buffers with the bwlzh or base algo. */
+
+#if 0
+  /* Inspect the data. */
+  printarray(xtc3_context.instructions,xtc3_context.ninstr,"A instr");
+  printarray(xtc3_context.rle,xtc3_context.nrle,"A rle");
+  printarray(xtc3_context.large_direct,xtc3_context.nlargedir,"A largedir");
+  printarray(xtc3_context.large_intra_delta,xtc3_context.nlargeintra,"A largeintra");
+  printarray(xtc3_context.large_inter_delta,xtc3_context.nlargeinter,"A largeinter");
+  printarray(xtc3_context.smallintra,xtc3_context.nsmallintra,"A smallintra");
+  exit(0);
+#endif
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+  fprintf(stderr,"instructions: %d\n",xtc3_context.ninstr);
+#endif
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+#define bwlzh_compress bwlzh_compress_verbose
+#define bwlzh_compress_no_lz77 bwlzh_compress_no_lz77_verbose
+#endif
+
+  output_int(output,&outdata,(unsigned int)xtc3_context.ninstr);
+  if (xtc3_context.ninstr)
+    {
+      bwlzh_buf=warnmalloc(bwlzh_get_buflen(xtc3_context.ninstr));
+      if (speed>=5)
+        bwlzh_compress(xtc3_context.instructions,xtc3_context.ninstr,bwlzh_buf,&bwlzh_buf_len);
+      else
+        bwlzh_compress_no_lz77(xtc3_context.instructions,xtc3_context.ninstr,bwlzh_buf,&bwlzh_buf_len);
+      output_int(output,&outdata,(unsigned int)bwlzh_buf_len);
+      memcpy(output+outdata,bwlzh_buf,bwlzh_buf_len);
+      outdata+=bwlzh_buf_len;
+      free(bwlzh_buf);
+    }
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+  fprintf(stderr,"rle: %d\n",xtc3_context.nrle);
+#endif
+
+  output_int(output,&outdata,(unsigned int)xtc3_context.nrle);
+  if (xtc3_context.nrle)
+    {
+      bwlzh_buf=warnmalloc(bwlzh_get_buflen(xtc3_context.nrle));
+      if (speed>=5)
+        bwlzh_compress(xtc3_context.rle,xtc3_context.nrle,bwlzh_buf,&bwlzh_buf_len);
+      else
+        bwlzh_compress_no_lz77(xtc3_context.rle,xtc3_context.nrle,bwlzh_buf,&bwlzh_buf_len);
+      output_int(output,&outdata,(unsigned int)bwlzh_buf_len);
+      memcpy(output+outdata,bwlzh_buf,bwlzh_buf_len);
+      outdata+=bwlzh_buf_len;
+      free(bwlzh_buf);
+    }
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+  fprintf(stderr,"large direct: %d\n",xtc3_context.nlargedir);
+#endif
+
+  output_int(output,&outdata,(unsigned int)xtc3_context.nlargedir);
+  if (xtc3_context.nlargedir)
+    {
+      if ((speed<=2) || ((speed<=5) && (!heuristic_bwlzh(xtc3_context.large_direct,xtc3_context.nlargedir))))
+        {
+          bwlzh_buf=NULL;
+          bwlzh_buf_len=INT_MAX;
+        }
+      else
+        {
+          bwlzh_buf=warnmalloc(bwlzh_get_buflen(xtc3_context.nlargedir));
+          if (speed>=5)
+            bwlzh_compress(xtc3_context.large_direct,xtc3_context.nlargedir,bwlzh_buf,&bwlzh_buf_len);
+          else
+            bwlzh_compress_no_lz77(xtc3_context.large_direct,xtc3_context.nlargedir,bwlzh_buf,&bwlzh_buf_len);
+        }
+      /* If this can be written smaller using base compression we should do that. */
+      base_buf=warnmalloc((xtc3_context.nlargedir+3)*sizeof(int));
+      base_compress(xtc3_context.large_direct,xtc3_context.nlargedir,base_buf,&base_buf_len);
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+      fprintf(stderr,"Large direct: Base len=%d. BWLZH len=%d\n",base_buf_len,bwlzh_buf_len);
+#endif
+      if (base_buf_len<bwlzh_buf_len)
+        {
+          output[outdata++]=0U;
+          output_int(output,&outdata,(unsigned int)base_buf_len);
+          memcpy(output+outdata,base_buf,base_buf_len);
+          outdata+=base_buf_len;
+        }
+      else
+        {
+          output[outdata++]=1U;
+          output_int(output,&outdata,(unsigned int)bwlzh_buf_len);
+          memcpy(output+outdata,bwlzh_buf,bwlzh_buf_len);
+          outdata+=bwlzh_buf_len;
+        }
+      free(bwlzh_buf);
+      free(base_buf);
+    }
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+  fprintf(stderr,"large intra: %d\n",xtc3_context.nlargeintra);
+#endif
+
+  output_int(output,&outdata,(unsigned int)xtc3_context.nlargeintra);
+  if (xtc3_context.nlargeintra)
+    {
+      if ((speed<=2) || ((speed<=5) && (!heuristic_bwlzh(xtc3_context.large_intra_delta,xtc3_context.nlargeintra))))
+        {
+          bwlzh_buf=NULL;
+          bwlzh_buf_len=INT_MAX;
+        }
+      else
+        {
+          bwlzh_buf=warnmalloc(bwlzh_get_buflen(xtc3_context.nlargeintra));
+          if (speed>=5)
+            bwlzh_compress(xtc3_context.large_intra_delta,xtc3_context.nlargeintra,bwlzh_buf,&bwlzh_buf_len);
+          else
+            bwlzh_compress_no_lz77(xtc3_context.large_intra_delta,xtc3_context.nlargeintra,bwlzh_buf,&bwlzh_buf_len);
+        }
+      /* If this can be written smaller using base compression we should do that. */
+      base_buf=warnmalloc((xtc3_context.nlargeintra+3)*sizeof(int));
+      base_compress(xtc3_context.large_intra_delta,xtc3_context.nlargeintra,base_buf,&base_buf_len);
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+      fprintf(stderr,"Large intra: Base len=%d. BWLZH len=%d\n",base_buf_len,bwlzh_buf_len);
+#endif
+      if (base_buf_len<bwlzh_buf_len)
+        {
+          output[outdata++]=0U;
+          output_int(output,&outdata,(unsigned int)base_buf_len);
+          memcpy(output+outdata,base_buf,base_buf_len);
+          outdata+=base_buf_len;
+        }
+      else
+        {
+          output[outdata++]=1U;
+          output_int(output,&outdata,(unsigned int)bwlzh_buf_len);
+          memcpy(output+outdata,bwlzh_buf,bwlzh_buf_len);
+          outdata+=bwlzh_buf_len;
+        }
+      free(bwlzh_buf);
+      free(base_buf);
+    }
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+  fprintf(stderr,"large inter: %d\n",xtc3_context.nlargeinter);
+#endif
+
+  output_int(output,&outdata,(unsigned int)xtc3_context.nlargeinter);
+  if (xtc3_context.nlargeinter)
+    {
+      if ((speed<=2) || ((speed<=5) && (!heuristic_bwlzh(xtc3_context.large_inter_delta,xtc3_context.nlargeinter))))
+        {
+          bwlzh_buf=NULL;
+          bwlzh_buf_len=INT_MAX;
+        }
+      else
+        {
+          bwlzh_buf=warnmalloc(bwlzh_get_buflen(xtc3_context.nlargeinter));
+          if (speed>=5)
+            bwlzh_compress(xtc3_context.large_inter_delta,xtc3_context.nlargeinter,bwlzh_buf,&bwlzh_buf_len);
+          else
+            bwlzh_compress_no_lz77(xtc3_context.large_inter_delta,xtc3_context.nlargeinter,bwlzh_buf,&bwlzh_buf_len);
+        }
+      /* If this can be written smaller using base compression we should do that. */
+      base_buf=warnmalloc((xtc3_context.nlargeinter+3)*sizeof(int));
+      base_compress(xtc3_context.large_inter_delta,xtc3_context.nlargeinter,base_buf,&base_buf_len);
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+      fprintf(stderr,"Large inter: Base len=%d. BWLZH len=%d\n",base_buf_len,bwlzh_buf_len);
+#endif
+      if (base_buf_len<bwlzh_buf_len)
+        {
+          output[outdata++]=0U;
+          output_int(output,&outdata,(unsigned int)base_buf_len);
+          memcpy(output+outdata,base_buf,base_buf_len);
+          outdata+=base_buf_len;
+        }
+      else
+        {
+          output[outdata++]=1U;
+          output_int(output,&outdata,(unsigned int)bwlzh_buf_len);
+          memcpy(output+outdata,bwlzh_buf,bwlzh_buf_len);
+          outdata+=bwlzh_buf_len;
+        }
+      free(bwlzh_buf);
+      free(base_buf);
+    }
+
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+  fprintf(stderr,"small intra: %d\n",xtc3_context.nsmallintra);
+#endif
+
+  output_int(output,&outdata,(unsigned int)xtc3_context.nsmallintra);
+  if (xtc3_context.nsmallintra)
+    {
+      if ((speed<=2) || ((speed<=5) && (!heuristic_bwlzh(xtc3_context.smallintra,xtc3_context.nsmallintra))))
+        {
+          bwlzh_buf=NULL;
+          bwlzh_buf_len=INT_MAX;
+        }
+      else
+        {
+          bwlzh_buf=warnmalloc(bwlzh_get_buflen(xtc3_context.nsmallintra));
+          if (speed>=5)
+            bwlzh_compress(xtc3_context.smallintra,xtc3_context.nsmallintra,bwlzh_buf,&bwlzh_buf_len);
+          else
+            bwlzh_compress_no_lz77(xtc3_context.smallintra,xtc3_context.nsmallintra,bwlzh_buf,&bwlzh_buf_len);
+        }
+      /* If this can be written smaller using base compression we should do that. */
+      base_buf=warnmalloc((xtc3_context.nsmallintra+3)*sizeof(int));
+      base_compress(xtc3_context.smallintra,xtc3_context.nsmallintra,base_buf,&base_buf_len);
+#if defined(SHOWIT) || defined(SHOWIT_LIGHT)
+      fprintf(stderr,"Small intra: Base len=%d. BWLZH len=%d\n",base_buf_len,bwlzh_buf_len);
+#endif
+      if (base_buf_len<bwlzh_buf_len)
+        {
+          output[outdata++]=0U;
+          output_int(output,&outdata,(unsigned int)base_buf_len);
+          memcpy(output+outdata,base_buf,base_buf_len);
+          outdata+=base_buf_len;
+        }
+      else
+        {
+          output[outdata++]=1U;
+          output_int(output,&outdata,(unsigned int)bwlzh_buf_len);
+          memcpy(output+outdata,bwlzh_buf,bwlzh_buf_len);
+          outdata+=bwlzh_buf_len;
+        }
+      free(bwlzh_buf);
+      free(base_buf);
+    }
+  *length=outdata;
+
+  free_xtc3_context(&xtc3_context);
+  return output;
+}
+
+static void decompress_bwlzh_block(unsigned char **ptr,
+                                   int nvals,
+                                   unsigned int **vals)
+{
+  int bwlzh_buf_len=(int)(((unsigned int)(*ptr)[0]) |
+                          (((unsigned int)(*ptr)[1])<<8) |
+                          (((unsigned int)(*ptr)[2])<<16) |
+                          (((unsigned int)(*ptr)[3])<<24));
+  (*ptr)+=4;
+  *vals=warnmalloc(nvals*sizeof (**vals));
+  bwlzh_decompress(*ptr,nvals,*vals);
+  (*ptr)+=bwlzh_buf_len;
+}
+
+static void decompress_base_block(unsigned char **ptr,
+                                  int nvals,
+                                  unsigned int **vals)
+{
+  int base_buf_len=(int)(((unsigned int)(*ptr)[0]) |
+                         (((unsigned int)(*ptr)[1])<<8) |
+                         (((unsigned int)(*ptr)[2])<<16) |
+                         (((unsigned int)(*ptr)[3])<<24));
+  (*ptr)+=4;
+  *vals=warnmalloc(nvals*sizeof (**vals));
+  base_decompress(*ptr,nvals,*vals);
+  (*ptr)+=base_buf_len;
+}
+
+static void unpack_one_large(struct xtc3_context *xtc3_context,
+                             int *ilargedir, int *ilargeintra,
+                             int *ilargeinter, int *prevcoord,
+                             int *minint, int *output,
+                             int outdata, int didswap,
+                             int natoms, int current_large_type)
+{
+  int large_ints[3]={0,0,0};
+  if (current_large_type==0 && xtc3_context->large_direct)
+    {
+      large_ints[0]=(int)xtc3_context->large_direct[(*ilargedir)]+minint[0];
+      large_ints[1]=(int)xtc3_context->large_direct[(*ilargedir)+1]+minint[1];
+      large_ints[2]=(int)xtc3_context->large_direct[(*ilargedir)+2]+minint[2];
+      (*ilargedir)+=3;
+    }
+  else if (current_large_type==1 && xtc3_context->large_intra_delta)
+    {
+      large_ints[0]=unpositive_int(xtc3_context->large_intra_delta[(*ilargeintra)])+prevcoord[0];
+      large_ints[1]=unpositive_int(xtc3_context->large_intra_delta[(*ilargeintra)+1])+prevcoord[1];
+      large_ints[2]=unpositive_int(xtc3_context->large_intra_delta[(*ilargeintra)+2])+prevcoord[2];
+      (*ilargeintra)+=3;
+    }
+  else if (xtc3_context->large_inter_delta)
+    {
+      large_ints[0]=unpositive_int(xtc3_context->large_inter_delta[(*ilargeinter)])
+        +output[outdata-natoms*3+didswap*3];
+      large_ints[1]=unpositive_int(xtc3_context->large_inter_delta[(*ilargeinter)+1])
+        +output[outdata-natoms*3+1+didswap*3];
+      large_ints[2]=unpositive_int(xtc3_context->large_inter_delta[(*ilargeinter)+2])
+        +output[outdata-natoms*3+2+didswap*3];
+      (*ilargeinter)+=3;
+    }
+  prevcoord[0]=large_ints[0];
+  prevcoord[1]=large_ints[1];
+  prevcoord[2]=large_ints[2];
+  output[outdata]=large_ints[0];
+  output[outdata+1]=large_ints[1];
+  output[outdata+2]=large_ints[2];
+#ifdef SHOWIT
+  fprintf(stderr,"Unpack one large: %d %d %d\n",prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+}
+
+
+int Ptngc_unpack_array_xtc3(unsigned char *packed,int *output, int length, int natoms)
+{
+  int i;
+  int minint[3];
+  unsigned char *ptr=packed;
+  int prevcoord[3];
+  int outdata=0;
+  int ntriplets_left=length/3;
+  int swapatoms=0;
+  int runlength=0;
+  int current_large_type=0;
+  int iinstr=0;
+  int irle=0;
+  int ilargedir=0;
+  int ilargeintra=0;
+  int ilargeinter=0;
+  int ismallintra=0;
+
+  struct xtc3_context xtc3_context;
+  init_xtc3_context(&xtc3_context);
+
+  for (i=0; i<3; i++)
+    {
+      minint[i]=unpositive_int((int)(((unsigned int)ptr[0]) |
+                                     (((unsigned int)ptr[1])<<8) |
+                                     (((unsigned int)ptr[2])<<16) |
+                                     (((unsigned int)ptr[3])<<24)));
+      ptr+=4;
+    }
+
+  xtc3_context.ninstr=(int)(((unsigned int)ptr[0]) |
+                            (((unsigned int)ptr[1])<<8) |
+                            (((unsigned int)ptr[2])<<16) |
+                            (((unsigned int)ptr[3])<<24));
+  ptr+=4;
+  if (xtc3_context.ninstr)
+    decompress_bwlzh_block(&ptr,xtc3_context.ninstr,&xtc3_context.instructions);
+
+  xtc3_context.nrle=(int)(((unsigned int)ptr[0]) |
+                          (((unsigned int)ptr[1])<<8) |
+                          (((unsigned int)ptr[2])<<16) |
+                          (((unsigned int)ptr[3])<<24));
+  ptr+=4;
+  if (xtc3_context.nrle)
+    decompress_bwlzh_block(&ptr,xtc3_context.nrle,&xtc3_context.rle);
+
+  xtc3_context.nlargedir=(int)(((unsigned int)ptr[0]) |
+                               (((unsigned int)ptr[1])<<8) |
+                               (((unsigned int)ptr[2])<<16) |
+                               (((unsigned int)ptr[3])<<24));
+  ptr+=4;
+  if (xtc3_context.nlargedir)
+    {
+      if (*ptr++==1)
+        decompress_bwlzh_block(&ptr,xtc3_context.nlargedir,&xtc3_context.large_direct);
+      else
+        decompress_base_block(&ptr,xtc3_context.nlargedir,&xtc3_context.large_direct);
+    }
+
+  xtc3_context.nlargeintra=(int)(((unsigned int)ptr[0]) |
+                                 (((unsigned int)ptr[1])<<8) |
+                                 (((unsigned int)ptr[2])<<16) |
+                                 (((unsigned int)ptr[3])<<24));
+  ptr+=4;
+  if (xtc3_context.nlargeintra)
+    {
+      if (*ptr++==1)
+        decompress_bwlzh_block(&ptr,xtc3_context.nlargeintra,&xtc3_context.large_intra_delta);
+      else
+        decompress_base_block(&ptr,xtc3_context.nlargeintra,&xtc3_context.large_intra_delta);
+    }
+
+  xtc3_context.nlargeinter=(int)(((unsigned int)ptr[0]) |
+                                 (((unsigned int)ptr[1])<<8) |
+                                 (((unsigned int)ptr[2])<<16) |
+                                 (((unsigned int)ptr[3])<<24));
+  ptr+=4;
+  if (xtc3_context.nlargeinter)
+    {
+      if (*ptr++==1)
+        decompress_bwlzh_block(&ptr,xtc3_context.nlargeinter,&xtc3_context.large_inter_delta);
+      else
+        decompress_base_block(&ptr,xtc3_context.nlargeinter,&xtc3_context.large_inter_delta);
+    }
+
+  xtc3_context.nsmallintra=(int)(((unsigned int)ptr[0]) |
+                                 (((unsigned int)ptr[1])<<8) |
+                                 (((unsigned int)ptr[2])<<16) |
+                                 (((unsigned int)ptr[3])<<24));
+  ptr+=4;
+  if (xtc3_context.nsmallintra)
+    {
+      if (*ptr++==1)
+        decompress_bwlzh_block(&ptr,xtc3_context.nsmallintra,&xtc3_context.smallintra);
+      else
+        decompress_base_block(&ptr,xtc3_context.nsmallintra,&xtc3_context.smallintra);
+    }
+
+  /* Initial prevcoord is the minimum integers. */
+  prevcoord[0]=minint[0];
+  prevcoord[1]=minint[1];
+  prevcoord[2]=minint[2];
+
+  while (ntriplets_left>0 && iinstr<xtc3_context.ninstr)
+    {
+      int instr=xtc3_context.instructions[iinstr++];
+#ifdef SHOWIT
+      fprintf(stderr,"instr=%d @ %d\n",instr,iinstr-1);
+#endif
+#ifdef SHOWIT
+      fprintf(stderr,"ntriplets left=%d\n",ntriplets_left);
+#endif
+      if ((instr==INSTR_DEFAULT) /* large+small */
+          || (instr==INSTR_ONLY_LARGE) /* only large */
+          || (instr==INSTR_ONLY_SMALL)) /* only small */
+        {
+          if (instr!=INSTR_ONLY_SMALL)
+            {
+              int didswap=0;
+              if ((instr==INSTR_DEFAULT) && (swapatoms))
+                didswap=1;
+              unpack_one_large(&xtc3_context,&ilargedir, &ilargeintra, &ilargeinter,
+                               prevcoord, minint, output, outdata, didswap,
+                               natoms, current_large_type);
+              ntriplets_left--;
+              outdata+=3;
+            }
+          if (instr!=INSTR_ONLY_LARGE)
+            {
+              for (i=0; i<runlength; i++)
+                {
+                  prevcoord[0]+=unpositive_int(xtc3_context.smallintra[ismallintra]);
+                  prevcoord[1]+=unpositive_int(xtc3_context.smallintra[ismallintra+1]);
+                  prevcoord[2]+=unpositive_int(xtc3_context.smallintra[ismallintra+2]);
+                  ismallintra+=3;
+                  output[outdata+i*3]=prevcoord[0];
+                  output[outdata+i*3+1]=prevcoord[1];
+                  output[outdata+i*3+2]=prevcoord[2];
+#ifdef SHOWIT
+                  fprintf(stderr,"Unpack small: %d %d %d\n",prevcoord[0],prevcoord[1],prevcoord[2]);
+#endif
+                }
+              if ((instr==INSTR_DEFAULT) && (swapatoms))
+                {
+                  for (i=0; i<3; i++)
+                    {
+                      int tmp=output[outdata-3+i];
+                      output[outdata-3+i]=output[outdata+i];
+                      output[outdata+i]=tmp;
+                    }
+#ifdef SHOWIT
+                  fprintf(stderr,"Unswap results in\n");
+                  for (i=0; i<3; i++)
+                    fprintf(stderr,"%d %d %d\n",output[outdata-3+i*3],output[outdata-2+i*3],output[outdata-1+i*3]);
+#endif
+                }
+              ntriplets_left-=runlength;
+              outdata+=runlength*3;
+            }
+        }
+      else if (instr==INSTR_LARGE_RLE && irle<xtc3_context.nrle)
+        {
+          int large_rle=xtc3_context.rle[irle++];
+#ifdef SHOWIT
+          fprintf(stderr,"large_rle=%d @ %d\n",large_rle,irle-1);
+#endif
+          for (i=0; i<large_rle; i++)
+            {
+              unpack_one_large(&xtc3_context,&ilargedir, &ilargeintra, &ilargeinter,
+                               prevcoord, minint, output, outdata, 0,
+                               natoms, current_large_type);
+              ntriplets_left--;
+              outdata+=3;
+            }
+        }
+      else if (instr==INSTR_SMALL_RUNLENGTH && irle<xtc3_context.nrle)
+        {
+          runlength=xtc3_context.rle[irle++];
+#ifdef SHOWIT
+          fprintf(stderr,"small_rle=%d @ %d\n",runlength,irle-1);
+#endif
+        }
+      else if (instr==INSTR_FLIP)
+        {
+          swapatoms=1-swapatoms;
+#ifdef SHOWIT
+          fprintf(stderr,"new flip=%d\n",swapatoms);
+#endif
+        }
+      else if (instr==INSTR_LARGE_DIRECT)
+        {
+          current_large_type=0;
+#ifdef SHOWIT
+          fprintf(stderr,"large direct\n");
+#endif
+        }
+      else if (instr==INSTR_LARGE_INTRA_DELTA)
+        {
+          current_large_type=1;
+#ifdef SHOWIT
+          fprintf(stderr,"large intra delta\n");
+#endif
+        }
+      else if (instr==INSTR_LARGE_INTER_DELTA)
+        {
+          current_large_type=2;
+#ifdef SHOWIT
+          fprintf(stderr,"large inter delta\n");
+#endif
+        }
+    }
+  if (ntriplets_left<0)
+    {
+      fprintf(stderr,"TRAJNG XTC3: A bug has been found. At end ntriplets_left<0\n");
+      exit(EXIT_FAILURE);
+    }
+  free_xtc3_context(&xtc3_context);
+  return 0;
+}
diff --git a/src/external/tng_io/src/lib/CMakeLists.txt b/src/external/tng_io/src/lib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..45e13d2
--- /dev/null
@@ -0,0 +1,31 @@
+if(TNG_BUILD_FORTRAN)
+  add_library(tng_io tng_io.c md5.c tng_io_fortran.c)
+else()
+  add_library(tng_io tng_io.c md5.c)
+endif()
+
+set_property(TARGET tng_io PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
+set_property(TARGET tng_io PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
+
+install(TARGETS tng_io
+        LIBRARY DESTINATION lib
+        ARCHIVE DESTINATION lib)
+
+if(HAVE_INTTYPES_H)
+  set_property(TARGET tng_io APPEND PROPERTY COMPILE_DEFINITIONS USE_STD_INTTYPES_H)
+endif()
+
+# This test is for md5. The TNG library itself determines the actual byte order -
+# not just if it is small or big endian.
+include(TestBigEndian)
+test_big_endian(TNG_INTEGER_BIG_ENDIAN)
+if(TNG_INTEGER_BIG_ENDIAN)
+  set_property(TARGET tng_io APPEND PROPERTY COMPILE_DEFINITIONS TNG_INTEGER_BIG_ENDIAN)
+endif()
+
+if(ZLIB_FOUND)
+  set_property(TARGET tng_io APPEND PROPERTY COMPILE_DEFINITIONS USE_ZLIB)
+  target_link_libraries(tng_io tng_compress ${ZLIB_LIBRARIES})
+else()
+  target_link_libraries(tng_io tng_compress)
+endif()
diff --git a/src/external/tng_io/src/lib/md5.c b/src/external/tng_io/src/lib/md5.c
new file mode 100644 (file)
index 0000000..ea8bb37
--- /dev/null
@@ -0,0 +1,406 @@
+/* This file has been modified in the TNG library distribution. Modifications
+ * are marked below. */
+
+/* The define below was added in the TNG library distribution of this file. */
+#ifdef TNG_INTEGER_BIG_ENDIAN
+#define ARCH_IS_BIG_ENDIAN 1
+#else
+#define ARCH_IS_BIG_ENDIAN 0
+#endif
+
+/* The defines below were added in TNG library distribution of this file
+ * in order to compile properly in MSVC */
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifdef USE_WINDOWS
+#define TNG_INLINE __inline
+#else
+#define TNG_INLINE inline
+#endif
+
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+       either statically or dynamically; added missing #include <string.h>
+       in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+       type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+       unsigned in ANSI C, signed in traditional"; made test program
+       self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "../../include/md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER      /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+/* In the TNG library inline has been changed to TNG_INLINE */
+static TNG_INLINE void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+       a = pms->abcd[0], b = pms->abcd[1],
+       c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    /* cppcheck-suppress unassignedVariable */
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+       /*
+        * Determine dynamically whether this is a big-endian or
+        * little-endian machine, since we can use a more efficient
+        * algorithm on the latter.
+        */
+       static const int w = 1;
+
+       if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0            /* little-endian */
+       {
+           /*
+            * On little-endian machines, we can process properly aligned
+            * data without copying it.
+            */
+           if (!((data - (const md5_byte_t *)0) & 3)) {
+               /* data are properly aligned */
+               X = (const md5_word_t *)data;
+           } else {
+               /* not aligned */
+               memcpy(xbuf, data, 64);
+               X = xbuf;
+           }
+       }
+#endif
+#if BYTE_ORDER == 0
+       else                    /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0            /* big-endian */
+       {
+           /*
+            * On big-endian machines, we must arrange the bytes in the
+            * right order.
+            */
+           const md5_byte_t *xp = data;
+           int i;
+
+#  if BYTE_ORDER == 0
+           X = xbuf;           /* (dynamic only) */
+#  else
+#    define xbuf X             /* (static only) */
+#  endif
+           for (i = 0; i < 16; ++i, xp += 4)
+               xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+       }
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+       return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+       pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+       int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+       memcpy(pms->buf + offset, p, copy);
+       if (offset + copy < 64)
+           return;
+       p += copy;
+       left -= copy;
+       md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+       md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+       memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+       data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+       digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/src/external/tng_io/src/lib/tng_io.c b/src/external/tng_io/src/lib/tng_io.c
new file mode 100644 (file)
index 0000000..4b81312
--- /dev/null
@@ -0,0 +1,18575 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.4
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#ifdef USE_ZLIB
+#include <zlib.h>
+#endif
+
+#include "../../include/tng_io.h"
+#include "../../include/md5.h"
+#include "../../include/compression/tng_compress.h"
+
+
+struct tng_bond {
+    /** One of the atoms of the bond */
+    int64_t from_atom_id;
+    /** The other atom of the bond */
+    int64_t to_atom_id;
+};
+
+struct tng_atom {
+    /** The residue containing this atom */
+    tng_residue_t residue;
+    /** A unique (per molecule) ID number of the atom */
+    int64_t id;
+    /** The atom_type (depending on the forcefield) */
+    char *atom_type;
+    /** The name of the atom */
+    char *name;
+};
+
+struct tng_residue {
+    /** The chain containing this residue */
+    tng_chain_t chain;
+    /** A unique (per chain) ID number of the residue */
+    int64_t id;
+    /** The name of the residue */
+    char *name;
+    /** The number of atoms in the residue */
+    int64_t n_atoms;
+    /** A list of atoms in the residue */
+    int64_t atoms_offset;
+};
+
+struct tng_chain {
+    /** The molecule containing this chain */
+    tng_molecule_t molecule;
+    /** A unique (per molecule) ID number of the chain */
+    int64_t id;
+    /** The name of the chain */
+    char *name;
+    /** The number of residues in the chain */
+    int64_t n_residues;
+    /** A list of residues in the chain */
+    tng_residue_t residues;
+};
+
+struct tng_molecule {
+    /** A unique ID number of the molecule */
+    int64_t id;
+    /** Quaternary structure of the molecule.
+     *  1 => monomeric
+     *  2 => dimeric
+     *  3 => trimeric
+     *  etc */
+    int64_t quaternary_str;
+    /** The number of chains in the molecule */
+    int64_t n_chains;
+    /** The number of residues in the molecule */
+    int64_t n_residues;
+    /** The number of atoms in the molecule */
+    int64_t n_atoms;
+    /** The number of bonds in the molecule. If the bonds are not specified this
+     * value can be 0. */
+    int64_t n_bonds;
+    /** The name of the molecule */
+    char *name;
+    /** A list of chains in the molecule */
+    tng_chain_t chains;
+    /** A list of residues in the molecule */
+    tng_residue_t residues;
+    /** A list of the atoms in the molecule */
+    tng_atom_t atoms;
+    /** A list of the bonds in the molecule */
+    tng_bond_t bonds;
+};
+
+struct tng_gen_block {
+    /** The size of the block header in bytes */
+    int64_t header_contents_size;
+    /** The size of the block contents in bytes */
+    int64_t block_contents_size;
+    /** The ID of the block to determine its type */
+    int64_t id;
+    /** The MD5 hash of the block to verify integrity */
+    char md5_hash[TNG_MD5_HASH_LEN];
+    /** The name of the block */
+    char *name;
+    /** The library version used to write the block */
+    int64_t block_version;
+    int64_t alt_hash_type;
+    int64_t alt_hash_len;
+    char *alt_hash;
+    int64_t signature_type;
+    int64_t signature_len;
+    char *signature;
+    /** The full block header contents */
+    char *header_contents;
+    /** The full block contents */
+    char *block_contents;
+};
+
+struct tng_particle_mapping {
+    /** The index number of the first particle in this mapping block */
+    int64_t num_first_particle;
+    /** The number of particles list in this mapping block */
+    int64_t n_particles;
+    /** the mapping of index numbers to the real particle numbers in the
+     * trajectory. real_particle_numbers[0] is the real particle number
+     * (as it is numbered in the molecular system) of the first particle
+     * in the data blocks covered by this particle mapping block */
+    int64_t *real_particle_numbers;
+};
+
+struct tng_trajectory_frame_set {
+    /** The number of different particle mapping blocks present. */
+    int64_t n_mapping_blocks;
+    /** The atom mappings of this frame set */
+    struct tng_particle_mapping *mappings;
+    /** The first frame of this frame set */
+    int64_t first_frame;
+    /** The number of frames in this frame set */
+    int64_t n_frames;
+    /** The number of written frames in this frame set (used when writing one
+     * frame at a time). */
+    int64_t n_written_frames;
+    /** The number of frames not yet written to file in this frame set
+     * (used from the utility functions to finish the writing properly. */
+    int64_t n_unwritten_frames;
+
+
+    /** A list of the number of each molecule type - only used when using
+     * variable number of atoms */
+    int64_t *molecule_cnt_list;
+    /** The number of particles/atoms - only used when using variable number
+     * of atoms */
+    int64_t n_particles;
+    /** The file position of the next frame set */
+    int64_t next_frame_set_file_pos;
+    /** The file position of the previous frame set */
+    int64_t prev_frame_set_file_pos;
+    /** The file position of the frame set one long stride step ahead */
+    int64_t medium_stride_next_frame_set_file_pos;
+    /** The file position of the frame set one long stride step behind */
+    int64_t medium_stride_prev_frame_set_file_pos;
+    /** The file position of the frame set one long stride step ahead */
+    int64_t long_stride_next_frame_set_file_pos;
+    /** The file position of the frame set one long stride step behind */
+    int64_t long_stride_prev_frame_set_file_pos;
+    /** Time stamp (in seconds) of first frame in frame set */
+    double first_frame_time;
+
+    /* The data blocks in a frame set are trajectory data blocks */
+    /** The number of trajectory data blocks of particle dependent data */
+    int n_particle_data_blocks;
+    /** A list of data blocks containing particle dependent data */
+    struct tng_particle_data *tr_particle_data;
+    /** The number of trajectory data blocks independent of particles */
+    int n_data_blocks;
+    /** A list of data blocks containing particle indepdendent data */
+    struct tng_non_particle_data *tr_data;
+};
+
+/* FIXME: Should there be a pointer to a tng_gen_block from each data block? */
+struct tng_particle_data {
+    /** The block ID of the data block containing this particle data.
+     *  This is used to determine the kind of data that is stored */
+    int64_t block_id;
+    /** The name of the data block. This is used to determine the kind of
+     *  data that is stored */
+    char *block_name;
+    /** The type of data stored. */
+    char datatype;
+    /** The frame number of the first data value */
+    int64_t first_frame_with_data;
+    /** The number of frames in this frame set */
+    int64_t n_frames;
+    /** The number of values stored per frame */
+    int64_t n_values_per_frame;
+    /** The number of frames between each data point - e.g. when
+     *  storing sparse data. */
+    int64_t stride_length;
+    /** ID of the CODEC used for compression 0 == no compression. */
+    int64_t codec_id;
+    /** If reading one frame at a time this is the last read frame */
+    int64_t last_retrieved_frame;
+    /** The multiplier used for getting integer values for compression */
+    double compression_multiplier;
+    /** A 1-dimensional array of values of length
+     *  [sizeof (datatype)] * n_frames * n_particles * n_values_per_frame */
+    void *values;
+    /** If storing character data store it in a 3-dimensional array */
+    char ****strings;
+};
+
+struct tng_non_particle_data {
+    /** The ID of the data block */
+    int64_t block_id;
+    /** The name of the data block. This is used to determine the kind of
+     *  data that is stored */
+    char *block_name;
+    /** The type of data stored. */
+    char datatype;
+    /** The first frame number of the first data value */
+    int64_t first_frame_with_data;
+    /** The number of frames in this data block */
+    int64_t n_frames;
+    /** The number of values stored per frame */
+    int64_t n_values_per_frame;
+    /** The number of frames between each data value, e.g. if storing data
+     *  that is not saved every frame. */
+    int64_t stride_length;
+    /** ID of the CODEC used for compression. 0 == no compression. */
+    int64_t codec_id;
+    /** If reading one frame at a time this is the last read frame */
+    int64_t last_retrieved_frame;
+    /** Compressed data is stored as integers. This compression multiplier is
+     *  the multiplication factor to convert from integer to float/double */
+    double compression_multiplier;
+    /** A 1-dimensional array of values of length
+     *  [sizeof (datatype)] * n_frames * n_values_per_frame */
+    void *values;
+    /** If storing character data store it in a 3-dimensional array */
+    char ***strings;
+};
+
+
+
+struct tng_trajectory {
+    /** The path of the input trajectory file */
+    char *input_file_path;
+    /** A handle to the input file */
+    FILE *input_file;
+    /** The length of the input file */
+    long input_file_len;
+    /** The path of the output trajectory file */
+    char *output_file_path;
+    /** A handle to the output file */
+    FILE *output_file;
+    /** Function to swap 32 bit values to and from the endianness of the
+     * input file */
+    tng_function_status (*input_endianness_swap_func_32)(const tng_trajectory_t, int32_t *);
+    /** Function to swap 64 bit values to and from the endianness of the
+     * input file */
+    tng_function_status (*input_endianness_swap_func_64)(const tng_trajectory_t, int64_t *);
+    /** Function to swap 32 bit values to and from the endianness of the
+     * input file */
+    tng_function_status (*output_endianness_swap_func_32)(const tng_trajectory_t, int32_t *);
+    /** Function to swap 64 bit values to and from the endianness of the
+     * input file */
+    tng_function_status (*output_endianness_swap_func_64)(const tng_trajectory_t, int64_t *);
+    /** The endianness of 32 bit values of the current computer */
+    char endianness_32;
+    /** The endianness of 64 bit values of the current computer */
+    char endianness_64;
+
+    /** The name of the program producing this trajectory */
+    char *first_program_name;
+    /** The forcefield used in the simulations */
+    char *forcefield_name;
+    /** The name of the user running the simulations */
+    char *first_user_name;
+    /** The name of the computer on which the simulations were performed */
+    char *first_computer_name;
+    /** The PGP signature of the user creating the file. */
+    char *first_pgp_signature;
+    /** The name of the program used when making last modifications to the
+     *  file */
+    char *last_program_name;
+    /** The name of the user making the last modifications to the file */
+    char *last_user_name;
+    /** The name of the computer on which the last modifications were made */
+    char *last_computer_name;
+    /** The PGP signature of the user making the last modifications to the
+     *  file. */
+    char *last_pgp_signature;
+    /** The time (n seconds since 1970) when the file was created */
+    int64_t time;
+    /** The exponential of the value of the distance unit used. The default
+     * distance unit is nm (1e-9), i.e. distance_unit_exponential = -9. If
+     * the measurements are in Å the distance_unit_exponential = -10. */
+    int64_t distance_unit_exponential;
+
+    /** A flag indicating if the number of atoms can vary throughout the
+     *  simulation, e.g. using a grand canonical ensemble */
+    char var_num_atoms_flag;
+    /** The number of frames in a frame set. It is allowed to have frame sets
+     *  with fewer frames, but this will help searching for specific frames */
+    int64_t frame_set_n_frames;
+    /** The number of frame sets in a medium stride step */
+    int64_t medium_stride_length;
+    /** The number of frame sets in a long stride step */
+    int64_t long_stride_length;
+    /** The current (can change from one frame set to another) time length
+     *  (in seconds) of one frame */
+    double time_per_frame;
+
+    /** The number of different kinds of molecules in the trajectory */
+    int64_t n_molecules;
+    /** A list of molecules in the trajectory */
+    tng_molecule_t molecules;
+    /** A list of the count of each molecule - if using variable number of
+     *  particles this will be specified in each frame set */
+    int64_t *molecule_cnt_list;
+    /** The total number of particles/atoms. If using variable number of
+     *  particles this will be specified in each frame set */
+    int64_t n_particles;
+
+     /** The pos in the src file of the first frame set */
+    int64_t first_trajectory_frame_set_input_file_pos;
+    /** The pos in the dest file of the first frame set */
+    int64_t first_trajectory_frame_set_output_file_pos;
+    /** The pos in the src file of the last frame set */
+    int64_t last_trajectory_frame_set_input_file_pos;
+    /** The pos in the dest file of the last frame set */
+    int64_t last_trajectory_frame_set_output_file_pos;
+    /** The currently active frame set */
+    struct tng_trajectory_frame_set current_trajectory_frame_set;
+    /** The pos in the src file of the current frame set */
+    long current_trajectory_frame_set_input_file_pos;
+    /** The pos in the dest file of the current frame set */
+    long current_trajectory_frame_set_output_file_pos;
+    /** The number of frame sets in the trajectory N.B. Not saved in file and
+     *  cannot be trusted to be up-to-date */
+    int64_t n_trajectory_frame_sets;
+
+    /** The number of trajectory blocks in the file */
+    int64_t n_trajectory_blocks;
+
+    /* These data blocks are non-trajectory data blocks */
+    /** The number of non-frame dependent particle dependent data blocks */
+    int n_particle_data_blocks;
+    /** A list of data blocks containing particle dependent data */
+    struct tng_particle_data *non_tr_particle_data;
+
+    /** The number of frame and particle independent data blocks */
+    int n_data_blocks;
+    /** A list of frame and particle indepdendent data blocks */
+    struct tng_non_particle_data *non_tr_data;
+
+    /** TNG compression algorithm for compressing positions */
+    int *compress_algo_pos;
+    /** TNG compression algorithm for compressing velocities */
+    int *compress_algo_vel;
+    /** The precision used for lossy compression */
+    double compression_precision;
+};
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifdef USE_WINDOWS
+#define TNG_INLINE __inline
+#define TNG_SNPRINTF _snprintf
+#else
+#define TNG_INLINE inline
+#define TNG_SNPRINTF snprintf
+#endif
+
+static TNG_INLINE int tng_min_i(int a, int b)
+{
+    return (a < b ? a : b);
+}
+
+/*
+static TNG_INLINE int tng_max_i(int a, int b)
+{
+    return (a > b ? a : b);
+}
+*/
+static TNG_INLINE int64_t tng_min_i64(int64_t a, int64_t b)
+{
+    return (a < b ? a : b);
+}
+
+static TNG_INLINE int64_t tng_max_i64(int64_t a, int64_t b)
+{
+    return (a > b ? a : b);
+}
+
+/*
+static TNG_INLINE float tng_min_f(float a, float b)
+{
+    return (a < b ? a : b);
+}
+
+static TNG_INLINE float tng_max_f(float a, float b)
+{
+    return (a > b ? a : b);
+}
+
+static TNG_INLINE double tng_min_d(double a, double b)
+{
+    return (a < b ? a : b);
+}
+
+static TNG_INLINE double tng_max_d(double a, double b)
+{
+    return (a > b ? a : b);
+}
+*/
+
+/** This function swaps the byte order of a 32 bit numerical variable
+ * to big endian.
+ * It does not only work with integer, but e.g. floats need casting.
+ * If the byte order is already big endian no change is needed.
+ * @param tng_data is a trajectory data container.
+ * @param v is a pointer to a 32 bit numerical value (float or integer).
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the current
+ * byte order is not recognised.
+ */
+static tng_function_status tng_swap_byte_order_big_endian_32
+                (const tng_trajectory_t tng_data, int32_t *v)
+{
+    switch(tng_data->endianness_32)
+    {
+    case TNG_LITTLE_ENDIAN_32: /* Byte order is reversed. */
+        *v = ((*v & 0xFF000000) >> 24) | /* Move first byte to end */
+             ((*v & 0x00FF0000) >> 8) |  /* Move 2nd byte to pos 3 */
+             ((*v & 0x0000FF00) << 8) |  /* Move 3rd byte to pos 2 */
+             ((*v & 0x000000FF) << 24);  /* Move last byte to first */
+
+        return(TNG_SUCCESS);
+
+    case TNG_BYTE_PAIR_SWAP_32: /* byte pair swap */
+        *v = ((*v & 0xFFFF0000) >> 16) |
+             ((*v & 0x0000FFFF) << 16);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BIG_ENDIAN_32: /* Already correct */
+        return(TNG_SUCCESS);
+
+    default:
+        return(TNG_FAILURE);
+    }
+}
+
+/** This function swaps the byte order of a 64 bit numerical variable
+ * to big endian.
+ * It does not only work with integer, but e.g. floats need casting.
+ * The byte order swapping routine can convert four different byte
+ * orders to big endian.
+ * If the byte order is already big endian no change is needed.
+ * @param tng_data is a trajectory data container.
+ * @param v is a pointer to a 64 bit numerical value (double or integer).
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the current
+ * byte order is not recognised.
+ */
+static tng_function_status tng_swap_byte_order_big_endian_64
+                (const tng_trajectory_t tng_data, int64_t *v)
+{
+    switch(tng_data->endianness_64)
+    {
+    case TNG_LITTLE_ENDIAN_64: /* Byte order is reversed. */
+        *v = ((*v & 0xFF00000000000000LL) >> 56) | /* Move first byte to end */
+             ((*v & 0x00FF000000000000LL) >> 40) | /* Move 2nd byte to pos 7 */
+             ((*v & 0x0000FF0000000000LL) >> 24) | /* Move 3rd byte to pos 6 */
+             ((*v & 0x000000FF00000000LL) >> 8 ) | /* Move 4th byte to pos 5 */
+             ((*v & 0x00000000FF000000LL) << 8 ) | /* Move 5th byte to pos 4 */
+             ((*v & 0x0000000000FF0000LL) << 24) | /* Move 6th byte to pos 3 */
+             ((*v & 0x000000000000FF00LL) << 40) | /* Move 7th byte to pos 2 */
+             ((*v & 0x00000000000000FFLL) << 56);  /* Move last byte to first */
+
+        return(TNG_SUCCESS);
+
+    case TNG_QUAD_SWAP_64: /* Byte quad swap */
+        *v = ((*v & 0xFFFFFFFF00000000LL) >> 32) |
+             ((*v & 0x00000000FFFFFFFFLL) << 32);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BYTE_PAIR_SWAP_64: /* Byte pair swap */
+        *v = ((*v & 0xFFFF0000FFFF0000LL) >> 16) |
+             ((*v & 0x0000FFFF0000FFFFLL) << 16);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BYTE_SWAP_64: /* Byte swap */
+        *v = ((*v & 0xFF00FF00FF00FF00LL) >> 8) |
+             ((*v & 0x00FF00FF00FF00FFLL) << 8);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BIG_ENDIAN_64: /* Already correct */
+        return(TNG_SUCCESS);
+
+    default:
+        return(TNG_FAILURE);
+    }
+}
+
+/** This function swaps the byte order of a 32 bit numerical variable
+ * to little endian.
+ * It does not only work with integer, but e.g. floats need casting.
+ * If the byte order is already little endian no change is needed.
+ * @param tng_data is a trajectory data container.
+ * @param v is a pointer to a 32 bit numerical value (float or integer).
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the current
+ * byte order is not recognised.
+ */
+static tng_function_status tng_swap_byte_order_little_endian_32
+                (const tng_trajectory_t tng_data, int32_t *v)
+{
+    switch(tng_data->endianness_32)
+    {
+    case TNG_LITTLE_ENDIAN_32: /* Already correct */
+        return(TNG_SUCCESS);
+
+    case TNG_BYTE_PAIR_SWAP_32: /* byte pair swapped big endian to little endian */
+        *v = ((*v & 0xFF00FF00) >> 8) |
+             ((*v & 0x00FF00FF) << 8);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BIG_ENDIAN_32: /* Byte order is reversed. */
+        *v = ((*v & 0xFF000000) >> 24) | /* Move first byte to end */
+             ((*v & 0x00FF0000) >> 8) |  /* Move 2nd byte to pos 3 */
+             ((*v & 0x0000FF00) << 8) |  /* Move 3rd byte to pos 2 */
+             ((*v & 0x000000FF) << 24);  /* Move last byte to first */
+
+        return(TNG_SUCCESS);
+
+    default:
+        return(TNG_FAILURE);
+    }
+}
+
+/** This function swaps the byte order of a 64 bit numerical variable
+ * to little endian.
+ * It does not only work with integer, but e.g. floats need casting.
+ * The byte order swapping routine can convert four different byte
+ * orders to little endian.
+ * If the byte order is already little endian no change is needed.
+ * @param tng_data is a trajectory data container.
+ * @param v is a pointer to a 64 bit numerical value (double or integer).
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the current
+ * byte order is not recognised.
+ */
+static tng_function_status tng_swap_byte_order_little_endian_64
+                (const tng_trajectory_t tng_data, int64_t *v)
+{
+    switch(tng_data->endianness_64)
+    {
+    case TNG_LITTLE_ENDIAN_64: /* Already correct */
+        return(TNG_SUCCESS);
+
+    case TNG_QUAD_SWAP_64: /* Byte quad swapped big endian to little endian */
+        *v = ((*v & 0xFF000000FF000000LL) >> 24) |
+             ((*v & 0x00FF000000FF0000LL) >> 8) |
+             ((*v & 0x0000FF000000FF00LL) << 8) |
+             ((*v & 0x000000FF000000FFLL) << 24);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BYTE_PAIR_SWAP_64: /* Byte pair swapped big endian to little endian */
+        *v = ((*v & 0xFF00FF0000000000LL) >> 40) |
+             ((*v & 0x00FF00FF00000000LL) >> 24) |
+             ((*v & 0x00000000FF00FF00LL) << 24) |
+             ((*v & 0x0000000000FF00FFLL) << 40);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BYTE_SWAP_64: /* Byte swapped big endian to little endian */
+        *v = ((*v & 0xFFFF000000000000LL) >> 48) |
+             ((*v & 0x0000FFFF00000000LL) >> 16) |
+             ((*v & 0x00000000FFFF0000LL) << 16) |
+             ((*v & 0x000000000000FFFFLL) << 48);
+
+        return(TNG_SUCCESS);
+
+    case TNG_BIG_ENDIAN_64: /* Byte order is reversed. */
+        *v = ((*v & 0xFF00000000000000LL) >> 56) | /* Move first byte to end */
+             ((*v & 0x00FF000000000000LL) >> 40) | /* Move 2nd byte to pos 7 */
+             ((*v & 0x0000FF0000000000LL) >> 24) | /* Move 3rd byte to pos 6 */
+             ((*v & 0x000000FF00000000LL) >> 8 ) | /* Move 4th byte to pos 5 */
+             ((*v & 0x00000000FF000000LL) << 8 ) | /* Move 5th byte to pos 4 */
+             ((*v & 0x0000000000FF0000LL) << 24) | /* Move 6th byte to pos 3 */
+             ((*v & 0x000000000000FF00LL) << 40) | /* Move 7th byte to pos 2 */
+             ((*v & 0x00000000000000FFLL) << 56);  /* Move last byte to first */
+
+        return(TNG_SUCCESS);
+
+    default:
+        return(TNG_FAILURE);
+    }
+}
+/** Generate the md5 hash of a block.
+ * The hash is created based on the actual block contents.
+ * @param block is a general block container.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+static tng_function_status tng_block_md5_hash_generate(tng_gen_block_t block)
+{
+    md5_state_t md5_state;
+
+    md5_init(&md5_state);
+    md5_append(&md5_state, (md5_byte_t *)block->block_contents,
+               (int)block->block_contents_size);
+    md5_finish(&md5_state, (md5_byte_t *)block->md5_hash);
+
+    return(TNG_SUCCESS);
+}
+
+/** Compare the current block md5 hash (e.g. read from file) with the md5 hash
+ * calculated from the current contents.
+ * If the current md5 hash is not set skip the comparison.
+ * @param block is a general block container.
+ * @param results If the hashes match results is set to TNG_TRUE, otherwise it is
+ * set to TNG_FALSE. If the hash was not set results is set to TNG_TRUE.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the hash was not
+ * set.
+ */
+static tng_function_status tng_md5_hash_match_verify(tng_gen_block_t block,
+                                                     tng_bool *results)
+{
+    md5_state_t md5_state;
+    char hash[TNG_MD5_HASH_LEN];
+
+    TNG_ASSERT(block->block_contents_size > 0, "The block contents size must be > 0");
+
+    *results = TNG_TRUE;
+    if(strncmp(block->md5_hash, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0)
+    {
+        return(TNG_FAILURE);
+    }
+    md5_init(&md5_state);
+    md5_append(&md5_state, (md5_byte_t *)block->block_contents,
+               (int)block->block_contents_size);
+    md5_finish(&md5_state, (md5_byte_t *)hash);
+
+    if(strncmp(block->md5_hash, hash, 16) != 0)
+    {
+        *results = TNG_FALSE;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Open the input file if it is not already opened.
+ * @param tng_data is a trajectory data container.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_input_file_init(tng_trajectory_t tng_data)
+{
+    if(!tng_data->input_file)
+    {
+        if(!tng_data->input_file_path)
+        {
+            fprintf(stderr, "TNG library: No file specified for reading. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->input_file = fopen(tng_data->input_file_path, "r");
+        if(!tng_data->input_file)
+        {
+            fprintf(stderr, "TNG library: Cannot open file %s. %s: %d\n",
+                   tng_data->input_file_path, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Open the output file if it is not already opened
+ * @param tng_data is a trajectory data container.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_output_file_init(tng_trajectory_t tng_data)
+{
+    if(!tng_data->output_file)
+    {
+        if(!tng_data->output_file_path)
+        {
+            fprintf(stderr, "TNG library: No file specified for writing. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+
+        tng_data->output_file = fopen(tng_data->output_file_path, "w+");
+
+        if(!tng_data->output_file)
+        {
+            fprintf(stderr, "TNG library: Cannot open file %s. %s: %d\n",
+                   tng_data->output_file_path, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Setup a file block container.
+ * @param block_p a pointer to memory to initialise as a file block container.
+ * @details Memory is allocated during initialisation.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_block_init(struct tng_gen_block **block_p)
+{
+    tng_gen_block_t block;
+
+    *block_p = malloc(sizeof(struct tng_gen_block));
+
+    if(!*block_p)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+               sizeof(struct tng_gen_block), __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    block = *block_p;
+
+    block->id = -1;
+    /* Reset the md5_hash */
+    memcpy(block->md5_hash, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", TNG_MD5_HASH_LEN);
+    block->name = 0;
+    block->block_version = TNG_VERSION;
+    block->header_contents = 0;
+    block->header_contents_size = 0;
+    block->block_contents = 0;
+    block->block_contents_size = 0;
+
+    return(TNG_SUCCESS);
+}
+
+/**
+ * @brief Clean up a file block container.
+ * @param block_p a pointer to the file block container to destroy.
+ * @details All allocated memory in the data structure is freed, as well as
+ * block_p itself.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+static tng_function_status tng_block_destroy(struct tng_gen_block **block_p)
+{
+    tng_gen_block_t block = *block_p;
+
+    if(!*block_p)
+    {
+        return(TNG_SUCCESS);
+    }
+
+/*     fprintf(stderr, "TNG library: Destroying block\n"); */
+    if(block->name)
+    {
+        free(block->name);
+        block->name = 0;
+    }
+    if(block->header_contents)
+    {
+        free(block->header_contents);
+        block->header_contents = 0;
+    }
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+        block->block_contents = 0;
+    }
+
+    free(*block_p);
+    *block_p = 0;
+
+    return(TNG_SUCCESS);
+}
+
+/** Read the header of a data block, regardless of its type
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_block_header_read
+                (tng_trajectory_t tng_data, tng_gen_block_t block)
+{
+    int len, offset = 0;
+
+    TNG_ASSERT(block != 0, "TNG library: Trying to read to uninitialized block (NULL pointer).");
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    /* First read the header size to be able to read the whole header. */
+    if(fread(&block->header_contents_size, sizeof(block->header_contents_size),
+        1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read header size. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* If this was the size of the general info block check the endianness */
+    if(ftell(tng_data->input_file) < 9)
+    {
+        /* File is little endian */
+        if ( *((const char*)&block->header_contents_size) != 0x00 &&
+             *((const char*)(&block->header_contents_size) + 7) == 0x00)
+        {
+            /* If the architecture endianness is little endian no byte swap
+             * will be needed. Otherwise use the functions to swap to little
+             * endian */
+            if(tng_data->endianness_32 == TNG_LITTLE_ENDIAN_32)
+            {
+                tng_data->input_endianness_swap_func_32 = 0;
+            }
+            else
+            {
+                tng_data->input_endianness_swap_func_32 =
+                &tng_swap_byte_order_little_endian_32;
+            }
+            if(tng_data->endianness_64 == TNG_LITTLE_ENDIAN_64)
+            {
+                tng_data->input_endianness_swap_func_64 = 0;
+            }
+            else
+            {
+                tng_data->input_endianness_swap_func_64 =
+                &tng_swap_byte_order_little_endian_64;
+            }
+        }
+        /* File is big endian */
+        else
+        {
+            /* If the architecture endianness is big endian no byte swap
+             * will be needed. Otherwise use the functions to swap to big
+             * endian */
+            if(tng_data->endianness_32 == TNG_BIG_ENDIAN_32)
+            {
+                tng_data->input_endianness_swap_func_32 = 0;
+            }
+            else
+            {
+                tng_data->input_endianness_swap_func_32 =
+                &tng_swap_byte_order_big_endian_32;
+            }
+            if(tng_data->endianness_64 == TNG_BIG_ENDIAN_64)
+            {
+                tng_data->input_endianness_swap_func_64 = 0;
+            }
+            else
+            {
+                tng_data->input_endianness_swap_func_64 =
+                &tng_swap_byte_order_big_endian_64;
+            }
+        }
+    }
+
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &block->header_contents_size)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    /* Move the reading position to the beginning of the header. */
+    fseek(tng_data->input_file, -(long)sizeof(block->header_contents_size),
+          SEEK_CUR);
+
+    /* If there is already memory allocated for the contents free it (we do not
+     * know if the size is correct). */
+    if(block->header_contents)
+    {
+        free(block->header_contents);
+    }
+
+    block->header_contents = malloc(block->header_contents_size);
+    if(!block->header_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->header_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* Read the whole header into header_contents. This way it can be saved
+     * even if it cannot be interpreted
+     * for one reason or another. */
+    if(fread(block->header_contents, block->header_contents_size, 1,
+        tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read header. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* The header contents size has already been read. Skip ahead. */
+    offset = sizeof(block->header_contents_size);
+
+
+    /* Copy the respective parameters from the header contents block */
+    memcpy(&block->block_contents_size, block->header_contents+offset,
+           sizeof(block->block_contents_size));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &block->block_contents_size)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    offset += sizeof(block->block_contents_size);
+
+    memcpy(&block->id, block->header_contents+offset, sizeof(block->id));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &block->id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    offset += sizeof(block->id);
+
+    memcpy(block->md5_hash, block->header_contents+offset, TNG_MD5_HASH_LEN);
+    offset += TNG_MD5_HASH_LEN;
+
+    if(block->name && strcmp(block->name, block->header_contents+offset) != 0)
+    {
+        free(block->name);
+        block->name = 0;
+    }
+    len = tng_min_i((int)strlen(block->header_contents+offset) + 1, TNG_MAX_STR_LEN);
+    if(!block->name)
+    {
+        block->name = malloc(len);
+        if(!block->name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+                    __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        strncpy(block->name, block->header_contents+offset, len);
+    }
+    offset += len;
+
+    memcpy(&block->block_version, block->header_contents+offset,
+           sizeof(block->block_version));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &block->block_version)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Write a whole block, both header and contents, regardless of it type
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+/* Disabled until it is used.*/
+/*
+// static tng_function_status tng_block_verbatim_write(tng_trajectory_t tng_data,
+//                                                     tng_gen_block_t block)
+// {
+//     if(!block->header_contents)
+//     {
+//         fprintf(stderr, "TNG library: No contents to write. %s: %d\n", __FILE__, __LINE__);
+//         return(TNG_FAILURE);
+//     }
+//     if(fwrite(block->header_contents, block->header_contents_size, 1,
+//                 tng_data->output_file) != 1)
+//     {
+//         fprintf(stderr, "TNG library: Could not write all header data. %s: %d\n",
+//                 __FILE__, __LINE__);
+//         return(TNG_CRITICAL);
+//     }
+//
+//     if(!block->block_contents)
+//     {
+//         fprintf(stderr, "TNG library: No block data to write. %s: %d\n",
+//                 __FILE__, __LINE__);
+//         return(TNG_FAILURE);
+//     }
+//     if(fwrite(block->block_contents, block->block_contents_size, 1,
+//                 tng_data->output_file) != 1)
+//     {
+//         fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n",
+//                 __FILE__, __LINE__);
+//         return(TNG_CRITICAL);
+//     }
+//     return(TNG_SUCCESS);
+// }
+*/
+/** Write the header of a data block, regardless of its type
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_block_header_write
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const char hash_mode)
+{
+    int name_len, offset = 0;
+
+    TNG_ASSERT(block != 0, "TNG library: Trying to write uninitialized block (NULL pointer).");
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(!block->name)
+    {
+        block->name = malloc(1);
+        if(!block->name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        block->name[0] = 0;
+    }
+
+    name_len = tng_min_i((int)strlen(block->name) + 1, TNG_MAX_STR_LEN);
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_block_md5_hash_generate(block);
+    }
+
+    /* Calculate the size of the header to write */
+    block->header_contents_size = sizeof(block->header_contents_size) +
+                                  sizeof(block->block_contents_size) +
+                                  sizeof(block->id) +
+                                  sizeof(block->block_version) +
+                                  TNG_MD5_HASH_LEN +
+                                  name_len;
+
+    if(block->header_contents)
+    {
+        free(block->header_contents);
+    }
+
+    block->header_contents = malloc(block->header_contents_size);
+    if(!block->header_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->header_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* First copy all data into the header_contents block and finally write
+     * the whole block at once. */
+    memcpy(block->header_contents, &block->header_contents_size,
+           sizeof(block->header_contents_size));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(block->header_contents_size);
+
+    memcpy(block->header_contents+offset, &block->block_contents_size,
+           sizeof(block->block_contents_size));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(block->block_contents_size);
+
+    memcpy(block->header_contents+offset, &block->id, sizeof(block->id));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(block->id);
+
+    memcpy(block->header_contents+offset, block->md5_hash, TNG_MD5_HASH_LEN);
+    offset += TNG_MD5_HASH_LEN;
+
+    strncpy(block->header_contents+offset, block->name, name_len);
+    offset += name_len;
+
+    memcpy(block->header_contents+offset, &block->block_version,
+           sizeof(block->block_version));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(fwrite(block->header_contents, block->header_contents_size,
+       1, tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all header data. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Read a general info block. This is the first block of a TNG file.
+ *  Populate the fields in tng_data.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_general_info_block_read
+                (tng_trajectory_t tng_data, tng_gen_block_t block,
+                 const char hash_mode)
+{
+    int len, offset = 0;
+    tng_bool same_hash;
+
+    void *temp;
+
+    TNG_ASSERT(block != 0, "TNG library: Trying to read data to an uninitialized block (NULL pointer)");
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    temp = realloc(block->block_contents, block->block_contents_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        free(block->block_contents);
+        block->block_contents = 0;
+        return(TNG_CRITICAL);
+    }
+    block->block_contents = temp;
+
+    /* Read the whole block into block_contents to be able to write it to disk
+     * even if it cannot be interpreted. */
+    if(fread(block->block_contents, block->block_contents_size, 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* FIXME: Does not check if the size of the contents matches the expected
+     * size or if the contents can be read. */
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_match_verify(block, &same_hash);
+        if(same_hash != TNG_TRUE)
+        {
+            fprintf(stderr, "TNG library: General info block contents corrupt. Hashes do not match. "
+                "%s: %d\n",
+                __FILE__, __LINE__);
+    /*         return(TNG_FAILURE); */
+        }
+    }
+
+    len = tng_min_i((int)strlen(block->block_contents) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->first_program_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->first_program_name);
+        tng_data->first_program_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->first_program_name = temp;
+    strncpy(tng_data->first_program_name, block->block_contents, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents + offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->last_program_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->last_program_name);
+        tng_data->last_program_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->last_program_name = temp;
+    strncpy(tng_data->last_program_name, block->block_contents + offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->first_user_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->first_user_name);
+        tng_data->first_user_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->first_user_name = temp;
+    strncpy(tng_data->first_user_name, block->block_contents+offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->last_user_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->last_user_name);
+        tng_data->last_user_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->last_user_name = temp;
+    strncpy(tng_data->last_user_name, block->block_contents+offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->first_computer_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->first_computer_name);
+        tng_data->first_computer_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->first_computer_name = temp;
+    strncpy(tng_data->first_computer_name, block->block_contents+offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->last_computer_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->last_computer_name);
+        tng_data->last_computer_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->last_computer_name = temp;
+    strncpy(tng_data->last_computer_name, block->block_contents+offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->first_pgp_signature, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->first_pgp_signature);
+        tng_data->first_pgp_signature = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->first_pgp_signature = temp;
+    strncpy(tng_data->first_pgp_signature, block->block_contents+offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->last_pgp_signature, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->last_pgp_signature);
+        tng_data->last_pgp_signature = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->last_pgp_signature = temp;
+    strncpy(tng_data->last_pgp_signature, block->block_contents+offset, len);
+    offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->forcefield_name, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->forcefield_name);
+        tng_data->forcefield_name = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->forcefield_name = temp;
+    strncpy(tng_data->forcefield_name, block->block_contents+offset, len);
+    offset += len;
+
+    memcpy(&tng_data->time, block->block_contents+offset,
+           sizeof(tng_data->time));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &tng_data->time)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->time);
+
+    memcpy(&tng_data->var_num_atoms_flag, block->block_contents+offset,
+           sizeof(tng_data->var_num_atoms_flag));
+    offset += sizeof(tng_data->var_num_atoms_flag);
+
+    memcpy(&tng_data->frame_set_n_frames, block->block_contents+offset,
+           sizeof(tng_data->frame_set_n_frames));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                 &tng_data->frame_set_n_frames)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->frame_set_n_frames);
+
+    memcpy(&tng_data->first_trajectory_frame_set_input_file_pos,
+           block->block_contents+offset,
+           sizeof(tng_data->first_trajectory_frame_set_input_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                          &tng_data->first_trajectory_frame_set_input_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->first_trajectory_frame_set_input_file_pos);
+
+    tng_data->current_trajectory_frame_set.next_frame_set_file_pos =
+    tng_data->first_trajectory_frame_set_input_file_pos;
+
+
+    memcpy(&tng_data->last_trajectory_frame_set_input_file_pos,
+           block->block_contents+offset,
+           sizeof(tng_data->last_trajectory_frame_set_input_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                          &tng_data->last_trajectory_frame_set_input_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->last_trajectory_frame_set_input_file_pos);
+
+    memcpy(&tng_data->medium_stride_length, block->block_contents+offset,
+           sizeof(tng_data->medium_stride_length));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                               &tng_data->medium_stride_length)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->medium_stride_length);
+
+    memcpy(&tng_data->long_stride_length, block->block_contents+offset,
+           sizeof(tng_data->long_stride_length));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                 &tng_data->long_stride_length)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->long_stride_length);
+
+    if(block->block_version >= 3)
+    {
+        memcpy(&tng_data->distance_unit_exponential, block->block_contents+offset,
+            sizeof(tng_data->distance_unit_exponential));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                          &tng_data->distance_unit_exponential)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Write a general info block. This is the first block of a TNG file.
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_general_info_block_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    int first_program_name_len, first_user_name_len;
+    int first_computer_name_len, first_pgp_signature_len;
+    int last_program_name_len, last_user_name_len;
+    int last_computer_name_len, last_pgp_signature_len;
+    int forcefield_name_len, name_len;
+    int offset = 0;
+    tng_gen_block_t block;
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    fseek(tng_data->output_file, 0, SEEK_SET);
+
+    /* If the strings are unallocated allocate memory for just string
+     * termination */
+    if(!tng_data->first_program_name)
+    {
+        tng_data->first_program_name = malloc(1);
+        if(!tng_data->first_program_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->first_program_name[0] = 0;
+    }
+    if(!tng_data->last_program_name)
+    {
+        tng_data->last_program_name = malloc(1);
+        if(!tng_data->last_program_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->last_program_name[0] = 0;
+    }
+    if(!tng_data->first_user_name)
+    {
+        tng_data->first_user_name = malloc(1);
+        if(!tng_data->first_user_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->first_user_name[0] = 0;
+    }
+    if(!tng_data->last_user_name)
+    {
+        tng_data->last_user_name = malloc(1);
+        if(!tng_data->last_user_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->last_user_name[0] = 0;
+    }
+    if(!tng_data->first_computer_name)
+    {
+        tng_data->first_computer_name = malloc(1);
+        if(!tng_data->first_computer_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->first_computer_name[0] = 0;
+    }
+    if(!tng_data->last_computer_name)
+    {
+        tng_data->last_computer_name = malloc(1);
+        if(!tng_data->last_computer_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->last_computer_name[0] = 0;
+    }
+    if(!tng_data->first_pgp_signature)
+    {
+        tng_data->first_pgp_signature = malloc(1);
+        if(!tng_data->first_pgp_signature)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->first_pgp_signature[0] = 0;
+    }
+    if(!tng_data->last_pgp_signature)
+    {
+        tng_data->last_pgp_signature = malloc(1);
+        if(!tng_data->last_pgp_signature)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->last_pgp_signature[0] = 0;
+    }
+    if(!tng_data->forcefield_name)
+    {
+        tng_data->forcefield_name = malloc(1);
+        if(!tng_data->forcefield_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        tng_data->forcefield_name[0] = 0;
+    }
+
+    tng_block_init(&block);
+
+    name_len = (int)strlen("GENERAL INFO");
+
+    block->name = malloc(name_len + 1);
+    if(!block->name)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                name_len+1, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    strcpy(block->name, "GENERAL INFO");
+    block->id = TNG_GENERAL_INFO;
+
+    first_program_name_len = tng_min_i((int)strlen(tng_data->first_program_name) + 1,
+                           TNG_MAX_STR_LEN);
+    last_program_name_len = tng_min_i((int)strlen(tng_data->last_program_name) + 1,
+                           TNG_MAX_STR_LEN);
+    first_user_name_len = tng_min_i((int)strlen(tng_data->first_user_name) + 1,
+                        TNG_MAX_STR_LEN);
+    last_user_name_len = tng_min_i((int)strlen(tng_data->last_user_name) + 1,
+                        TNG_MAX_STR_LEN);
+    first_computer_name_len = tng_min_i((int)strlen(tng_data->first_computer_name) + 1,
+                            TNG_MAX_STR_LEN);
+    last_computer_name_len = tng_min_i((int)strlen(tng_data->last_computer_name) + 1,
+                            TNG_MAX_STR_LEN);
+    first_pgp_signature_len = tng_min_i((int)strlen(tng_data->first_pgp_signature) + 1,
+                            TNG_MAX_STR_LEN);
+    last_pgp_signature_len = tng_min_i((int)strlen(tng_data->last_pgp_signature) + 1,
+                            TNG_MAX_STR_LEN);
+    forcefield_name_len = tng_min_i((int)strlen(tng_data->forcefield_name) + 1,
+                              TNG_MAX_STR_LEN);
+
+    block->block_contents_size = sizeof(tng_data->time) +
+                sizeof(tng_data->var_num_atoms_flag) +
+                sizeof(tng_data->frame_set_n_frames) +
+                sizeof(tng_data->first_trajectory_frame_set_input_file_pos) +
+                sizeof(tng_data->last_trajectory_frame_set_input_file_pos) +
+                sizeof(tng_data->medium_stride_length) +
+                sizeof(tng_data->long_stride_length) +
+                sizeof(tng_data->distance_unit_exponential) +
+                first_program_name_len +
+                last_program_name_len +
+                first_user_name_len +
+                last_user_name_len +
+                first_computer_name_len +
+                last_computer_name_len +
+                first_pgp_signature_len +
+                last_pgp_signature_len +
+                forcefield_name_len;
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    strncpy(block->block_contents, tng_data->first_program_name, first_program_name_len);
+    offset += first_program_name_len;
+
+    strncpy(block->block_contents+offset, tng_data->last_program_name, last_program_name_len);
+    offset += last_program_name_len;
+
+    strncpy(block->block_contents+offset, tng_data->first_user_name, first_user_name_len);
+    offset += first_user_name_len;
+
+    strncpy(block->block_contents+offset, tng_data->last_user_name, last_user_name_len);
+    offset += last_user_name_len;
+
+    strncpy(block->block_contents+offset, tng_data->first_computer_name,
+            first_computer_name_len);
+    offset += first_computer_name_len;
+
+    strncpy(block->block_contents+offset, tng_data->last_computer_name,
+            last_computer_name_len);
+    offset += last_computer_name_len;
+
+    strncpy(block->block_contents+offset, tng_data->first_pgp_signature,
+            first_pgp_signature_len);
+    offset += first_pgp_signature_len;
+
+    strncpy(block->block_contents+offset, tng_data->last_pgp_signature,
+            last_pgp_signature_len);
+    offset += last_pgp_signature_len;
+
+    strncpy(block->block_contents+offset, tng_data->forcefield_name,
+            forcefield_name_len);
+    offset += forcefield_name_len;
+
+    memcpy(block->block_contents+offset, &tng_data->time,
+           sizeof(tng_data->time));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->time);
+
+    memcpy(block->block_contents+offset, &tng_data->var_num_atoms_flag,
+           sizeof(tng_data->var_num_atoms_flag));
+    offset += sizeof(tng_data->var_num_atoms_flag);
+
+    memcpy(block->block_contents+offset, &tng_data->frame_set_n_frames,
+           sizeof(tng_data->frame_set_n_frames));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->frame_set_n_frames);
+
+    memcpy(block->block_contents+offset,
+           &tng_data->first_trajectory_frame_set_input_file_pos,
+           sizeof(tng_data->first_trajectory_frame_set_input_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->first_trajectory_frame_set_input_file_pos);
+
+    memcpy(block->block_contents+offset,
+           &tng_data->last_trajectory_frame_set_input_file_pos,
+           sizeof(tng_data->last_trajectory_frame_set_input_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->last_trajectory_frame_set_input_file_pos);
+
+    memcpy(block->block_contents+offset, &tng_data->medium_stride_length,
+           sizeof(tng_data->medium_stride_length));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->medium_stride_length);
+
+    memcpy(block->block_contents+offset, &tng_data->long_stride_length,
+           sizeof(tng_data->long_stride_length));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->long_stride_length);
+
+    memcpy(block->block_contents+offset, &tng_data->distance_unit_exponential,
+           sizeof(tng_data->distance_unit_exponential));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(tng_block_header_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot write header of file %s. %s: %d\n",
+               tng_data->output_file_path, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(fwrite(block->block_contents, block->block_contents_size, 1,
+        tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    tng_block_destroy(&block);
+
+    return(TNG_SUCCESS);
+}
+
+/** Read the chain data of a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param chain is the chain data container.
+ * @param offset is the offset of the block input and is updated when reading.
+ * @return TNG_SUCCESS(0) is successful.
+ */
+static tng_function_status tng_chain_data_read(tng_trajectory_t tng_data,
+                                               tng_gen_block_t block,
+                                               tng_chain_t chain,
+                                               int *offset)
+{
+    int len;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    memcpy(&chain->id, block->block_contents+*offset,
+            sizeof(chain->id));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &chain->id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(chain->id);
+
+    len = tng_min_i((int)strlen(block->block_contents+*offset) + 1,
+            TNG_MAX_STR_LEN);
+    chain->name = malloc(len);
+    strncpy(chain->name,
+            block->block_contents+*offset, len);
+    *offset += len;
+
+    memcpy(&chain->n_residues, block->block_contents+*offset,
+        sizeof(chain->n_residues));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &chain->n_residues)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(chain->n_residues);
+
+    return(TNG_SUCCESS);
+}
+
+/** Write the chain data of a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param chain is the chain data container.
+ * @param offset is the offset of the block output and is updated when writing.
+ * @return TNG_SUCCESS(0) is successful.
+ */
+static tng_function_status tng_chain_data_write(tng_trajectory_t tng_data,
+                                                tng_gen_block_t block,
+                                                tng_chain_t chain,
+                                                int *offset)
+{
+    int len;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    memcpy(block->block_contents+*offset, &chain->id, sizeof(chain->id));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                    (int64_t *)block->header_contents+*offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(chain->id);
+
+    len = tng_min_i((int)strlen(chain->name) + 1, TNG_MAX_STR_LEN);
+    strncpy(block->block_contents + *offset, chain->name, len);
+    *offset += len;
+
+    memcpy(block->block_contents+*offset, &chain->n_residues,
+        sizeof(chain->n_residues));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                    (int64_t *)block->header_contents+*offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(chain->n_residues);
+
+    return(TNG_SUCCESS);
+}
+
+/** Read the residue data of a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param residue is the residue data container.
+ * @param offset is the offset of the block input and is updated when reading.
+ * @return TNG_SUCCESS(0) is successful.
+ */
+static tng_function_status tng_residue_data_read(tng_trajectory_t tng_data,
+                                                 tng_gen_block_t block,
+                                                 tng_residue_t residue,
+                                                 int *offset)
+{
+    int len;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    memcpy(&residue->id, block->block_contents+*offset,
+        sizeof(residue->id));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &residue->id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(residue->id);
+
+    len = tng_min_i((int)strlen(block->block_contents+*offset) + 1,
+            TNG_MAX_STR_LEN);
+    residue->name = malloc(len);
+    strncpy(residue->name,
+            block->block_contents+*offset, len);
+    *offset += len;
+
+    memcpy(&residue->n_atoms, block->block_contents+*offset,
+            sizeof(residue->n_atoms));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &residue->n_atoms)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(residue->n_atoms);
+
+    return(TNG_SUCCESS);
+}
+
+/** Write the residue data of a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param residue is the residue data container.
+ * @param offset is the offset of the block output and is updated when writing.
+ * @return TNG_SUCCESS(0) is successful.
+ */
+static tng_function_status tng_residue_data_write(tng_trajectory_t tng_data,
+                                                  tng_gen_block_t block,
+                                                  tng_residue_t residue,
+                                                  int *offset)
+{
+    int len;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    memcpy(block->block_contents+*offset, &residue->id, sizeof(residue->id));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                    (int64_t *)block->header_contents+*offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(residue->id);
+
+    len = tng_min_i((int)strlen(residue->name) + 1, TNG_MAX_STR_LEN);
+    strncpy(block->block_contents + *offset, residue->name, len);
+    *offset += len;
+
+    memcpy(block->block_contents+*offset, &residue->n_atoms,
+        sizeof(residue->n_atoms));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                    (int64_t *)block->header_contents+*offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(residue->n_atoms);
+
+    return(TNG_SUCCESS);
+}
+
+/** Read the atom data of a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param atom is the atom data container.
+ * @param offset is the offset of the block input and is updated when reading.
+ * @return TNG_SUCCESS(0) is successful.
+ */
+static tng_function_status tng_atom_data_read(tng_trajectory_t tng_data,
+                                              tng_gen_block_t block,
+                                              tng_atom_t atom,
+                                              int *offset)
+{
+    int len;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    memcpy(&atom->id, block->block_contents+*offset,
+        sizeof(atom->id));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                    &atom->id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(atom->id);
+
+    len = tng_min_i((int)strlen(block->block_contents+*offset) + 1,
+            TNG_MAX_STR_LEN);
+    atom->name = malloc(len);
+    strncpy(atom->name,
+            block->block_contents+*offset, len);
+    *offset += len;
+
+    len = tng_min_i((int)strlen(block->block_contents+*offset) + 1,
+            TNG_MAX_STR_LEN);
+    atom->atom_type = malloc(len);
+    strncpy(atom->atom_type,
+            block->block_contents+*offset, len);
+    *offset += len;
+
+    return(TNG_SUCCESS);
+}
+
+/** Write the atom data of a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param atom is the atom data container.
+ * @param offset is the offset of the block output and is updated when writing.
+ * @return TNG_SUCCESS(0) is successful.
+ */
+static tng_function_status tng_atom_data_write(tng_trajectory_t tng_data,
+                                               tng_gen_block_t block,
+                                               tng_atom_t atom,
+                                               int *offset)
+{
+    int len;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    memcpy(block->block_contents+*offset, &atom->id,
+            sizeof(atom->id));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                    (int64_t *)block->header_contents+*offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(atom->id);
+
+    len = tng_min_i((int)strlen(atom->name) + 1, TNG_MAX_STR_LEN);
+    strncpy(block->block_contents + *offset, atom->name, len);
+    *offset += len;
+
+    len = tng_min_i((int)strlen(atom->atom_type) + 1, TNG_MAX_STR_LEN);
+    strncpy(block->block_contents + *offset, atom->atom_type, len);
+    *offset += len;
+
+    return(TNG_SUCCESS);
+}
+
+/** Read a molecules block. Contains chain, residue and atom data
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_molecules_block_read
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const char hash_mode)
+{
+    int64_t i, j, k, l;
+    int len, offset = 0;
+    tng_molecule_t molecule;
+    tng_chain_t chain;
+    tng_residue_t residue;
+    tng_atom_t atom;
+    tng_bond_t bond;
+    tng_bool same_hash;
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* Read the whole block into block_contents to be able to write it to disk
+     * even if it cannot be interpreted. */
+    if(fread(block->block_contents, block->block_contents_size, 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+    }
+
+    /* FIXME: Does not check if the size of the contents matches the expected
+     * size or if the contents can be read. */
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_match_verify(block, &same_hash);
+        if(same_hash != TNG_TRUE)
+        {
+            fprintf(stderr, "TNG library: Molecules block contents corrupt. Hashes do not match. "
+                "%s: %d\n",
+                __FILE__, __LINE__);
+        }
+    }
+
+    if(tng_data->molecules)
+    {
+        for(i=tng_data->n_molecules; i--;)
+        {
+            tng_molecule_destroy(tng_data, &tng_data->molecules[i]);
+        }
+        free(tng_data->molecules);
+        tng_data->molecules = 0;
+        tng_data->n_molecules = 0;
+    }
+
+    memcpy(&tng_data->n_molecules, block->block_contents,
+           sizeof(tng_data->n_molecules));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &tng_data->n_molecules)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->n_molecules);
+
+    if(tng_data->molecules)
+    {
+        free(tng_data->molecules);
+    }
+
+    tng_data->n_particles = 0;
+
+    tng_data->molecules = malloc(tng_data->n_molecules *
+                          sizeof(struct tng_molecule));
+    if(!tng_data->molecules)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               tng_data->n_molecules * sizeof(struct tng_molecule),
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(!tng_data->var_num_atoms_flag)
+    {
+        if(tng_data->molecule_cnt_list)
+        {
+            free(tng_data->molecule_cnt_list);
+        }
+        tng_data->molecule_cnt_list = malloc(sizeof(int64_t) *
+                                      tng_data->n_molecules);
+        if(!tng_data->molecule_cnt_list)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   tng_data->n_molecules * sizeof(struct tng_molecule),
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    /* Read each molecule from file */
+    for(i=0; i < tng_data->n_molecules; i++)
+    {
+        molecule = &tng_data->molecules[i];
+
+        memcpy(&molecule->id, block->block_contents+offset,
+               sizeof(molecule->id));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       &molecule->id)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->id);
+
+/*         fprintf(stderr, "TNG library: Read id: %"PRId64" offset: %d\n", molecule->id, offset);*/
+        len = tng_min_i((int)strlen(block->block_contents+offset) + 1, TNG_MAX_STR_LEN);
+        molecule->name = malloc(len);
+        strncpy(molecule->name, block->block_contents+offset, len);
+        offset += len;
+
+        memcpy(&molecule->quaternary_str, block->block_contents+offset,
+               sizeof(molecule->quaternary_str));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                     &molecule->quaternary_str)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->quaternary_str);
+
+        if(!tng_data->var_num_atoms_flag)
+        {
+            memcpy(&tng_data->molecule_cnt_list[i],
+                   block->block_contents+offset,
+                   sizeof(int64_t));
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                if(tng_data->input_endianness_swap_func_64(tng_data,
+                                               &tng_data->molecule_cnt_list[i])
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            offset += sizeof(int64_t);
+        }
+
+
+        memcpy(&molecule->n_chains, block->block_contents+offset,
+               sizeof(molecule->n_chains));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       &molecule->n_chains)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_chains);
+
+        memcpy(&molecule->n_residues, block->block_contents+offset,
+               sizeof(molecule->n_residues));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       &molecule->n_residues)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_residues);
+
+        memcpy(&molecule->n_atoms, block->block_contents+offset,
+               sizeof(molecule->n_atoms));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       &molecule->n_atoms)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_atoms);
+
+        tng_data->n_particles += molecule->n_atoms *
+                                 tng_data->molecule_cnt_list[i];
+
+        if(molecule->n_chains > 0)
+        {
+            molecule->chains = malloc(molecule->n_chains *
+                                    sizeof(struct tng_chain));
+            if(!molecule->chains)
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                    molecule->n_chains * sizeof(struct tng_chain),
+                    __FILE__, __LINE__);
+                return(TNG_CRITICAL);
+            }
+
+            chain = molecule->chains;
+        }
+        else
+        {
+            chain = 0;
+        }
+
+        if(molecule->n_residues > 0)
+        {
+            molecule->residues = malloc(molecule->n_residues *
+                                sizeof(struct tng_residue));
+            if(!molecule->residues)
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                    molecule->n_residues * sizeof(struct tng_residue),
+                    __FILE__, __LINE__);
+                if(molecule->chains)
+                {
+                    free(molecule->chains);
+                    molecule->chains = 0;
+                }
+                return(TNG_CRITICAL);
+            }
+
+            residue = molecule->residues;
+        }
+        else
+        {
+            residue = 0;
+        }
+
+        molecule->atoms = malloc(molecule->n_atoms *
+                                 sizeof(struct tng_atom));
+        if(!molecule->atoms)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   molecule->n_atoms * sizeof(struct tng_atom),
+                   __FILE__, __LINE__);
+            if(molecule->chains)
+            {
+                free(molecule->chains);
+                molecule->chains = 0;
+            }
+            if(molecule->residues)
+            {
+                free(molecule->residues);
+                molecule->residues = 0;
+            }
+            return(TNG_CRITICAL);
+        }
+
+        atom = molecule->atoms;
+
+        if(molecule->n_chains > 0)
+        {
+            /* Read the chains of the molecule */
+            for(j=molecule->n_chains; j--;)
+            {
+                chain->molecule = molecule;
+
+                tng_chain_data_read(tng_data, block, chain, &offset);
+
+                chain->residues = molecule->residues;
+                residue = chain->residues;
+
+                /* Read the residues of the chain */
+                for(k=chain->n_residues; k--;)
+                {
+                    residue->chain = chain;
+
+                    tng_residue_data_read(tng_data, block, residue, &offset);
+
+                    residue->atoms_offset = atom - molecule->atoms;
+                    /* Read the atoms of the residue */
+                    for(l=residue->n_atoms; l--;)
+                    {
+                        atom->residue = residue;
+
+                        tng_atom_data_read(tng_data, block, atom, &offset);
+
+                        atom++;
+                    }
+                    residue++;
+                }
+                chain++;
+            }
+        }
+        else
+        {
+            if(molecule->n_residues > 0)
+            {
+                for(k=molecule->n_residues; k--;)
+                {
+                    residue->chain = 0;
+
+                    tng_residue_data_read(tng_data, block, residue, &offset);
+
+                    residue->atoms_offset = atom - molecule->atoms;
+                    /* Read the atoms of the residue */
+                    for(l=residue->n_atoms; l--;)
+                    {
+                        atom->residue = residue;
+
+                        tng_atom_data_read(tng_data, block, atom, &offset);
+
+                        atom++;
+                    }
+                    residue++;
+                }
+            }
+            else
+            {
+                for(l=molecule->n_atoms; l--;)
+                {
+                    atom->residue = 0;
+
+                    tng_atom_data_read(tng_data, block, atom, &offset);
+
+                    atom++;
+                }
+            }
+        }
+
+        memcpy(&molecule->n_bonds, block->block_contents+offset,
+               sizeof(molecule->n_bonds));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       &molecule->n_bonds)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_bonds);
+
+        if(molecule->n_bonds > 0)
+        {
+            tng_data->molecules[i].bonds = malloc(molecule->n_bonds *
+                                           sizeof(struct tng_bond));
+            if(!molecule->bonds)
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                       molecule->n_bonds * sizeof(struct tng_bond),
+                       __FILE__, __LINE__);
+                if(molecule->chains)
+                {
+                    free(molecule->chains);
+                    molecule->chains = 0;
+                }
+                if(molecule->residues)
+                {
+                    free(molecule->residues);
+                    molecule->residues = 0;
+                }
+                if(molecule->atoms)
+                {
+                    free(molecule->atoms);
+                    molecule->atoms = 0;
+                }
+                return(TNG_CRITICAL);
+            }
+
+            bond = molecule->bonds;
+
+            for(j=molecule->n_bonds; j--;)
+            {
+                memcpy(&bond->from_atom_id, block->block_contents+offset,
+                    sizeof(bond->from_atom_id));
+                if(tng_data->input_endianness_swap_func_64)
+                {
+                    if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                               &bond->from_atom_id)
+                        != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+                offset += sizeof(bond->from_atom_id);
+
+                memcpy(&bond->to_atom_id, block->block_contents+offset,
+                    sizeof(bond->to_atom_id));
+                if(tng_data->input_endianness_swap_func_64)
+                {
+                    if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                               &bond->to_atom_id)
+                        != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+                offset += sizeof(bond->to_atom_id);
+
+                bond++;
+            }
+        }
+        else
+        {
+            molecule->bonds = 0;
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Write a molecules block.
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_molecules_block_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    int len = 0, name_len, offset = 0;
+    int64_t i, j, k, l;
+    tng_molecule_t molecule;
+    tng_chain_t chain;
+    tng_residue_t residue;
+    tng_atom_t atom;
+    tng_bond_t bond;
+    tng_gen_block_t block;
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    /* First predict the size of the block */
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        molecule = &tng_data->molecules[i];
+        if(!molecule->name)
+        {
+            molecule->name = malloc(1);
+            if(!molecule->name)
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                       __FILE__, __LINE__);
+                return(TNG_CRITICAL);
+            }
+            molecule->name[0] = 0;
+        }
+        len += tng_min_i((int)strlen(molecule->name) + 1, TNG_MAX_STR_LEN);
+
+        chain = molecule->chains;
+        for(j = molecule->n_chains; j--;)
+        {
+            len += sizeof(chain->id);
+
+            if(!chain->name)
+            {
+                chain->name = malloc(1);
+                if(!chain->name)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+                chain->name[0] = 0;
+            }
+            len += tng_min_i((int)strlen(chain->name) + 1, TNG_MAX_STR_LEN);
+
+            len += sizeof(chain->n_residues);
+
+            chain++;
+        }
+
+        residue = molecule->residues;
+        for(j = molecule->n_residues; j--;)
+        {
+            len += sizeof(residue->id);
+
+            if(!residue->name)
+            {
+                residue->name = malloc(1);
+                if(!residue->name)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+                residue->name[0] = 0;
+            }
+            len += tng_min_i((int)strlen(residue->name) + 1, TNG_MAX_STR_LEN);
+
+            len += sizeof(residue->n_atoms);
+
+            residue++;
+        }
+
+        atom = molecule->atoms;
+        for(j = molecule->n_atoms; j--;)
+        {
+            len += sizeof(atom->id);
+            if(!atom->name)
+            {
+                atom->name = malloc(1);
+                if(!atom->name)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+                atom->name[0] = 0;
+            }
+            len += tng_min_i((int)strlen(atom->name) + 1, TNG_MAX_STR_LEN);
+
+            if(!atom->atom_type)
+            {
+                atom->atom_type = malloc(1);
+                if(!atom->atom_type)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (1 byte). %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+                atom->atom_type[0] = 0;
+            }
+            len += tng_min_i((int)strlen(atom->atom_type) + 1, TNG_MAX_STR_LEN);
+
+            atom++;
+        }
+
+        for(j = molecule->n_bonds; j--;)
+        {
+            len += sizeof(bond->from_atom_id) + sizeof(bond->to_atom_id);
+        }
+    }
+
+    tng_block_init(&block);
+
+    name_len = (int)strlen("MOLECULES");
+
+    block->name = malloc(name_len + 1);
+    if(!block->name)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                name_len+1, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    strcpy(block->name, "MOLECULES");
+    block->id = TNG_MOLECULES;
+
+    block->block_contents_size = sizeof(tng_data->n_molecules) +
+                                 (sizeof(molecule->id) +
+                                 sizeof(molecule->quaternary_str) +
+                                 sizeof(molecule->n_chains) +
+                                 sizeof(molecule->n_residues) +
+                                 sizeof(molecule->n_atoms) +
+                                 sizeof(molecule->n_bonds)) *
+                                 tng_data->n_molecules +
+                                 len;
+
+    if(!tng_data->var_num_atoms_flag)
+    {
+        block->block_contents_size += tng_data->n_molecules * sizeof(int64_t);
+    }
+
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    memcpy(block->block_contents+offset, &tng_data->n_molecules,
+           sizeof(tng_data->n_molecules));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(tng_data->n_molecules);
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        molecule = &tng_data->molecules[i];
+        memcpy(block->block_contents+offset, &molecule->id,
+               sizeof(molecule->id));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->id);
+
+/*         fprintf(stderr, "TNG library: Wrote id: %"PRId64" offset: %d\n", molecule->id, offset); */
+        len = tng_min_i((int)strlen(molecule->name) + 1, TNG_MAX_STR_LEN);
+        strncpy(block->block_contents + offset, molecule->name, len);
+        offset += len;
+
+        memcpy(block->block_contents+offset, &molecule->quaternary_str,
+               sizeof(molecule->quaternary_str));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->quaternary_str);
+
+        if(!tng_data->var_num_atoms_flag)
+        {
+            memcpy(block->block_contents+offset,
+                   &tng_data->molecule_cnt_list[i], sizeof(int64_t));
+            if(tng_data->output_endianness_swap_func_64)
+            {
+                if(tng_data->output_endianness_swap_func_64(tng_data,
+                                            (int64_t *)block->header_contents+offset)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            offset += sizeof(int64_t);
+        }
+
+        memcpy(block->block_contents+offset, &molecule->n_chains,
+               sizeof(molecule->n_chains));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_chains);
+
+        memcpy(block->block_contents+offset, &molecule->n_residues,
+               sizeof(molecule->n_residues));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_residues);
+
+        memcpy(block->block_contents+offset, &molecule->n_atoms,
+               sizeof(molecule->n_atoms));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_atoms);
+
+        if(molecule->n_chains > 0)
+        {
+            chain = molecule->chains;
+            for(j = molecule->n_chains; j--;)
+            {
+                tng_chain_data_write(tng_data, block, chain, &offset);
+
+                residue = chain->residues;
+                for(k = chain->n_residues; k--;)
+                {
+                    tng_residue_data_write(tng_data, block, residue, &offset);
+
+                    atom = molecule->atoms + residue->atoms_offset;
+                    for(l = residue->n_atoms; l--;)
+                    {
+                        tng_atom_data_write(tng_data, block, atom, &offset);
+
+                        atom++;
+                    }
+                    residue++;
+                }
+                chain++;
+            }
+        }
+        else
+        {
+            if(molecule->n_residues > 0)
+            {
+                residue = molecule->residues;
+                for(k = molecule->n_residues; k--;)
+                {
+                    tng_residue_data_write(tng_data, block, residue, &offset);
+
+                    atom = molecule->atoms + residue->atoms_offset;
+                    for(l = residue->n_atoms; l--;)
+                    {
+                        tng_atom_data_write(tng_data, block, atom, &offset);
+
+                        atom++;
+                    }
+                    residue++;
+                }
+            }
+            else
+            {
+                atom = molecule->atoms;
+                for(l = molecule->n_atoms; l--;)
+                {
+                    tng_atom_data_write(tng_data, block, atom, &offset);
+
+                    atom++;
+                }
+            }
+        }
+
+        memcpy(block->block_contents+offset, &molecule->n_bonds,
+               sizeof(molecule->n_bonds));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(molecule->n_bonds);
+
+        bond = molecule->bonds;
+        for(j = molecule->n_bonds; j--;)
+        {
+            memcpy(block->block_contents+offset, &bond->from_atom_id,
+                   sizeof(bond->from_atom_id));
+            if(tng_data->output_endianness_swap_func_64)
+            {
+                if(tng_data->output_endianness_swap_func_64(tng_data,
+                                            (int64_t *)block->header_contents+offset)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            offset += sizeof(bond->from_atom_id);
+
+            memcpy(block->block_contents+offset, &bond->to_atom_id,
+                   sizeof(bond->to_atom_id));
+            if(tng_data->output_endianness_swap_func_64)
+            {
+                if(tng_data->output_endianness_swap_func_64(tng_data,
+                                            (int64_t *)block->header_contents+offset)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            offset += sizeof(bond->to_atom_id);
+
+            bond++;
+        }
+    }
+
+    if(tng_block_header_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot write header of file %s. %s: %d\n",
+               tng_data->output_file_path, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(fwrite(block->block_contents, block->block_contents_size, 1,
+              tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n",
+               __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    tng_block_destroy(&block);
+
+    return(TNG_SUCCESS);
+}
+
+/** Read a frame set block. Update tng_data->current_trajectory_frame_set
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_frame_set_block_read
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const char hash_mode)
+{
+    long file_pos;
+    int offset = 0;
+    int64_t i, prev_n_particles;
+    tng_bool same_hash;
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* Read the whole block into block_contents to be able to write it to
+     * disk even if it cannot be interpreted. */
+    if(fread(block->block_contents, block->block_contents_size, 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* FIXME: Does not check if the size of the contents matches the expected
+     * size or if the contents can be read. */
+
+    file_pos = (int64_t)ftell(tng_data->input_file) -
+               (long)(block->block_contents_size + block->header_contents_size);
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_match_verify(block, &same_hash);
+        if(same_hash != TNG_TRUE)
+        {
+            fprintf(stderr, "TNG library: Frame set block contents corrupt. File pos %ld Hashes do not match. "
+                "%s: %d\n",
+                file_pos, __FILE__, __LINE__);
+    /*         return(TNG_FAILURE); */
+        }
+    }
+
+    tng_data->current_trajectory_frame_set_input_file_pos = file_pos;
+
+    tng_frame_set_particle_mapping_free(tng_data);
+
+    if(tng_data->first_trajectory_frame_set_input_file_pos <= 0)
+    {
+        tng_data->first_trajectory_frame_set_input_file_pos = file_pos;
+    }
+    /* FIXME: Should check the frame number instead of the file_pos, in case
+     * frame sets are not in order */
+    if(tng_data->last_trajectory_frame_set_input_file_pos < file_pos)
+    {
+        tng_data->last_trajectory_frame_set_input_file_pos = file_pos;
+    }
+
+    memcpy(&frame_set->first_frame, block->block_contents,
+           sizeof(frame_set->first_frame));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &frame_set->first_frame)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->first_frame);
+
+    memcpy(&frame_set->n_frames, block->block_contents + offset,
+           sizeof(frame_set->n_frames));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &frame_set->n_frames)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->n_frames);
+
+    if(tng_data->var_num_atoms_flag)
+    {
+        prev_n_particles = frame_set->n_particles;
+        frame_set->n_particles = 0;
+        /* If the list of molecule counts has already been created assume that
+         * it is of correct size. */
+        if(!frame_set->molecule_cnt_list)
+        {
+                frame_set->molecule_cnt_list =
+                malloc(sizeof(int64_t) * tng_data->n_molecules);
+
+                if(!frame_set->molecule_cnt_list)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                           sizeof(int64_t) * tng_data->n_molecules,
+                           __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+        }
+        for(i = 0; i < tng_data->n_molecules; i++)
+        {
+            memcpy(&frame_set->molecule_cnt_list[i],
+                   block->block_contents + offset,
+                   sizeof(int64_t));
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                if(tng_data->input_endianness_swap_func_64(tng_data,
+                                              &frame_set->molecule_cnt_list[i])
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            offset += sizeof(int64_t);
+            frame_set->n_particles += tng_data->molecules[i].n_atoms *
+                                      frame_set->molecule_cnt_list[i];
+        }
+        if(prev_n_particles && frame_set->n_particles != prev_n_particles)
+        {
+            /* FIXME: Particle dependent data memory management */
+        }
+    }
+
+    memcpy(&frame_set->next_frame_set_file_pos,
+           block->block_contents + offset,
+           sizeof(frame_set->next_frame_set_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                           &frame_set->next_frame_set_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->next_frame_set_file_pos);
+
+    memcpy(&frame_set->prev_frame_set_file_pos,
+           block->block_contents + offset,
+           sizeof(frame_set->prev_frame_set_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                           &frame_set->prev_frame_set_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->prev_frame_set_file_pos);
+
+    memcpy(&frame_set->medium_stride_next_frame_set_file_pos,
+           block->block_contents + offset,
+           sizeof(frame_set->medium_stride_next_frame_set_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                             &frame_set->medium_stride_next_frame_set_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->medium_stride_next_frame_set_file_pos);
+
+    memcpy(&frame_set->medium_stride_prev_frame_set_file_pos,
+           block->block_contents + offset,
+           sizeof(frame_set->medium_stride_prev_frame_set_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                             &frame_set->medium_stride_prev_frame_set_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->medium_stride_prev_frame_set_file_pos);
+
+    memcpy(&frame_set->long_stride_next_frame_set_file_pos,
+           block->block_contents + offset,
+           sizeof(frame_set->long_stride_next_frame_set_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                               &frame_set->long_stride_next_frame_set_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->long_stride_next_frame_set_file_pos);
+
+    memcpy(&frame_set->long_stride_prev_frame_set_file_pos,
+           block->block_contents + offset,
+           sizeof(frame_set->long_stride_prev_frame_set_file_pos));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                               &frame_set->long_stride_prev_frame_set_file_pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->long_stride_prev_frame_set_file_pos);
+
+    if(block->block_version >= 3)
+    {
+        memcpy(&frame_set->first_frame_time,
+            block->block_contents + offset,
+            sizeof(frame_set->first_frame_time));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                (int64_t *)&frame_set->first_frame_time)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(frame_set->first_frame_time);
+
+        memcpy(&tng_data->time_per_frame,
+            block->block_contents + offset,
+            sizeof(tng_data->time_per_frame));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                (int64_t *)&tng_data->time_per_frame)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+    }
+    else
+    {
+        frame_set->first_frame_time = -1;
+        tng_data->time_per_frame = -1;
+    }
+
+    frame_set->n_written_frames = frame_set->n_frames;
+
+    return(TNG_SUCCESS);
+}
+
+/** Write tng_data->current_trajectory_frame_set to file
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_frame_set_block_write
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const char hash_mode)
+{
+    char *temp_name;
+    int64_t i;
+    int offset = 0;
+    unsigned int name_len;
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    name_len = (int)strlen("TRAJECTORY FRAME SET");
+
+    if(!block->name || strlen(block->name) < name_len)
+    {
+        temp_name = realloc(block->name, name_len + 1);
+        if(!temp_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                   name_len+1, __FILE__, __LINE__);
+            free(block->name);
+            block->name = 0;
+            return(TNG_CRITICAL);
+        }
+        block->name = temp_name;
+    }
+    strcpy(block->name, "TRAJECTORY FRAME SET");
+    block->id = TNG_TRAJECTORY_FRAME_SET;
+
+    block->block_contents_size = sizeof(int64_t) * 8;
+    block->block_contents_size += sizeof(double) * 2;
+
+    if(tng_data->var_num_atoms_flag)
+    {
+        block->block_contents_size += sizeof(int64_t) * tng_data->n_molecules;
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    memcpy(block->block_contents, &frame_set->first_frame,
+           sizeof(frame_set->first_frame));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->first_frame);
+
+    memcpy(block->block_contents+offset, &frame_set->n_frames,
+           sizeof(frame_set->n_frames));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->n_frames);
+
+    if(tng_data->var_num_atoms_flag)
+    {
+        for(i = 0; i < tng_data->n_molecules; i++)
+        {
+            memcpy(block->block_contents+offset,
+                   &frame_set->molecule_cnt_list[i],
+                   sizeof(int64_t));
+            if(tng_data->output_endianness_swap_func_64)
+            {
+                if(tng_data->output_endianness_swap_func_64(tng_data,
+                                            (int64_t *)block->header_contents+offset)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            offset += sizeof(int64_t);
+        }
+    }
+
+
+    memcpy(block->block_contents+offset, &frame_set->next_frame_set_file_pos,
+           sizeof(frame_set->next_frame_set_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->next_frame_set_file_pos);
+
+    memcpy(block->block_contents+offset, &frame_set->prev_frame_set_file_pos,
+           sizeof(frame_set->prev_frame_set_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->prev_frame_set_file_pos);
+
+    memcpy(block->block_contents+offset,
+           &frame_set->medium_stride_next_frame_set_file_pos,
+           sizeof(frame_set->medium_stride_next_frame_set_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->medium_stride_next_frame_set_file_pos);
+
+    memcpy(block->block_contents+offset,
+           &frame_set->medium_stride_prev_frame_set_file_pos,
+           sizeof(frame_set->medium_stride_prev_frame_set_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->medium_stride_prev_frame_set_file_pos);
+
+    memcpy(block->block_contents+offset,
+           &frame_set->long_stride_next_frame_set_file_pos,
+           sizeof(frame_set->long_stride_next_frame_set_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->long_stride_next_frame_set_file_pos);
+
+    memcpy(block->block_contents+offset,
+           &frame_set->long_stride_prev_frame_set_file_pos,
+           sizeof(frame_set->long_stride_prev_frame_set_file_pos));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->long_stride_prev_frame_set_file_pos);
+
+    memcpy(block->block_contents+offset,
+           &frame_set->first_frame_time,
+           sizeof(frame_set->first_frame_time));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(frame_set->first_frame_time);
+
+    memcpy(block->block_contents+offset,
+           &tng_data->time_per_frame,
+           sizeof(tng_data->time_per_frame));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(tng_block_header_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot write header of file %s. %s: %d\n",
+               tng_data->output_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(fwrite(block->block_contents, block->block_contents_size, 1,
+              tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+
+/** Read an atom mappings block (translating between real atom indexes and how
+ *  the atom info is written in this frame set).
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_trajectory_mapping_block_read
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const char hash_mode)
+{
+    int64_t i;
+    int offset = 0;
+    tng_bool same_hash;
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+
+    tng_particle_mapping_t mapping, mappings;
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* Read the whole block into block_contents to be able to write it to disk
+     *  even if it cannot be interpreted. */
+    if(fread(block->block_contents, block->block_contents_size, 1,
+        tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* FIXME: Does not check if the size of the contents matches the expected
+     * size or if the contents can be read. */
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_match_verify(block, &same_hash);
+        if(same_hash != TNG_TRUE)
+        {
+            fprintf(stderr, "TNG library: Particle mapping block contents corrupt. Hashes do not match. "
+                "%s: %d\n",
+                __FILE__, __LINE__);
+    /*         return(TNG_FAILURE); */
+        }
+    }
+
+    frame_set->n_mapping_blocks++;
+    mappings = realloc(frame_set->mappings,
+                       sizeof(struct tng_particle_mapping) *
+                       frame_set->n_mapping_blocks);
+    if(!mappings)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        free(frame_set->mappings);
+        frame_set->mappings = 0;
+        return(TNG_CRITICAL);
+    }
+    frame_set->mappings = mappings;
+    mapping = &mappings[frame_set->n_mapping_blocks - 1];
+
+
+    memcpy(&mapping->num_first_particle, block->block_contents+offset,
+           sizeof(mapping->num_first_particle));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &mapping->num_first_particle)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(mapping->num_first_particle);
+
+    memcpy(&mapping->n_particles, block->block_contents+offset,
+           sizeof(mapping->n_particles));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   &mapping->n_particles)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(mapping->n_particles);
+
+    mapping->real_particle_numbers = malloc(mapping->n_particles *
+                                            sizeof(int64_t));
+    if(!mapping->real_particle_numbers)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                mapping->n_particles * sizeof(int64_t), __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* If the byte order needs to be swapped the data must be read one value at
+     * a time and swapped */
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        for(i = 0; i < mapping->n_particles; i++)
+        {
+            memcpy(&mapping->real_particle_numbers[i],
+                    block->block_contents + offset,
+                    sizeof(int64_t));
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                            &mapping->real_particle_numbers[i])
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+            offset += sizeof(int64_t);
+        }
+    }
+    /* Otherwise the data can be read all at once */
+    else
+    {
+        memcpy(mapping->real_particle_numbers, block->block_contents + offset,
+               mapping->n_particles * sizeof(int64_t));
+    }
+
+
+    return(TNG_SUCCESS);
+}
+
+/** Write the atom mappings of the current trajectory frame set
+ * @param tng_data is a trajectory data container.
+ * @param block is a general block container.
+ * @param mapping_block_nr is the index of the mapping block to write.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+static tng_function_status tng_trajectory_mapping_block_write
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 int mapping_block_nr,
+                 const char hash_mode)
+{
+    char *temp_name;
+    int i, offset = 0;
+    unsigned int name_len;
+    tng_particle_mapping_t mapping =
+    &tng_data->current_trajectory_frame_set.mappings[mapping_block_nr];
+
+    if(mapping_block_nr >=
+       tng_data->current_trajectory_frame_set.n_mapping_blocks)
+    {
+        fprintf(stderr, "TNG library: Mapping block index out of bounds. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    name_len = (int)strlen("PARTICLE MAPPING");
+
+    if(!block->name || strlen(block->name) < name_len)
+    {
+        temp_name = realloc(block->name, name_len + 1);
+        if(!temp_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                   name_len+1, __FILE__, __LINE__);
+            free(block->name);
+            block->name = 0;
+            return(TNG_CRITICAL);
+        }
+        block->name = temp_name;
+    }
+    strcpy(block->name, "PARTICLE MAPPING");
+    block->id = TNG_PARTICLE_MAPPING;
+
+    block->block_contents_size = sizeof(int64_t) * (2 + mapping->n_particles);
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    memcpy(block->block_contents, &mapping->num_first_particle,
+           sizeof(mapping->num_first_particle));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(mapping->num_first_particle);
+
+    memcpy(block->block_contents+offset, &mapping->n_particles,
+           sizeof(mapping->n_particles));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+                                      (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(mapping->n_particles);
+
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        for(i = 0; i < mapping->n_particles; i++)
+        {
+            memcpy(block->block_contents+offset, &mapping->real_particle_numbers[i],
+                sizeof(int64_t));
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                                        (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+            offset += sizeof(int64_t);
+        }
+    }
+    else
+    {
+        memcpy(block->block_contents+offset, mapping->real_particle_numbers,
+               mapping->n_particles * sizeof(int64_t));
+    }
+
+
+    if(tng_block_header_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot write header of file %s. %s: %d\n",
+               tng_data->output_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(fwrite(block->block_contents, block->block_contents_size, 1,
+              tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Prepare a block for storing particle data
+ * @param tng_data is a trajectory data container.
+ * @param block_type_flag specifies if this is a trajectory block or a
+ * non-trajectory block. (TNG_TRAJECTORY_BLOCK or TNG_NON_TRAJECTORY_BLOCK)
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_particle_data_block_create
+                (tng_trajectory_t tng_data,
+                 const char block_type_flag)
+{
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+
+    tng_particle_data_t data;
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        frame_set->n_particle_data_blocks++;
+        data = realloc(frame_set->tr_particle_data,
+                    sizeof(struct tng_particle_data) *
+                    frame_set->n_particle_data_blocks);
+        if(!data)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+                sizeof(struct tng_particle_data) *
+                frame_set->n_particle_data_blocks,
+                __FILE__, __LINE__);
+            free(frame_set->tr_particle_data);
+            frame_set->tr_particle_data = 0;
+            return(TNG_CRITICAL);
+        }
+        frame_set->tr_particle_data = data;
+    }
+    else
+    {
+        tng_data->n_particle_data_blocks++;
+        data = realloc(tng_data->non_tr_particle_data,
+                        sizeof(struct tng_particle_data) *
+                        tng_data->n_particle_data_blocks);
+        if(!data)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+                    sizeof(struct tng_particle_data) *
+                    tng_data->n_particle_data_blocks,
+                    __FILE__, __LINE__);
+            free(tng_data->non_tr_particle_data);
+            tng_data->non_tr_particle_data = 0;
+            return(TNG_CRITICAL);
+        }
+        tng_data->non_tr_particle_data = data;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_compress(tng_trajectory_t tng_data,
+                                        tng_gen_block_t block,
+                                        const int64_t n_frames,
+                                        const int64_t n_particles,
+                                        const char type,
+                                        void *start_pos)
+{
+    int nalgo;
+    int new_len;
+    int *alt_algo = 0;
+    char *dest, *temp;
+    int64_t algo_find_n_frames;
+    unsigned long offset;
+    float f_precision;
+    double d_precision;
+
+    if(block->id != TNG_TRAJ_POSITIONS &&
+       block->id != TNG_TRAJ_VELOCITIES)
+    {
+        fprintf(stderr, "TNG library: Can only compress positions and velocities with the "
+               "TNG method. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+    if(type != TNG_FLOAT_DATA && type != TNG_DOUBLE_DATA)
+    {
+        fprintf(stderr, "TNG library: Data type not supported. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    if(n_frames <= 0 || n_particles <= 0)
+    {
+        fprintf(stderr, "TNG library: Missing frames or particles. Cannot compress data "
+               "with the TNG method. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    f_precision = 1/tng_data->compression_precision;
+    d_precision = 1/tng_data->compression_precision;
+
+    if(block->id == TNG_TRAJ_POSITIONS)
+    {
+        /* If there is only one frame in this frame set and there might be more
+         * do not store the algorithm as the compression algorithm, but find
+         * the best one without storing it */
+        if(n_frames == 1 && tng_data->frame_set_n_frames > 1)
+        {
+            nalgo = tng_compress_nalgo();
+            alt_algo=malloc(nalgo * sizeof *tng_data->compress_algo_pos);
+            if(type == TNG_FLOAT_DATA)
+            {
+                dest = tng_compress_pos_float_find_algo(start_pos, (int)n_particles,
+                                                        (int)n_frames,
+                                                        f_precision,
+                                                        0, alt_algo,
+                                                        &new_len);
+
+            }
+            else
+            {
+                dest = tng_compress_pos_find_algo(start_pos, (int)n_particles,
+                                           (int)n_frames,
+                                           d_precision,
+                                           0, alt_algo,
+                                           &new_len);
+            }
+        }
+        else if(!tng_data->compress_algo_pos)
+        {
+            if(n_frames > 10)
+            {
+                algo_find_n_frames = 5;
+            }
+            else
+            {
+                algo_find_n_frames = n_frames;
+            }
+
+            nalgo = tng_compress_nalgo();
+            tng_data->compress_algo_pos=malloc(nalgo *
+                                           sizeof *tng_data->compress_algo_pos);
+            if(type == TNG_FLOAT_DATA)
+            {
+                dest = tng_compress_pos_float_find_algo(start_pos, (int)n_particles,
+                                                        (int)algo_find_n_frames,
+                                                        f_precision,
+                                                        0, tng_data->
+                                                        compress_algo_pos,
+                                                        &new_len);
+
+                if(algo_find_n_frames < n_frames)
+                {
+                    dest = tng_compress_pos_float(start_pos, (int)n_particles,
+                                                  (int)n_frames,
+                                                  f_precision,
+                                                  0, tng_data->compress_algo_pos,
+                                                  &new_len);
+                }
+            }
+            else
+            {
+                dest = tng_compress_pos_find_algo(start_pos, (int)n_particles,
+                                           (int)algo_find_n_frames,
+                                           d_precision,
+                                           0, tng_data->
+                                           compress_algo_pos,
+                                           &new_len);
+
+                if(algo_find_n_frames < n_frames)
+                {
+                    dest = tng_compress_pos(start_pos, (int)n_particles,
+                                            (int)n_frames,
+                                            d_precision, 0,
+                                            tng_data->compress_algo_pos,
+                                            &new_len);
+                }
+            }
+        }
+        else
+        {
+            if(type == TNG_FLOAT_DATA)
+            {
+                dest = tng_compress_pos_float(start_pos, (int)n_particles,
+                                              (int)n_frames,
+                                              f_precision, 0,
+                                              tng_data->compress_algo_pos, &new_len);
+            }
+            else
+            {
+                dest = tng_compress_pos(start_pos, (int)n_particles,
+                                        (int)n_frames,
+                                        d_precision, 0,
+                                        tng_data->compress_algo_pos,
+                                        &new_len);
+            }
+        }
+    }
+    else if(block->id == TNG_TRAJ_VELOCITIES)
+    {
+        /* If there is only one frame in this frame set and there might be more
+         * do not store the algorithm as the compression algorithm, but find
+         * the best one without storing it */
+        if(n_frames == 1 && tng_data->frame_set_n_frames > 1)
+        {
+            nalgo = tng_compress_nalgo();
+            alt_algo=malloc(nalgo * sizeof *tng_data->compress_algo_pos);
+            if(type == TNG_FLOAT_DATA)
+            {
+                dest = tng_compress_vel_float_find_algo(start_pos, (int)n_particles,
+                                                        (int)n_frames,
+                                                        f_precision,
+                                                        0, alt_algo,
+                                                        &new_len);
+
+            }
+            else
+            {
+                dest = tng_compress_vel_find_algo(start_pos, (int)n_particles,
+                                                  (int)n_frames,
+                                                  d_precision,
+                                                  0, alt_algo,
+                                                  &new_len);
+            }
+        }
+        else if(!tng_data->compress_algo_vel)
+        {
+            if(n_frames > 10)
+            {
+                algo_find_n_frames = 5;
+            }
+            else
+            {
+                algo_find_n_frames = n_frames;
+            }
+
+            nalgo = tng_compress_nalgo();
+            tng_data->compress_algo_vel=malloc(nalgo *
+                                           sizeof *tng_data->compress_algo_vel);
+
+            if(type == TNG_FLOAT_DATA)
+            {
+                dest = tng_compress_vel_float_find_algo(start_pos, (int)n_particles,
+                                                        (int)algo_find_n_frames,
+                                                        f_precision,
+                                                        0, tng_data->
+                                                        compress_algo_vel,
+                                                        &new_len);
+                if(algo_find_n_frames < n_frames)
+                {
+                    dest = tng_compress_vel_float(start_pos, (int)n_particles,
+                                                  (int)n_frames,
+                                                  f_precision,
+                                                  0, tng_data->compress_algo_vel,
+                                                  &new_len);
+                }
+            }
+            else
+            {
+                dest = tng_compress_vel_find_algo(start_pos, (int)n_particles,
+                                                  (int)algo_find_n_frames,
+                                                  d_precision,
+                                                  0, tng_data->
+                                                  compress_algo_vel,
+                                                  &new_len);
+                if(algo_find_n_frames < n_frames)
+                {
+                    dest = tng_compress_vel(start_pos, (int)n_particles,
+                                            (int)n_frames,
+                                            d_precision,
+                                            0, tng_data->compress_algo_vel,
+                                            &new_len);
+                }
+            }
+        }
+        else
+        {
+            if(type == TNG_FLOAT_DATA)
+            {
+                dest = tng_compress_vel_float(start_pos, (int)n_particles,
+                                              (int)n_frames,
+                                              f_precision,
+                                              0, tng_data->
+                                              compress_algo_vel,
+                                              &new_len);
+            }
+            else
+            {
+                dest = tng_compress_vel(start_pos, (int)n_particles,
+                                        (int)n_frames,
+                                        d_precision,
+                                        0, tng_data->
+                                        compress_algo_vel,
+                                        &new_len);
+            }
+        }
+    }
+    else
+    {
+        fprintf(stderr, "TNG library: Can only compress positions and velocities using TNG-MF1 algorithms.\n");
+        return(TNG_FAILURE);
+    }
+
+    offset = (unsigned long)((char *)start_pos - block->block_contents);
+
+    if(alt_algo)
+    {
+        free(alt_algo);
+    }
+
+    block->block_contents_size = new_len + offset;
+
+    temp = realloc(block->block_contents, block->block_contents_size);
+    if(!temp)
+    {
+        free(block->block_contents);
+        block->block_contents = 0;
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+    block->block_contents = temp;
+    if(dest)
+    {
+        memcpy(temp + offset, dest, new_len);
+        free(dest);
+    }
+    else
+    {
+        fprintf(stderr, "TNG library: Error during TNG compression. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_uncompress(tng_trajectory_t tng_data,
+                                          tng_gen_block_t block,
+                                          const char type,
+                                          void *start_pos,
+                                          const unsigned long uncompressed_len)
+{
+    char *temp;
+    double *d_dest = 0;
+    float *f_dest = 0;
+    unsigned long offset;
+    int result;
+    (void)tng_data;
+
+    TNG_ASSERT(uncompressed_len, "TNG library: The full length of the uncompressed data must be > 0.");
+
+    if(block->id != TNG_TRAJ_POSITIONS &&
+       block->id != TNG_TRAJ_VELOCITIES)
+    {
+        fprintf(stderr, "TNG library: Can only uncompress positions and velocities with the"
+               "TNG method.\n");
+        return(TNG_FAILURE);
+    }
+    if(type != TNG_FLOAT_DATA && type != TNG_DOUBLE_DATA)
+    {
+        fprintf(stderr, "TNG library: Data type not supported.\n");
+        return(TNG_FAILURE);
+    }
+
+    if(type == TNG_FLOAT_DATA)
+    {
+        f_dest = malloc(uncompressed_len);
+        if(!f_dest)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+                uncompressed_len, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        result = tng_compress_uncompress_float(start_pos, f_dest);
+    }
+    else
+    {
+        d_dest = malloc(uncompressed_len);
+        if(!d_dest)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+                uncompressed_len, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        result = tng_compress_uncompress(start_pos, d_dest);
+    }
+
+    if(result == 1)
+    {
+        fprintf(stderr, "TNG library: Cannot uncompress TNG compressed block.\n");
+        return(TNG_FAILURE);
+    }
+
+    offset = (unsigned long)((char *)start_pos - (char *)block->block_contents);
+
+    block->block_contents_size = (int64_t)(uncompressed_len + offset);
+
+    temp = realloc(block->block_contents, uncompressed_len + offset);
+    if(!temp)
+    {
+        free(block->block_contents);
+        block->block_contents = 0;
+        if(d_dest)
+        {
+            free(d_dest);
+        }
+        if(f_dest)
+        {
+            free(f_dest);
+        }
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(type == TNG_FLOAT_DATA)
+    {
+        memcpy(temp + offset, f_dest, uncompressed_len);
+    }
+    else
+    {
+        memcpy(temp + offset, d_dest, uncompressed_len);
+    }
+
+    block->block_contents = temp;
+
+    if(d_dest)
+    {
+        free(d_dest);
+    }
+    if(f_dest)
+    {
+        free(f_dest);
+    }
+    return(TNG_SUCCESS);
+}
+
+#ifdef USE_ZLIB
+static tng_function_status tng_gzip_compress(tng_trajectory_t tng_data,
+                                             tng_gen_block_t block,
+                                             void *start_pos, const int len)
+{
+    Bytef *dest;
+    char *temp;
+    unsigned long max_len, stat, offset;
+    (void)tng_data;
+
+    max_len = compressBound(len);
+    dest = malloc(max_len);
+
+    stat = compress(dest, &max_len, start_pos, len);
+    if(stat != (unsigned long)Z_OK)
+    {
+        free(dest);
+        if(stat == (unsigned long)Z_MEM_ERROR)
+        {
+            fprintf(stderr, "TNG library: Not enough memory. ");
+        }
+        else if(stat == (unsigned long)Z_BUF_ERROR)
+        {
+            fprintf(stderr, "TNG library: Destination buffer too small. ");
+        }
+        fprintf(stderr, "TNG library: Error gzipping data. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    offset = (char *)start_pos - block->block_contents;
+
+    block->block_contents_size = max_len + offset;
+
+    temp = realloc(block->block_contents, block->block_contents_size);
+    if(!temp)
+    {
+        free(block->block_contents);
+        free(dest);
+        block->block_contents = 0;
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    block->block_contents = temp;
+
+    memcpy(temp + offset, dest, max_len);
+
+    free(dest);
+
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_gzip_uncompress(tng_trajectory_t tng_data,
+                                               tng_gen_block_t block,
+                                               void *start_pos,
+                                               unsigned long uncompressed_len)
+{
+    Bytef *dest;
+    char *temp;
+    unsigned long stat;
+    int offset;
+    (void)tng_data;
+
+    offset = (char *)start_pos - (char *)block->block_contents;
+
+    dest = malloc(uncompressed_len);
+
+    stat = uncompress(dest, &uncompressed_len, (Bytef *) start_pos,
+                      block->block_contents_size - offset);
+
+    if(stat != Z_OK)
+    {
+        free(dest);
+        if(stat == (unsigned long)Z_MEM_ERROR)
+        {
+            fprintf(stderr, "TNG library: Not enough memory. ");
+        }
+        else if(stat == (unsigned long)Z_BUF_ERROR)
+        {
+            fprintf(stderr, "TNG library: Destination buffer too small. ");
+        }
+        else if(stat == (unsigned long)Z_DATA_ERROR)
+        {
+            fprintf(stderr, "TNG library: Data corrupt. ");
+        }
+        fprintf(stderr, "TNG library: Error uncompressing gzipped data. %s: %d\n", __FILE__,
+               __LINE__);
+        return(TNG_FAILURE);
+    }
+
+
+    block->block_contents_size = uncompressed_len + offset;
+
+    temp = realloc(block->block_contents, uncompressed_len + offset);
+    if(!temp)
+    {
+        free(block->block_contents);
+        block->block_contents = 0;
+        free(dest);
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    memcpy(temp + offset, dest, uncompressed_len);
+
+    block->block_contents = temp;
+
+    free(dest);
+    return(TNG_SUCCESS);
+}
+#endif
+
+/** Allocate memory for storing particle data.
+ * The allocated block will be refered to by data->values.
+ * @param tng_data is a trajectory data container.
+ * @param data is the data struct, which will contain the allocated memory in
+ * data->values.
+ * @param n_frames is the number of frames of data to store.
+ * @param n_particles is the number of particles with data.
+ * @param n_values_per_frame is the number of data values per particle and
+ * frame.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_allocate_particle_data_mem
+                (tng_trajectory_t tng_data,
+                 tng_particle_data_t data,
+                 int64_t n_frames,
+                 int64_t stride_length,
+                 const int64_t n_particles,
+                 const int64_t n_values_per_frame)
+{
+    void ***values;
+    int64_t i, j, k, size, frame_alloc;
+    (void)tng_data;
+
+    if(n_particles == 0 || n_values_per_frame == 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(data->strings && data->datatype == TNG_CHAR_DATA)
+    {
+        for(i = data->n_frames; i--;)
+        {
+            for(j = n_particles; j--;)
+            {
+                for(k = data->n_values_per_frame; k--;)
+                {
+                    if(data->strings[i][j][k])
+                    {
+                        free(data->strings[i][j][k]);
+                    }
+                }
+                free(data->strings[i][j]);
+            }
+            free(data->strings[i]);
+        }
+        free(data->strings);
+    }
+    data->n_frames = n_frames;
+    n_frames = tng_max_i64(1, n_frames);
+    data->stride_length = tng_max_i64(1, stride_length);
+    data->n_values_per_frame = n_values_per_frame;
+    frame_alloc = (n_frames % stride_length) ? n_frames / stride_length + 1 : n_frames / stride_length;
+
+    if(data->datatype == TNG_CHAR_DATA)
+    {
+        data->strings = malloc(sizeof(char ***) * frame_alloc);
+        for(i = frame_alloc; i-- ;)
+        {
+            data->strings[i] = malloc(sizeof(char **) *
+                                    n_particles);
+            if(!data->strings[i])
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                    sizeof(union data_values *) * n_particles,
+                    __FILE__, __LINE__);
+                return(TNG_CRITICAL);
+            }
+            for(j = n_particles; j--;)
+            {
+                data->strings[i][j] = malloc(sizeof(char *) *
+                                            n_values_per_frame);
+                if(!data->strings[i][j])
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                        sizeof(union data_values) * n_values_per_frame,
+                        __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+                for(k = n_values_per_frame; k--;)
+                {
+                    data->strings[i][j][k] = 0;
+                }
+            }
+        }
+    }
+    else
+    {
+        switch(data->datatype)
+        {
+        case TNG_INT_DATA:
+            size = sizeof(int64_t);
+            break;
+        case TNG_FLOAT_DATA:
+            size = sizeof(float);
+            break;
+        case TNG_DOUBLE_DATA:
+        default:
+            size = sizeof(double);
+        }
+
+        values = realloc(data->values,
+                         size * frame_alloc *
+                         n_particles * n_values_per_frame);
+        if(!values)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   size * frame_alloc *
+                   n_particles * n_values_per_frame,
+                   __FILE__, __LINE__);
+            free(data->values);
+            data->values = 0;
+            return(TNG_CRITICAL);
+        }
+        data->values = values;
+    }
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_particle_data_find
+                (tng_trajectory_t tng_data,
+                 const int64_t id,
+                 tng_particle_data_t *data)
+{
+    int64_t block_index, i;
+    tng_trajectory_frame_set_t frame_set = &tng_data->
+                                           current_trajectory_frame_set;
+    char block_type_flag;
+
+    if(tng_data->current_trajectory_frame_set_input_file_pos > 0 ||
+       tng_data->current_trajectory_frame_set_output_file_pos > 0)
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+    }
+    else
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+    }
+
+    block_index = -1;
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        for(i = frame_set->n_particle_data_blocks; i-- ;)
+        {
+            *data = &frame_set->tr_particle_data[i];
+            if((*data)->block_id == id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+    }
+    else
+    {
+        for(i = tng_data->n_particle_data_blocks; i-- ;)
+        {
+            *data = &tng_data->non_tr_particle_data[i];
+            if((*data)->block_id == id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+    }
+    if(block_index == -1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_data_find
+                (tng_trajectory_t tng_data,
+                 const int64_t id,
+                 tng_non_particle_data_t *data)
+{
+    int64_t block_index, i;
+    tng_trajectory_frame_set_t frame_set = &tng_data->
+                                           current_trajectory_frame_set;
+    char block_type_flag;
+
+    if(tng_data->current_trajectory_frame_set_input_file_pos > 0 ||
+       tng_data->current_trajectory_frame_set_output_file_pos > 0)
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+    }
+    else
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+    }
+
+    block_index = -1;
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        for(i = frame_set->n_data_blocks; i-- ;)
+        {
+            *data = &frame_set->tr_data[i];
+            if((*data)->block_id == id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+        if(block_index == -1)
+        {
+            for(i = tng_data->n_data_blocks; i-- ;)
+            {
+                *data = &tng_data->non_tr_data[i];
+                if((*data)->block_id == id)
+                {
+                    block_index = i;
+                    break;
+                }
+            }
+        }
+    }
+    else
+    {
+        for(i = tng_data->n_data_blocks; i-- ;)
+        {
+            *data = &tng_data->non_tr_data[i];
+            if((*data)->block_id == id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+    }
+    if(block_index == -1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Read the values of a particle data block
+ * @param tng_data is a trajectory data container.
+ * @param block is the block to store the data (should already contain
+ * the block headers and the block contents).
+ * @param offset is the reading offset to point at the place where the actual
+ * values are stored, starting from the beginning of the block_contents. The
+ * offset is changed during the reading.
+ * @param datatype is the type of data of the data block (char, int, float or
+ * double).
+ * @param num_first_particle is the number of the first particle in the data
+ * block. This should be the same as in the corresponding particle mapping
+ * block.
+ * @param n_particles is the number of particles in the data block. This should
+ * be the same as in the corresponding particle mapping block.
+ * @param first_frame_with_data is the frame number of the first frame with data
+ * in this data block.
+ * @param stride_length is the number of frames between each data entry.
+ * @param n_frames is the number of frames in this data block.
+ * @param n_values is the number of values per particle and frame stored in this
+ * data block.
+ * @param codec_id is the ID of the codec to compress the data.
+ * @param multiplier is the multiplication factor applied to each data value
+ * before compression. This factor is applied since some compression algorithms
+ * work only on integers.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_particle_data_read
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 int *offset,
+                 const char datatype,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t first_frame_with_data,
+                 const int64_t stride_length,
+                 int64_t n_frames,
+                 const int64_t n_values,
+                 const int64_t codec_id,
+                 const double multiplier)
+{
+    int64_t i, j, k, tot_n_particles, n_frames_div;
+    int size, len;
+    unsigned long data_size;
+    char ***first_dim_values, **second_dim_values;
+    tng_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+    char block_type_flag;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+    switch(datatype)
+    {
+    case TNG_CHAR_DATA:
+        size = 1;
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    /* If the block does not exist, create it */
+    if(tng_particle_data_find(tng_data, block->id, &data) != TNG_SUCCESS)
+    {
+        if(tng_data->current_trajectory_frame_set_input_file_pos > 0)
+        {
+            block_type_flag = TNG_TRAJECTORY_BLOCK;
+        }
+        else
+        {
+            block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+        }
+
+        if(tng_particle_data_block_create(tng_data, block_type_flag) !=
+           TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create particle data block. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            data = &frame_set->tr_particle_data[frame_set->
+                                                n_particle_data_blocks - 1];
+        }
+        else
+        {
+            data = &tng_data->non_tr_particle_data[tng_data->
+                                                   n_particle_data_blocks - 1];
+        }
+        data->block_id = block->id;
+
+        data->block_name = malloc(strlen(block->name) + 1);
+        if(!data->block_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                   (int)strlen(block->name)+1, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        strcpy(data->block_name, block->name);
+
+        data->datatype = datatype;
+
+        data->values = 0;
+        /* FIXME: Memory leak from strings. */
+        data->strings = 0;
+        data->n_frames = 0;
+        data->codec_id = codec_id;
+        data->compression_multiplier = multiplier;
+        data->last_retrieved_frame = -1;
+    }
+
+    if(/*block_type_flag == TNG_TRAJECTORY_BLOCK &&*/
+       tng_data->current_trajectory_frame_set_input_file_pos > 0 &&
+       tng_data->var_num_atoms_flag)
+    {
+        tot_n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        tot_n_particles = tng_data->n_particles;
+    }
+
+    n_frames_div = (n_frames % stride_length) ? n_frames / stride_length + 1 : n_frames / stride_length;
+
+    if(codec_id != TNG_UNCOMPRESSED)
+    {
+        data_size = (unsigned long)(n_frames_div * size * n_particles * n_values);
+        switch(codec_id)
+        {
+        case TNG_XTC_COMPRESSION:
+            fprintf(stderr, "TNG library: XTC compression not implemented yet.\n");
+            break;
+        case TNG_TNG_COMPRESSION:
+/*            fprintf(stderr, "TNG library: Before TNG uncompression: %"PRId64"\n", block->block_contents_size);*/
+            if(tng_uncompress(tng_data, block, datatype,
+                              block->block_contents + *offset,
+                              data_size) != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Could not read tng compressed block data. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(TNG_CRITICAL);
+            }
+/*            fprintf(stderr, "TNG library: After TNG uncompression: %"PRId64"\n", block->block_contents_size);*/
+            break;
+#ifdef USE_ZLIB
+        case TNG_GZIP_COMPRESSION:
+/*            fprintf(stderr, "TNG library: Before GZIP uncompression: %"PRId64"\n", block->block_contents_size);*/
+            if(tng_gzip_uncompress(tng_data, block,
+                                   block->block_contents + *offset,
+                                   data_size) != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Could not read gzipped block data. %s: %d\n", __FILE__,
+                    __LINE__);
+                return(TNG_CRITICAL);
+            }
+/*            fprintf(stderr, "TNG library: After GZIP uncompression: %"PRId64"\n", block->block_contents_size);*/
+            break;
+#endif
+        }
+    }
+    /* Allocate memory */
+    if(!data->values || data->n_frames != n_frames ||
+       data->n_values_per_frame != n_values)
+    {
+        if(tng_allocate_particle_data_mem(tng_data, data, n_frames,
+                                          stride_length,
+                                          tot_n_particles, n_values) !=
+           TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory for particle data. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    data->first_frame_with_data = first_frame_with_data;
+
+    if(datatype == TNG_CHAR_DATA)
+    {
+        for(i = 0; i < n_frames_div; i++)
+        {
+            first_dim_values = data->strings[i];
+            for(j = num_first_particle; j < num_first_particle + n_particles;
+                j++)
+            {
+                second_dim_values = first_dim_values[j];
+                for(k = 0; k < n_values; k++)
+                {
+                    len = tng_min_i((int)strlen(block->block_contents+*offset) + 1,
+                              TNG_MAX_STR_LEN);
+                    if(second_dim_values[k])
+                    {
+                        free(second_dim_values[k]);
+                    }
+                    second_dim_values[k] = malloc(len);
+                    if(!second_dim_values[k])
+                    {
+                        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                            len, __FILE__, __LINE__);
+                        return(TNG_CRITICAL);
+                    }
+                    strncpy(second_dim_values[k],
+                            block->block_contents+*offset, len);
+                    *offset += len;
+                }
+            }
+        }
+    }
+    else
+    {
+        memcpy((char *)data->values + n_frames_div * size * n_values *
+               num_first_particle,
+               block->block_contents + *offset,
+               block->block_contents_size - *offset);
+        switch(datatype)
+        {
+        case TNG_FLOAT_DATA:
+            if(tng_data->input_endianness_swap_func_32)
+            {
+                for(i = 0; i < (block->block_contents_size - *offset); i+=size)
+                {
+                    if(tng_data->input_endianness_swap_func_32(tng_data,
+                        (int32_t *)((char *)data->values + i))
+                        != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+            }
+            break;
+        case TNG_INT_DATA:
+        case TNG_DOUBLE_DATA:
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                for(i = 0; i < (block->block_contents_size - *offset); i+=size)
+                {
+                    if(tng_data->input_endianness_swap_func_64(tng_data,
+                        (int64_t *)((char *)data->values + i))
+                        != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+            }
+            break;
+        case TNG_CHAR_DATA:
+            break;
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Write a particle data block
+ * @param tng_data is a trajectory data container.
+ * @param block is the block to store the data (should already contain
+ * the block headers and the block contents).
+ * @param block_index is the index number of the data block in the frame set.
+ * @param mapping is the particle mapping that is relevant for the data block.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_particle_data_block_write
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const int64_t block_index,
+                 const tng_particle_mapping_t mapping,
+                 const char hash_mode)
+{
+    int64_t n_particles, num_first_particle, n_frames, stride_length;
+    int64_t frame_step, data_start_pos;
+    int64_t i, j, k;
+    int size;
+    size_t len, offset = 0;
+    char dependency, temp, *temp_name;
+    double multiplier;
+    char ***first_dim_values, **second_dim_values;
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+
+    tng_particle_data_t data;
+    char block_type_flag;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    /* If we have already started writing frame sets it is too late to write
+     * non-trajectory data blocks */
+    if(tng_data->current_trajectory_frame_set_output_file_pos > 0)
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+    }
+    else
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+    }
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        data = &frame_set->tr_particle_data[block_index];
+
+        /* If this data block has not had any data added in this frame set
+         * do not write it. */
+        if(data->first_frame_with_data < frame_set->first_frame)
+        {
+            return(TNG_SUCCESS);
+        }
+
+        stride_length = tng_max_i64(1, data->stride_length);
+    }
+    else
+    {
+        data = &tng_data->non_tr_particle_data[block_index];
+        stride_length = 1;
+    }
+
+    switch(data->datatype)
+    {
+    case TNG_CHAR_DATA:
+        size = 1;
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    len = strlen(data->block_name) + 1;
+
+    if(!block->name || strlen(block->name) < len)
+    {
+        temp_name = realloc(block->name, len);
+        if(!temp_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            free(block->name);
+            block->name = 0;
+            return(TNG_CRITICAL);
+        }
+        block->name = temp_name;
+    }
+    strncpy(block->name, data->block_name, len);
+    block->id = data->block_id;
+
+    /* If writing frame independent data data->n_frames is 0, but n_frames
+       is used for the loop writing the data (and reserving memory) and needs
+       to be at least 1 */
+    n_frames = tng_max_i64(1, data->n_frames);
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        /* If the frame set is finished before writing the full number of frames
+           make sure the data block is not longer than the frame set. */
+        n_frames = tng_min_i64(n_frames, frame_set->n_frames);
+
+        n_frames -= (data->first_frame_with_data - frame_set->first_frame);
+    }
+
+    frame_step = (n_frames % stride_length) ? n_frames / stride_length + 1:
+                 n_frames / stride_length;
+
+    /* TNG compression will use compression precision to get integers from
+     * floating point data. The compression multiplier stores that information
+     * to be able to return the precision of the compressed data. */
+    if(data->codec_id == TNG_TNG_COMPRESSION)
+    {
+        data->compression_multiplier = tng_data->compression_precision;
+    }
+    /* Uncompressed data blocks do not use compression multipliers at all.
+     * GZip compression does not need it either. */
+    else if(data->codec_id == TNG_UNCOMPRESSED || data->codec_id == TNG_GZIP_COMPRESSION)
+    {
+        data->compression_multiplier = 1.0;
+    }
+
+    if(mapping && mapping->n_particles != 0)
+    {
+        n_particles = mapping->n_particles;
+        num_first_particle = mapping->num_first_particle;
+    }
+    else
+    {
+        num_first_particle = 0;
+        if(tng_data->var_num_atoms_flag)
+        {
+            n_particles = frame_set->n_particles;
+        }
+        else
+        {
+            n_particles = tng_data->n_particles;
+        }
+    }
+
+    block->block_contents_size = sizeof(char) * 2 +
+                                 sizeof(data->n_values_per_frame) +
+                                 sizeof(data->codec_id) +
+                                 sizeof(num_first_particle) +
+                                 sizeof(n_particles);
+
+    if(stride_length > 1)
+    {
+        block->block_contents_size += sizeof(data->first_frame_with_data) +
+                                      sizeof(data->stride_length);
+    }
+
+    if(data->codec_id != TNG_UNCOMPRESSED)
+    {
+        block->block_contents_size += sizeof(data->compression_multiplier);
+    }
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK && data->n_frames > 0)
+    {
+        dependency = TNG_FRAME_DEPENDENT + TNG_PARTICLE_DEPENDENT;
+    }
+    else
+    {
+        dependency = TNG_PARTICLE_DEPENDENT;
+    }
+    if(dependency & TNG_FRAME_DEPENDENT)
+    {
+        block->block_contents_size += sizeof(char);
+    }
+
+    data_start_pos = block->block_contents_size;
+
+    if(data->datatype == TNG_CHAR_DATA)
+    {
+        for(i = n_frames; i--;)
+        {
+            first_dim_values = data->strings[i];
+            for(j = num_first_particle; j < num_first_particle + n_particles;
+                j++)
+            {
+                second_dim_values = first_dim_values[j];
+                for(k = data->n_values_per_frame; k--;)
+                {
+                    block->block_contents_size +=
+                    strlen(second_dim_values[k]) + 1;
+                }
+            }
+        }
+    }
+    else
+    {
+        block->block_contents_size += size * frame_step *
+                                      n_particles * data->n_values_per_frame;
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+
+    memcpy(block->block_contents, &data->datatype, sizeof(char));
+    offset += sizeof(char);
+
+    memcpy(block->block_contents+offset, &dependency, sizeof(char));
+    offset += sizeof(char);
+
+    if(dependency & TNG_FRAME_DEPENDENT)
+    {
+        if(stride_length > 1)
+        {
+            temp = 1;
+        }
+        else
+        {
+            temp = 0;
+        }
+        memcpy(block->block_contents+offset, &temp, sizeof(char));
+        offset += sizeof(char);
+    }
+
+    memcpy(block->block_contents+offset, &data->n_values_per_frame,
+           sizeof(data->n_values_per_frame));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+           (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(data->n_values_per_frame);
+
+    memcpy(block->block_contents+offset, &data->codec_id,
+           sizeof(data->codec_id));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+           (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(data->codec_id);
+
+    if(data->codec_id != TNG_UNCOMPRESSED)
+    {
+        memcpy(block->block_contents+offset, &data->compression_multiplier,
+               sizeof(data->compression_multiplier));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+               (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(data->compression_multiplier);
+    }
+
+    if(data->n_frames > 0 && stride_length > 1)
+    {
+        /* FIXME: first_frame_with_data is not reliably set */
+        if(data->first_frame_with_data == 0)
+        {
+            data->first_frame_with_data = frame_set->first_frame;
+        }
+        memcpy(block->block_contents+offset, &data->first_frame_with_data,
+               sizeof(data->first_frame_with_data));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+               (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(data->first_frame_with_data);
+
+        memcpy(block->block_contents+offset, &stride_length,
+               sizeof(stride_length));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+               (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(stride_length);
+    }
+
+
+    memcpy(block->block_contents+offset, &num_first_particle,
+           sizeof(num_first_particle));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+           (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(num_first_particle);
+
+    memcpy(block->block_contents+offset, &n_particles, sizeof(n_particles));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+           (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(n_particles);
+
+    if(data->datatype == TNG_CHAR_DATA)
+    {
+        if(data->strings)
+        {
+            for(i = 0; i < frame_step; i++)
+            {
+                first_dim_values = data->strings[i];
+                for(j = num_first_particle; j < num_first_particle + n_particles;
+                    j++)
+                {
+                    second_dim_values = first_dim_values[j];
+                    for(k = 0; k < data->n_values_per_frame; k++)
+                    {
+                        len = (unsigned int)strlen(second_dim_values[k]) + 1;
+                        strncpy(block->block_contents+offset,
+                                second_dim_values[k], len);
+                        offset += len;
+                    }
+                }
+            }
+        }
+    }
+    else if(data->values)
+    {
+        memcpy(block->block_contents + offset, data->values,
+               block->block_contents_size - offset);
+
+        switch(data->datatype)
+        {
+        case TNG_FLOAT_DATA:
+            if(data->codec_id == TNG_UNCOMPRESSED || data-> codec_id == TNG_GZIP_COMPRESSION ||
+               data->codec_id == TNG_TNG_COMPRESSION)
+            {
+                if(tng_data->input_endianness_swap_func_32)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        if(tng_data->input_endianness_swap_func_32(tng_data,
+                           (int32_t *)(block->block_contents + i))
+                           != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                multiplier = data->compression_multiplier;
+                if(fabs(multiplier - 1.0) > 0.00001 ||
+                   tng_data->input_endianness_swap_func_32)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        *(float *)(block->block_contents + i) *= (float)multiplier;
+                        if(tng_data->input_endianness_swap_func_32 &&
+                        tng_data->input_endianness_swap_func_32(tng_data,
+                        (int32_t *)(block->block_contents + i))
+                        != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            break;
+        case TNG_INT_DATA:
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                for(i = offset; i < block->block_contents_size; i+=size)
+                {
+                    if(tng_data->input_endianness_swap_func_64(tng_data,
+                       (int64_t *)(block->block_contents + i))
+                       != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+            }
+            break;
+        case TNG_DOUBLE_DATA:
+            if(data->codec_id == TNG_UNCOMPRESSED || data-> codec_id == TNG_GZIP_COMPRESSION ||
+               data->codec_id == TNG_TNG_COMPRESSION)
+            {
+                if(tng_data->input_endianness_swap_func_64)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        if(tng_data->input_endianness_swap_func_64(tng_data,
+                           (int64_t *)(block->block_contents + i))
+                           != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                multiplier = data->compression_multiplier;
+                if(fabs(multiplier - 1.0) > 0.00001 ||
+                   tng_data->input_endianness_swap_func_64)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        *(double *)(block->block_contents + i) *= multiplier;
+                        if(tng_data->input_endianness_swap_func_64 &&
+                        tng_data->input_endianness_swap_func_64(tng_data,
+                        (int64_t *)(block->block_contents + i))
+                        != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            break;
+        case TNG_CHAR_DATA:
+            break;
+        }
+    }
+    else
+    {
+        memset(block->block_contents+offset, 0, block->block_contents_size - offset);
+    }
+
+    frame_set->n_written_frames += frame_set->n_unwritten_frames;
+    frame_set->n_unwritten_frames = 0;
+
+    if(block_type_flag == TNG_NON_TRAJECTORY_BLOCK || frame_set->n_written_frames > 0)
+    {
+        switch(data->codec_id)
+        {
+        case TNG_XTC_COMPRESSION:
+            fprintf(stderr, "TNG library: XTC compression not implemented yet.\n");
+            data->codec_id = TNG_UNCOMPRESSED;
+            break;
+        case TNG_TNG_COMPRESSION:
+            stat = tng_compress(tng_data, block, frame_step,
+                                n_particles, data->datatype,
+                                block->block_contents + data_start_pos);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Could not write tng compressed block data. %s: %d\n",
+                    __FILE__, __LINE__);
+                if(stat == TNG_CRITICAL)
+                {
+                    return(TNG_CRITICAL);
+                }
+                /* Set the data again, but with no compression (to write only
+                 * the relevant data) */
+                data->codec_id = TNG_UNCOMPRESSED;
+                stat = tng_particle_data_block_write(tng_data, block,
+                                                     block_index, mapping,
+                                                     hash_mode);
+                return(stat);
+            }
+            break;
+#ifdef USE_ZLIB
+        case TNG_GZIP_COMPRESSION:
+    /*         fprintf(stderr, "TNG library: Before compression: %"PRId64"\n", block->block_contents_size);*/
+            stat = tng_gzip_compress(tng_data, block,
+                                     block->block_contents + data_start_pos,
+                                     block->block_contents_size - data_start_pos);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Could not write gzipped block data. %s: %d\n", __FILE__,
+                    __LINE__);
+                if(stat == TNG_CRITICAL)
+                {
+                    return(TNG_CRITICAL);
+                }
+                /* Set the data again, but with no compression (to write only
+                 * the relevant data) */
+                data->codec_id = TNG_UNCOMPRESSED;
+                stat = tng_particle_data_block_write(tng_data, block,
+                                                     block_index, mapping,
+                                                     hash_mode);
+                return(stat);
+            }
+    /*         fprintf(stderr, "TNG library: After compression: %"PRId64"\n", block->block_contents_size);*/
+            break;
+#endif
+        }
+    }
+
+    if(tng_block_header_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot write header of file %s. %s: %d\n",
+               tng_data->output_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(fwrite(block->block_contents, block->block_contents_size, 1,
+        tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n", __FILE__,
+                __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/* TEST: */
+/** Create a non-particle data block
+ * @param tng_data is a trajectory data container.
+ * @param block_type_flag specifies if this is a trajectory block or a
+ * non-trajectory block. (TNG_TRAJECTORY_BLOCK or TNG_NON_TRAJECTORY_BLOCK)
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_data_block_create
+                (tng_trajectory_t tng_data,
+                 const char block_type_flag)
+{
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+
+    tng_non_particle_data_t data;
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        frame_set->n_data_blocks++;
+        data = realloc(frame_set->tr_data, sizeof(struct tng_non_particle_data) *
+                       frame_set->n_data_blocks);
+        if(!data)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+                sizeof(struct tng_non_particle_data) * frame_set->n_data_blocks,
+                __FILE__, __LINE__);
+            free(frame_set->tr_data);
+            frame_set->tr_data = 0;
+            return(TNG_CRITICAL);
+        }
+        frame_set->tr_data = data;
+    }
+    else
+    {
+        tng_data->n_data_blocks++;
+        data = realloc(tng_data->non_tr_data, sizeof(struct tng_non_particle_data) *
+                        tng_data->n_data_blocks);
+        if(!data)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+                sizeof(struct tng_non_particle_data) * tng_data->n_data_blocks,
+                __FILE__, __LINE__);
+            free(tng_data->non_tr_data);
+            tng_data->non_tr_data = 0;
+            return(TNG_CRITICAL);
+        }
+        tng_data->non_tr_data = data;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/* TEST: */
+/** Allocate memory for storing non-particle data.
+ * The allocated block will be refered to by data->values.
+ * @param tng_data is a trajectory data container.
+ * @param data is the data struct, which will contain the allocated memory in
+ * data->values.
+ * @param n_frames is the number of frames of data to store.
+ * @param n_values_per_frame is the number of data values per frame.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_allocate_data_mem
+                (tng_trajectory_t tng_data,
+                 tng_non_particle_data_t data,
+                 int64_t n_frames,
+                 int64_t stride_length,
+                 const int64_t n_values_per_frame)
+{
+    void **values;
+    int64_t i, j, size, frame_alloc;
+    (void)tng_data;
+
+    if(data->strings && data->datatype == TNG_CHAR_DATA)
+    {
+        for(i = data->n_frames; i--;)
+        {
+            for(j = data->n_values_per_frame; j--;)
+            {
+                if(data->strings[i][j])
+                {
+                    free(data->strings[i][j]);
+                    data->strings[i][j] = 0;
+                }
+            }
+            free(data->strings[i]);
+            data->strings[i] = 0;
+        }
+        free(data->strings);
+    }
+    data->n_frames = n_frames;
+    data->stride_length = tng_max_i64(1, stride_length);
+    n_frames = tng_max_i64(1, n_frames);
+    data->n_values_per_frame = n_values_per_frame;
+    frame_alloc = (n_frames % stride_length) ? n_frames / stride_length + 1 : n_frames / stride_length;
+
+    if(data->datatype == TNG_CHAR_DATA)
+    {
+        data->strings = malloc(sizeof(char **) * frame_alloc);
+        for(i = frame_alloc; i-- ;)
+        {
+            data->strings[i] = malloc(sizeof(char *) * n_values_per_frame);
+            if(!data->strings[i])
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                       n_values_per_frame,
+                       __FILE__, __LINE__);
+                return(TNG_CRITICAL);
+            }
+            for(j = n_values_per_frame; j--;)
+            {
+                data->strings[i][j] = 0;
+            }
+        }
+    }
+    else
+    {
+        switch(data->datatype)
+        {
+        case TNG_INT_DATA:
+            size = sizeof(int64_t);
+            break;
+        case TNG_FLOAT_DATA:
+            size = sizeof(float);
+            break;
+        case TNG_DOUBLE_DATA:
+        default:
+            size = sizeof(double);
+        }
+
+        values = realloc(data->values,
+                         size * frame_alloc *
+                         n_values_per_frame);
+        if(!values)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   size * frame_alloc *
+                   n_values_per_frame,
+                   __FILE__, __LINE__);
+            free(data->values);
+            data->values = 0;
+            return(TNG_CRITICAL);
+        }
+        data->values = values;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Read the values of a non-particle data block
+ * @param tng_data is a trajectory data container.
+ * @param block is the block to store the data (should already contain
+ * the block headers and the block contents).
+ * @param offset is the reading offset to point at the place where the actual
+ * values are stored, starting from the beginning of the block_contents. The
+ * offset is changed during the reading.
+ * @param datatype is the type of data of the data block (char, int, float or
+ * double).
+ * @param first_frame_with_data is the frame number of the first frame with data
+ * in this data block.
+ * @param stride_length is the number of frames between each data entry.
+ * @param n_frames is the number of frames in this data block.
+ * @param n_values is the number of values per frame stored in this data block.
+ * @param codec_id is the ID of the codec to compress the data.
+ * @param multiplier is the multiplication factor applied to each data value
+ * before compression. This factor is applied since some compression algorithms
+ * work only on integers.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_data_read(tng_trajectory_t tng_data,
+                                         tng_gen_block_t block,
+                                         int *offset,
+                                         const char datatype,
+                                         const int64_t first_frame_with_data,
+                                         const int64_t stride_length,
+                                         int64_t n_frames,
+                                         const int64_t n_values,
+                                         const int64_t codec_id,
+                                         const double multiplier)
+{
+    int64_t i, j, n_frames_div;
+    int size, len;
+#ifdef USE_ZLIB
+    unsigned long data_size;
+#endif
+    tng_non_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+    char block_type_flag;
+
+    TNG_ASSERT(offset != 0, "TNG library: offset must not be a NULL pointer.");
+
+/*     fprintf(stderr, "TNG library: %s\n", block->name);*/
+
+    switch(datatype)
+    {
+    case TNG_CHAR_DATA:
+        size = 1;
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    /* If the block does not exist, create it */
+    if(tng_data_find(tng_data, block->id, &data) != TNG_SUCCESS)
+    {
+        if(tng_data->current_trajectory_frame_set_input_file_pos > 0)
+        {
+            block_type_flag = TNG_TRAJECTORY_BLOCK;
+        }
+        else
+        {
+            block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+        }
+
+        if(tng_data_block_create(tng_data, block_type_flag) !=
+            TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create particle data block. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            data = &frame_set->tr_data[frame_set->n_data_blocks - 1];
+        }
+        else
+        {
+            data = &tng_data->non_tr_data[tng_data->n_data_blocks - 1];
+        }
+        data->block_id = block->id;
+
+        data->block_name = malloc(strlen(block->name) + 1);
+        if(!data->block_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                   (int)strlen(block->name)+1, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        strcpy(data->block_name, block->name);
+
+        data->datatype = datatype;
+
+        data->values = 0;
+        /* FIXME: Memory leak from strings. */
+        data->strings = 0;
+        data->n_frames = 0;
+        data->codec_id = codec_id;
+        data->compression_multiplier = multiplier;
+        data->last_retrieved_frame = -1;
+    }
+
+    n_frames_div = (n_frames % stride_length) ? n_frames / stride_length + 1 : n_frames / stride_length;
+
+    if(codec_id != TNG_UNCOMPRESSED)
+    {
+        switch(codec_id)
+        {
+#ifdef USE_ZLIB
+        case TNG_GZIP_COMPRESSION:
+            data_size = n_frames_div * size * n_values;
+    /*         fprintf(stderr, "TNG library: Before compression: %"PRId64"\n", block->block_contents_size); */
+            if(tng_gzip_uncompress(tng_data, block,
+                                   block->block_contents + *offset,
+                                   data_size) != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Could not read gzipped block data. %s: %d\n", __FILE__,
+                    __LINE__);
+                return(TNG_CRITICAL);
+            }
+    /*         fprintf(stderr, "TNG library: After compression: %"PRId64"\n", block->block_contents_size); */
+            break;
+#endif
+        }
+    }
+
+    /* Allocate memory */
+    if(!data->values || data->n_frames != n_frames ||
+       data->n_values_per_frame != n_values)
+    {
+        if(tng_allocate_data_mem(tng_data, data, n_frames, stride_length,
+                                 n_values) !=
+           TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory for data. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    data->first_frame_with_data = first_frame_with_data;
+
+    if(datatype == TNG_CHAR_DATA)
+    {
+        for(i = 0; i < n_frames_div; i++)
+        {
+            for(j = 0; j < n_values; j++)
+            {
+                len = tng_min_i((int)strlen(block->block_contents+*offset) + 1,
+                              TNG_MAX_STR_LEN);
+                if(data->strings[i][j])
+                {
+                    free(data->strings[i][j]);
+                }
+                data->strings[i][j] = malloc(len);
+                if(!data->strings[i][j])
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                           len, __FILE__, __LINE__);
+                    return(TNG_CRITICAL);
+                }
+                strncpy(data->strings[i][j], block->block_contents+*offset,
+                        len);
+                *offset += len;
+            }
+        }
+    }
+    else
+    {
+        memcpy(data->values, block->block_contents + *offset,
+               block->block_contents_size - *offset);
+        switch(datatype)
+        {
+        case TNG_FLOAT_DATA:
+            if(tng_data->input_endianness_swap_func_32)
+            {
+                for(i = 0; i < (block->block_contents_size - *offset); i+=size)
+                {
+                    if(tng_data->input_endianness_swap_func_32(tng_data,
+                        (int32_t *)((char *)data->values + i))
+                        != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+            }
+            break;
+        case TNG_INT_DATA:
+        case TNG_DOUBLE_DATA:
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                for(i = 0; i < (block->block_contents_size - *offset); i+=size)
+                {
+                    if(tng_data->input_endianness_swap_func_64(tng_data,
+                        (int64_t *)((char *)data->values + i))
+                        != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+            }
+            break;
+        case TNG_CHAR_DATA:
+            break;
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Write a non-particle data block
+ * @param tng_data is a trajectory data container.
+ * @param block is the block to store the data (should already contain
+ * the block headers and the block contents).
+ * @param block_index is the index number of the data block in the frame set.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash will be generated and written.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_data_block_write(tng_trajectory_t tng_data,
+                                                tng_gen_block_t block,
+                                                const int64_t block_index,
+                                                const char hash_mode)
+{
+    int64_t n_frames, stride_length, frame_step;
+    int64_t i, j;
+    int offset = 0, size;
+    unsigned int len;
+#ifdef USE_ZLIB
+    int data_start_pos;
+    tng_function_status stat;
+#endif
+    char temp, dependency, *temp_name;
+    double multiplier;
+    tng_trajectory_frame_set_t frame_set =
+    &tng_data->current_trajectory_frame_set;
+
+    tng_non_particle_data_t data;
+    char block_type_flag;
+
+    /* If we have already started writing frame sets it is too late to write
+     * non-trajectory data blocks */
+    if(tng_data->current_trajectory_frame_set_output_file_pos > 0)
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+    }
+    else
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+    }
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        data = &frame_set->tr_data[block_index];
+
+        /* If this data block has not had any data added in this frame set
+         * do not write it. */
+        if(data->first_frame_with_data < frame_set->first_frame)
+        {
+            return(TNG_SUCCESS);
+        }
+
+        stride_length = tng_max_i64(1, data->stride_length);
+    }
+    else
+    {
+        data = &tng_data->non_tr_data[block_index];
+        stride_length = 1;
+    }
+
+    switch(data->datatype)
+    {
+    case TNG_CHAR_DATA:
+        size = 1;
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    len = (unsigned int)strlen(data->block_name) + 1;
+
+    if(!block->name || strlen(block->name) < len)
+    {
+        temp_name = realloc(block->name, len);
+        if(!temp_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len+1,
+                   __FILE__, __LINE__);
+            free(block->name);
+            block->name = 0;
+            return(TNG_CRITICAL);
+        }
+        block->name = temp_name;
+    }
+    strncpy(block->name, data->block_name, len);
+    block->id = data->block_id;
+
+    /* If writing frame independent data data->n_frames is 0, but n_frames
+       is used for the loop writing the data (and reserving memory) and needs
+       to be at least 1 */
+    n_frames = tng_max_i64(1, data->n_frames);
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+    {
+        /* If the frame set is finished before writing the full number of frames
+           make sure the data block is not longer than the frame set. */
+        n_frames = tng_min_i64(n_frames, frame_set->n_frames);
+
+        n_frames -= (data->first_frame_with_data - frame_set->first_frame);
+    }
+
+    frame_step = (n_frames % stride_length) ? n_frames / stride_length + 1:
+                 n_frames / stride_length;
+
+    /* TNG compression will use compression precision to get integers from
+     * floating point data. The compression multiplier stores that information
+     * to be able to return the precision of the compressed data. */
+    if(data->codec_id == TNG_TNG_COMPRESSION)
+    {
+        data->compression_multiplier = tng_data->compression_precision;
+    }
+    /* Uncompressed data blocks do not use compression multipliers at all.
+     * GZip compression does not need it either. */
+    else if(data->codec_id == TNG_UNCOMPRESSED || data->codec_id == TNG_GZIP_COMPRESSION)
+    {
+        data->compression_multiplier = 1.0;
+    }
+
+    block->block_contents_size = sizeof(char) * 2 +
+                                 sizeof(data->n_values_per_frame) +
+                                 sizeof(data->codec_id);
+
+    if(stride_length > 1)
+    {
+        block->block_contents_size += sizeof(data->first_frame_with_data) +
+                                      sizeof(data->stride_length);
+    }
+
+    if(data->codec_id != TNG_UNCOMPRESSED)
+    {
+        block->block_contents_size += sizeof(data->compression_multiplier);
+    }
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK && data->n_frames > 0)
+    {
+        dependency = TNG_FRAME_DEPENDENT;
+    }
+    else
+    {
+        dependency = 0;
+    }
+    if(dependency & TNG_FRAME_DEPENDENT)
+    {
+        block->block_contents_size += sizeof(char);
+    }
+
+#ifdef USE_ZLIB
+    data_start_pos = block->block_contents_size;
+#endif
+
+    if(data->datatype == TNG_CHAR_DATA)
+    {
+        for(i = n_frames; i--;)
+        {
+            for(j = data->n_values_per_frame; j--;)
+            {
+                block->block_contents_size += strlen(data->strings[i][j]) + 1;
+            }
+        }
+    }
+    else
+    {
+        block->block_contents_size += size * frame_step *
+        data->n_values_per_frame;
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+
+    memcpy(block->block_contents, &data->datatype, sizeof(char));
+    offset += sizeof(char);
+
+    memcpy(block->block_contents+offset, &dependency, sizeof(char));
+    offset += sizeof(char);
+
+    if(dependency & TNG_FRAME_DEPENDENT)
+    {
+        if(stride_length > 1)
+        {
+            temp = 1;
+        }
+        else
+        {
+            temp = 0;
+        }
+        memcpy(block->block_contents+offset, &temp, sizeof(char));
+        offset += sizeof(char);
+    }
+
+    memcpy(block->block_contents+offset, &data->n_values_per_frame,
+           sizeof(data->n_values_per_frame));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+           (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(data->n_values_per_frame);
+
+    memcpy(block->block_contents+offset, &data->codec_id,
+           sizeof(data->codec_id));
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+           (int64_t *)block->header_contents+offset)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    offset += sizeof(data->codec_id);
+
+    if(data->codec_id != TNG_UNCOMPRESSED)
+    {
+        memcpy(block->block_contents+offset, &data->compression_multiplier,
+               sizeof(data->compression_multiplier));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+            (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(data->compression_multiplier);
+    }
+
+    if(data->n_frames > 0 && stride_length > 1)
+    {
+        /* FIXME: first_frame_with_data is not reliably set */
+        if(data->first_frame_with_data == 0)
+        {
+            data->first_frame_with_data = frame_set->first_frame;
+        }
+        memcpy(block->block_contents+offset, &data->first_frame_with_data,
+               sizeof(data->first_frame_with_data));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+            (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(data->first_frame_with_data);
+
+        memcpy(block->block_contents+offset, &stride_length,
+               sizeof(data->stride_length));
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+            (int64_t *)block->header_contents+offset)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        offset += sizeof(data->stride_length);
+    }
+
+    if(data->datatype == TNG_CHAR_DATA)
+    {
+        if(data->strings)
+        {
+            for(i = 0; i < frame_step; i++)
+            {
+                for(j = 0; j < data->n_values_per_frame; j++)
+                {
+                    len = (unsigned int)strlen(data->strings[i][j]) + 1;
+                    strncpy(block->block_contents+offset, data->strings[i][j],
+                            len);
+                    offset += len;
+                }
+            }
+        }
+    }
+    else if(data->values)
+    {
+        memcpy(block->block_contents + offset, data->values,
+               block->block_contents_size - offset);
+        switch(data->datatype)
+        {
+        case TNG_FLOAT_DATA:
+            if(data->codec_id == TNG_UNCOMPRESSED || data-> codec_id == TNG_GZIP_COMPRESSION ||
+               data->codec_id == TNG_TNG_COMPRESSION)
+            {
+                if(tng_data->input_endianness_swap_func_32)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        if(tng_data->input_endianness_swap_func_32(tng_data,
+                           (int32_t *)(block->block_contents + i))
+                           != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                multiplier = data->compression_multiplier;
+                if(fabs(multiplier - 1.0) > 0.00001 ||
+                   tng_data->input_endianness_swap_func_32)
+                {
+                    for(i = offset; block->block_contents_size; i+=size)
+                    {
+                        *(float *)(block->block_contents + i) *= (float)multiplier;
+                        if(tng_data->input_endianness_swap_func_32 &&
+                        tng_data->input_endianness_swap_func_32(tng_data,
+                        (int32_t *)(block->block_contents + i))
+                        != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            break;
+        case TNG_INT_DATA:
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                for(i = offset; i < block->block_contents_size; i+=size)
+                {
+                    if(tng_data->input_endianness_swap_func_64(tng_data,
+                       (int64_t *)(block->block_contents + i))
+                       != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                __FILE__, __LINE__);
+                    }
+                }
+            }
+            break;
+        case TNG_DOUBLE_DATA:
+            if(data->codec_id == TNG_UNCOMPRESSED || data-> codec_id == TNG_GZIP_COMPRESSION ||
+               data->codec_id == TNG_TNG_COMPRESSION)
+            {
+                if(tng_data->input_endianness_swap_func_64)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        if(tng_data->input_endianness_swap_func_64(tng_data,
+                           (int64_t *)(block->block_contents + i))
+                           != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                multiplier = data->compression_multiplier;
+                if(fabs(multiplier - 1.0) > 0.00001 ||
+                   tng_data->input_endianness_swap_func_64)
+                {
+                    for(i = offset; i < block->block_contents_size; i+=size)
+                    {
+                        *(double *)(block->block_contents + i) *= multiplier;
+                        if(tng_data->input_endianness_swap_func_64 &&
+                        tng_data->input_endianness_swap_func_64(tng_data,
+                        (int64_t *)(block->block_contents + i))
+                        != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+                }
+            }
+            break;
+        case TNG_CHAR_DATA:
+            break;
+        }
+    }
+    else
+    {
+        memset(block->block_contents+offset, 0, block->block_contents_size - offset);
+    }
+
+    frame_set->n_written_frames += frame_set->n_unwritten_frames;
+    frame_set->n_unwritten_frames = 0;
+
+    if(block_type_flag == TNG_NON_TRAJECTORY_BLOCK || frame_set->n_written_frames > 0)
+    {
+        switch(data->codec_id)
+        {
+#ifdef USE_ZLIB
+        case TNG_GZIP_COMPRESSION:
+    /*         fprintf(stderr, "TNG library: Before compression: %"PRId64"\n", block->block_contents_size); */
+            stat = tng_gzip_compress(tng_data, block,
+                                     block->block_contents + data_start_pos,
+                                     block->block_contents_size - data_start_pos);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Could not write gzipped block data. %s: %d\n", __FILE__,
+                    __LINE__);
+                if(stat == TNG_CRITICAL)
+                {
+                    return(TNG_CRITICAL);
+                }
+                data->codec_id = TNG_UNCOMPRESSED;
+            }
+    /*         fprintf(stderr, "TNG library: After compression: %"PRId64"\n", block->block_contents_size); */
+            break;
+#endif
+        }
+    }
+
+    if(tng_block_header_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot write header of file %s. %s: %d\n",
+               tng_data->output_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(fwrite(block->block_contents, block->block_contents_size, 1,
+              tng_data->output_file) != 1)
+    {
+        fprintf(stderr, "TNG library: Could not write all block data. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+/** Read the meta information of a data block (particle or non-particle data).
+ * @param tng_data is a trajectory data container.
+ * @param block is the block to store the data (should already contain
+ * the block headers).
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_data_block_meta_information_read
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 int *offset,
+                 char *datatype,
+                 char *dependency,
+                 char *sparse_data,
+                 int64_t *n_values,
+                 int64_t *codec_id,
+                 int64_t *first_frame_with_data,
+                 int64_t *stride_length,
+                 int64_t *n_frames,
+                 int64_t *num_first_particle,
+                 int64_t *block_n_particles,
+                 double *multiplier)
+{
+    int meta_size;
+    char *contents;
+
+    if(block->block_contents)
+    {
+        contents = block->block_contents;
+    }
+    else
+    {
+        meta_size = 3 * sizeof(char) + sizeof(double) + 6 * sizeof(int64_t);
+        contents = malloc(meta_size);
+
+        if(!contents)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+               meta_size, __FILE__, __LINE__);
+        }
+
+        if(fread(contents, meta_size, 1, tng_data->input_file) == 0)
+        {
+            fprintf(stderr, "TNG library: Cannot read data block meta information. %s: %d\n", __FILE__, __LINE__);
+            free(contents);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    memcpy(datatype, contents+*offset,
+           sizeof(*datatype));
+    *offset += sizeof(*datatype);
+
+    memcpy(dependency, contents+*offset,
+           sizeof(*dependency));
+    *offset += sizeof(*dependency);
+
+    if(*dependency & TNG_FRAME_DEPENDENT)
+    {
+        memcpy(sparse_data, contents+*offset,
+               sizeof(*sparse_data));
+        *offset += sizeof(*sparse_data);
+    }
+
+    memcpy(n_values, contents+*offset,
+        sizeof(*n_values));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   n_values)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(*n_values);
+
+    memcpy(codec_id, contents+*offset,
+        sizeof(*codec_id));
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                   codec_id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+    *offset += sizeof(*codec_id);
+
+    if(*codec_id != TNG_UNCOMPRESSED)
+    {
+        memcpy(multiplier, contents+*offset,
+            sizeof(*multiplier));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       (int64_t *) multiplier)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        *offset += sizeof(*multiplier);
+    }
+    else
+    {
+        *multiplier = 1;
+    }
+
+    if(*dependency & TNG_FRAME_DEPENDENT)
+    {
+        if(*sparse_data)
+        {
+            memcpy(first_frame_with_data, contents+*offset,
+                sizeof(*first_frame_with_data));
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                           first_frame_with_data)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            *offset += sizeof(*first_frame_with_data);
+
+            memcpy(stride_length, contents+*offset,
+                sizeof(*stride_length));
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                           stride_length)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+            *offset += sizeof(*stride_length);
+            *n_frames = tng_data->current_trajectory_frame_set.n_frames -
+                        (*first_frame_with_data -
+                        tng_data->current_trajectory_frame_set.first_frame);
+        }
+        else
+        {
+            *first_frame_with_data = 0;
+            *stride_length = 1;
+            *n_frames = tng_data->current_trajectory_frame_set.n_frames;
+        }
+    }
+    else
+    {
+        *first_frame_with_data = 0;
+        *stride_length = 1;
+        *n_frames = 1;
+    }
+
+    if (*dependency & TNG_PARTICLE_DEPENDENT)
+    {
+        memcpy(num_first_particle, contents+*offset,
+               sizeof(*num_first_particle));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       num_first_particle)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        *offset += sizeof(*num_first_particle);
+
+        memcpy(block_n_particles, contents+*offset,
+            sizeof(*block_n_particles));
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                       block_n_particles)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        *offset += sizeof(*block_n_particles);
+    }
+
+    if(!block->block_contents)
+    {
+        free(contents);
+    }
+    return(TNG_SUCCESS);
+}
+
+/** Read the contents of a data block (particle or non-particle data).
+ * @param tng_data is a trajectory data container.
+ * @param block is the block to store the data (should already contain
+ * the block headers).
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_data_block_contents_read
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block,
+                 const char hash_mode)
+{
+    int64_t n_values, codec_id, n_frames, first_frame_with_data;
+    int64_t stride_length, block_n_particles, num_first_particle;
+    double multiplier;
+    char datatype, dependency, sparse_data;
+    int offset = 0;
+    tng_bool same_hash;
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+
+    block->block_contents = malloc(block->block_contents_size);
+    if(!block->block_contents)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               block->block_contents_size, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* Read the whole block into block_contents to be able to write it to
+     * disk even if it cannot be interpreted. */
+    if(fread(block->block_contents, block->block_contents_size, 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* FIXME: Does not check if the size of the contents matches the expected
+     * size or if the contents can be read. */
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_match_verify(block, &same_hash);
+        if(same_hash != TNG_TRUE)
+        {
+            fprintf(stderr, "TNG library: '%s' data block contents corrupt. Hashes do not match. %s: %d\n",
+                block->name, __FILE__, __LINE__);
+    /*         return(TNG_FAILURE); */
+        }
+    }
+
+    if(tng_data_block_meta_information_read(tng_data, block,
+                                            &offset, &datatype,
+                                            &dependency, &sparse_data,
+                                            &n_values, &codec_id,
+                                            &first_frame_with_data,
+                                            &stride_length, &n_frames,
+                                            &num_first_particle,
+                                            &block_n_particles,
+                                            &multiplier) == TNG_CRITICAL)
+    {
+        fprintf(stderr, "TNG library: Cannot read data block (%s) meta information. %s: %d\n",
+            block->name, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if (dependency & TNG_PARTICLE_DEPENDENT)
+    {
+        return(tng_particle_data_read(tng_data, block,
+                                      &offset, datatype,
+                                      num_first_particle,
+                                      block_n_particles,
+                                      first_frame_with_data,
+                                      stride_length,
+                                      n_frames, n_values,
+                                      codec_id, multiplier));
+    }
+    else
+    {
+        return(tng_data_read(tng_data, block,
+                             &offset, datatype,
+                             first_frame_with_data,
+                             stride_length,
+                             n_frames, n_values,
+                             codec_id, multiplier));
+    }
+}
+
+/** Update the md5 hash of a block already written to the file
+ * @param tng_data is a trajectory data container.
+ * @param block is the block, of which to update the md5 hash.
+ * @param header_start_pos is the file position where the block header starts.
+ * @param contents_start_pos is the file position where the block contents
+ * start.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_md5_hash_update(tng_trajectory_t tng_data,
+                                               tng_gen_block_t block,
+                                               const int64_t header_start_pos,
+                                               const int64_t contents_start_pos)
+{
+    if(block->block_contents)
+    {
+        free(block->block_contents);
+    }
+
+    block->block_contents = malloc(block->block_contents_size);
+    fseek(tng_data->output_file, (long)contents_start_pos, SEEK_SET);
+    if(fread(block->block_contents, block->block_contents_size, 1,
+            tng_data->output_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_block_md5_hash_generate(block);
+
+    fseek(tng_data->output_file, (long)header_start_pos + 3 * sizeof(int64_t),
+          SEEK_SET);
+    fwrite(block->md5_hash, TNG_MD5_HASH_LEN, 1, tng_data->output_file);
+
+    return(TNG_SUCCESS);
+}
+
+/** Update the frame set pointers in the file header (general info block),
+ * already written to disk
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode specifies whether to update the block md5 hash when
+ * updating the pointers.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_header_pointers_update
+                (tng_trajectory_t tng_data, const char hash_mode)
+{
+    tng_gen_block_t block;
+    FILE *temp = tng_data->input_file;
+    int64_t output_file_pos, pos, contents_start_pos;
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_data->input_file = tng_data->output_file;
+
+    tng_block_init(&block);
+
+    output_file_pos = ftell(tng_data->output_file);
+    fseek(tng_data->output_file, 0, SEEK_SET);
+
+    if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot read general info header. %s: %d\n",
+               __FILE__, __LINE__);
+        tng_data->input_file = temp;
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    contents_start_pos = ftell(tng_data->output_file);
+
+    fseek(tng_data->output_file, (long)block->block_contents_size - 5 *
+          sizeof(int64_t), SEEK_CUR);
+
+    tng_data->input_file = temp;
+
+    pos = tng_data->first_trajectory_frame_set_output_file_pos;
+
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                    &pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(fwrite(&pos, sizeof(int64_t), 1, tng_data->output_file) != 1)
+    {
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    pos = tng_data->last_trajectory_frame_set_output_file_pos;
+
+    if(tng_data->input_endianness_swap_func_64)
+    {
+        if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                    &pos)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(fwrite(&pos,
+        sizeof(int64_t), 1, tng_data->output_file) != 1)
+    {
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_update(tng_data, block, 0, contents_start_pos);
+    }
+
+    tng_block_destroy(&block);
+
+    fseek(tng_data->output_file, (long)output_file_pos, SEEK_SET);
+
+    return(TNG_SUCCESS);
+}
+
+/** Update the frame set pointers in the current frame set block, already
+ * written to disk. It also updates the pointers of the blocks pointing to
+ * the current frame set block.
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode specifies whether to update the block md5 hash when
+ * updating the pointers.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_frame_set_pointers_update
+                (tng_trajectory_t tng_data, const char hash_mode)
+{
+    tng_gen_block_t block;
+    tng_trajectory_frame_set_t frame_set;
+    FILE *temp = tng_data->input_file;
+    int64_t pos, output_file_pos, header_start_pos, contents_start_pos;
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_block_init(&block);
+    output_file_pos = ftell(tng_data->output_file);
+
+    tng_data->input_file = tng_data->output_file;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    /* Update previous frame set */
+    if(frame_set->prev_frame_set_file_pos != -1 &&
+       frame_set->prev_frame_set_file_pos != 0)
+    {
+        fseek(tng_data->output_file, (long)frame_set->prev_frame_set_file_pos,
+              SEEK_SET);
+
+        header_start_pos = frame_set->prev_frame_set_file_pos;
+
+        if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read frame header. %s: %d\n",
+                __FILE__, __LINE__);
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        contents_start_pos = ftell(tng_data->output_file);
+
+        fseek(tng_data->output_file, (long)block->block_contents_size - (6 *
+            sizeof(int64_t) + 2 * sizeof(double)), SEEK_CUR);
+
+        pos = tng_data->current_trajectory_frame_set_output_file_pos;
+
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                        &pos)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+
+        if(fwrite(&pos, sizeof(int64_t), 1, tng_data->output_file) != 1)
+        {
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        if(hash_mode == TNG_USE_HASH)
+        {
+            tng_md5_hash_update(tng_data, block, header_start_pos,
+                                contents_start_pos);
+        }
+        fseek(tng_data->output_file, (long)output_file_pos, SEEK_SET);
+    }
+
+    /* Update the frame set one medium stride step before */
+    if(frame_set->medium_stride_prev_frame_set_file_pos != -1 &&
+       frame_set->medium_stride_prev_frame_set_file_pos != 0)
+    {
+        fseek(tng_data->output_file,
+              (long)frame_set->medium_stride_prev_frame_set_file_pos,
+              SEEK_SET);
+
+        if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read frame set header. %s: %d\n",
+                __FILE__, __LINE__);
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        contents_start_pos = ftell(tng_data->output_file);
+
+        fseek(tng_data->output_file, (long)block->block_contents_size - (4 *
+            sizeof(int64_t) + 2 * sizeof(double)), SEEK_CUR);
+
+        pos = tng_data->current_trajectory_frame_set_output_file_pos;
+
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                        &pos)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+
+        if(fwrite(&pos, sizeof(int64_t), 1, tng_data->output_file) != 1)
+        {
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        if(hash_mode == TNG_USE_HASH)
+        {
+            tng_md5_hash_update(tng_data, block,
+                                frame_set->medium_stride_prev_frame_set_file_pos,
+                                contents_start_pos);
+        }
+    }
+
+    /* Update the frame set one long stride step before */
+    if(frame_set->long_stride_prev_frame_set_file_pos != -1 &&
+       frame_set->long_stride_prev_frame_set_file_pos != 0)
+    {
+        fseek(tng_data->output_file,
+              (long)frame_set->long_stride_prev_frame_set_file_pos,
+              SEEK_SET);
+
+        if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read frame set header. %s: %d\n",
+                __FILE__, __LINE__);
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        contents_start_pos = ftell(tng_data->output_file);
+
+        fseek(tng_data->output_file, (long)block->block_contents_size - (2 *
+            sizeof(int64_t) + 2 * sizeof(double)), SEEK_CUR);
+
+        pos = tng_data->current_trajectory_frame_set_output_file_pos;
+
+        if(tng_data->input_endianness_swap_func_64)
+        {
+            if(tng_data->input_endianness_swap_func_64(tng_data,
+                                                        &pos)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+
+        if(fwrite(&pos, sizeof(int64_t), 1, tng_data->output_file) != 1)
+        {
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        if(hash_mode == TNG_USE_HASH)
+        {
+            tng_md5_hash_update(tng_data, block,
+                                frame_set->long_stride_prev_frame_set_file_pos,
+                                contents_start_pos);
+        }
+    }
+
+    fseek(tng_data->output_file, (long)output_file_pos, SEEK_SET);
+
+    tng_data->input_file = temp;
+
+    tng_block_destroy(&block);
+
+    return(TNG_SUCCESS);
+}
+/*
+// ** Move the blocks in a frame set so that there is no unused space between
+//  * them. This can only be done on the last frame set in the file and should
+//  * be done e.g. if the last frame set in the file has fewer frames than
+//  * default or after compressing data blocks in a frame set.
+//  * @param tng_data is a trajectory data container.
+//  * @details the current_trajectory_frame_set is the one that will be modified.
+//  * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the frame set
+//  * cannot be aligned or TNG_CRITICAL (2) if a major error has occured.
+//  * FIXME: This function is not finished!!!
+//  *
+// static tng_function_status tng_frame_set_align(tng_trajectory_t tng_data)
+// {
+//     tng_gen_block_t block;
+//     tng_trajectory_frame_set_t frame_set;
+//     FILE *temp = tng_data->input_file;
+//     int64_t pos, contents_start_pos, output_file_len;
+//
+//     frame_set = &tng_data->current_trajectory_frame_set;
+//
+//     if(frame_set->n_written_frames == frame_set->n_frames)
+//     {
+//         return(TNG_SUCCESS);
+//     }
+//
+//     if(tng_data->current_trajectory_frame_set_output_file_pos !=
+//        tng_data->last_trajectory_frame_set_output_file_pos)
+//     {
+//     }
+//
+//     if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+//     {
+//         fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+//                __FILE__, __LINE__);
+//         return(TNG_CRITICAL);
+//     }
+//
+//     tng_block_init(&block);
+// //     output_file_pos = ftell(tng_data->output_file);
+//
+//     tng_data->input_file = tng_data->output_file;
+//
+//     pos = tng_data->current_trajectory_frame_set_output_file_pos;
+//
+//     fseek(tng_data->output_file, pos, SEEK_SET);
+//     if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+//     {
+//         fprintf(stderr, "TNG library: Cannot read frame set header. %s: %d\n",
+//             __FILE__, __LINE__);
+//         tng_data->input_file = temp;
+//         tng_block_destroy(&block);
+//         return(TNG_CRITICAL);
+//     }
+//
+//     contents_start_pos = ftell(tng_data->output_file);
+//
+//     fseek(tng_data->output_file, 0, SEEK_END);
+//     output_file_len = ftell(tng_data->output_file);
+//     pos = contents_start_pos + block->block_contents_size;
+//     fseek(tng_data->output_file, pos,
+//           SEEK_SET);
+//
+//     while(pos < output_file_len)
+//     {
+//         if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+//         {
+//             fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n", pos,
+//                    __FILE__, __LINE__);
+//             tng_data->input_file = temp;
+//             tng_block_destroy(&block);
+//             return(TNG_CRITICAL);
+//         }
+//         pos += block->header_contents_size + block->block_contents_size;
+//         fseek(tng_data->output_file, pos, SEEK_SET);
+//     }
+//
+//     return(TNG_SUCCESS);
+// }
+*/
+/** Finish writing the current frame set. Update the number of frames
+ * and the hashes of the frame set and all its data blocks (if hash_mode
+ * == TNG_USE_HASH).
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode specifies whether to update the block md5 hash when
+ * updating the pointers.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+static tng_function_status tng_frame_set_finalize
+                (tng_trajectory_t tng_data, const char hash_mode)
+{
+    tng_gen_block_t block;
+    tng_trajectory_frame_set_t frame_set;
+    FILE *temp = tng_data->input_file;
+    int64_t pos, contents_start_pos, output_file_len;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(frame_set->n_written_frames == frame_set->n_frames)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_block_init(&block);
+/*     output_file_pos = ftell(tng_data->output_file); */
+
+    tng_data->input_file = tng_data->output_file;
+
+    pos = tng_data->current_trajectory_frame_set_output_file_pos;
+
+    fseek(tng_data->output_file, (long)pos, SEEK_SET);
+
+    if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot read frame set header. %s: %d\n",
+            __FILE__, __LINE__);
+        tng_data->input_file = temp;
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    contents_start_pos = ftell(tng_data->output_file);
+
+    fseek(tng_data->output_file, sizeof(frame_set->first_frame), SEEK_CUR);
+    if(fwrite(&frame_set->n_written_frames, sizeof(frame_set->n_frames),
+              1, tng_data->output_file) != 1)
+    {
+        tng_data->input_file = temp;
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+
+    if(hash_mode == TNG_USE_HASH)
+    {
+        tng_md5_hash_update(tng_data, block, pos,
+                            pos + block->header_contents_size);
+    }
+
+    fseek(tng_data->output_file, 0, SEEK_END);
+    output_file_len = ftell(tng_data->output_file);
+    pos = contents_start_pos + block->block_contents_size;
+    fseek(tng_data->output_file, (long)pos, SEEK_SET);
+
+    while(pos < output_file_len)
+    {
+        if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n", pos,
+                   __FILE__, __LINE__);
+            tng_data->input_file = temp;
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        if(hash_mode == TNG_USE_HASH)
+        {
+            tng_md5_hash_update(tng_data, block, pos,
+                                pos + block->header_contents_size);
+        }
+        pos += block->header_contents_size + block->block_contents_size;
+        fseek(tng_data->output_file, (long)pos, SEEK_SET);
+    }
+
+    tng_data->input_file = temp;
+    tng_block_destroy(&block);
+    return(TNG_SUCCESS);
+}
+
+/*
+// ** Sets the name of a file contents block
+//  * @param tng_data is a trajectory data container.
+//  * @param block is the block, of which to change names.
+//  * @param new_name is the new name of the block.
+//  * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+//  * error has occured.
+//
+// static tng_function_status tng_block_name_set(tng_trajectory_t tng_data,
+//                                               tng_gen_block_t block,
+//                                               const char *new_name)
+// {
+//     int len;
+//
+//     len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+//
+//      * If the currently stored string length is not enough to store the new
+//      * string it is freed and reallocated. *
+//     if(block->name && strlen(block->name) < len)
+//     {
+//         free(block->name);
+//         block->name = 0;
+//     }
+//     if(!block->name)
+//     {
+//         block->name = malloc(len);
+//         if(!block->name)
+//         {
+//             fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+//                    __FILE__, __LINE__);
+//             return(TNG_CRITICAL);
+//         }
+//     }
+//
+//     strncpy(block->name, new_name, len);
+//
+//     return(TNG_SUCCESS);
+// }
+*/
+
+tng_function_status tng_atom_residue_get(tng_trajectory_t tng_data,
+                                         const tng_atom_t atom,
+                                         tng_residue_t *residue)
+{
+    (void) tng_data;
+
+    TNG_ASSERT(atom, "TNG library: atom must not be NULL");
+
+    *residue = atom->residue;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_atom_name_get(tng_trajectory_t tng_data,
+                                      const tng_atom_t atom,
+                                      char *name,
+                                      const int max_len)
+{
+    (void) tng_data;
+    TNG_ASSERT(atom, "TNG library: atom must not be NULL");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, atom->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(atom->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_atom_name_set(tng_trajectory_t tng_data,
+                                      tng_atom_t atom,
+                                      const char *new_name)
+{
+    unsigned int len;
+    (void)tng_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer.");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(atom->name && strlen(atom->name) < len)
+    {
+        free(atom->name);
+        atom->name = 0;
+    }
+    if(!atom->name)
+    {
+        atom->name = malloc(len);
+        if(!atom->name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(atom->name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_atom_type_get(tng_trajectory_t tng_data,
+                                      const tng_atom_t atom,
+                                      char *type,
+                                      const int max_len)
+{
+    (void) tng_data;
+    TNG_ASSERT(atom, "TNG library: atom must not be NULL");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer");
+
+    strncpy(type, atom->atom_type, max_len - 1);
+    type[max_len - 1] = 0;
+
+    if(strlen(atom->atom_type) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_atom_type_set(tng_trajectory_t tng_data,
+                                      tng_atom_t atom,
+                                      const char *new_type)
+{
+    unsigned int len;
+    (void)tng_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_type, "TNG library: new_type must not be a NULL pointer.");
+
+    len = tng_min_i((int)strlen(new_type) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(atom->atom_type && strlen(atom->atom_type) < len)
+    {
+        free(atom->atom_type);
+        atom->atom_type = 0;
+    }
+    if(!atom->atom_type)
+    {
+        atom->atom_type = malloc(len);
+        if(!atom->atom_type)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(atom->atom_type, new_type, len);
+
+    return(TNG_SUCCESS);
+}
+
+/** Initialise an atom struct
+ * @param atom is the atom to initialise.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+static tng_function_status tng_atom_init(tng_atom_t atom)
+{
+    atom->name = 0;
+    atom->atom_type = 0;
+
+    return(TNG_SUCCESS);
+}
+
+/** Free the memory in an atom struct
+ * @param atom is the atom to destroy.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+static tng_function_status tng_atom_destroy(tng_atom_t atom)
+{
+    if(atom->name)
+    {
+        free(atom->name);
+        atom->name = 0;
+    }
+    if(atom->atom_type)
+    {
+        free(atom->atom_type);
+        atom->atom_type = 0;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_add
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 tng_molecule_t *molecule)
+{
+    int64_t id, i;
+    tng_bool found_id = TNG_TRUE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    /* Find an unused ID */
+    id = 0;
+    while(found_id)
+    {
+        found_id = TNG_FALSE;
+        for(i = tng_data->n_molecules; i--;)
+        {
+            if(tng_data->molecules[i].id == id)
+            {
+                found_id = TNG_TRUE;
+                i = 0;
+            }
+        }
+        if(found_id)
+        {
+            id++;
+        }
+    }
+
+    return(tng_molecule_w_id_add(tng_data, name, id, molecule));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_w_id_add
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t id,
+                 tng_molecule_t *molecule)
+{
+    tng_molecule_t new_molecules;
+    int64_t *new_molecule_cnt_list, i;
+    tng_function_status stat = TNG_SUCCESS;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    new_molecules = realloc(tng_data->molecules,
+                            sizeof(struct tng_molecule) *
+                            (tng_data->n_molecules + 1));
+
+    if(!new_molecules)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_molecule) * (tng_data->n_molecules + 1),
+               __FILE__, __LINE__);
+        free(tng_data->molecules);
+        tng_data->molecules = 0;
+        return(TNG_CRITICAL);
+    }
+
+    new_molecule_cnt_list = realloc(tng_data->molecule_cnt_list,
+                                    sizeof(int64_t) *
+                                    (tng_data->n_molecules + 1));
+
+    if(!new_molecule_cnt_list)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(int64_t) * (tng_data->n_molecules + 1),
+               __FILE__, __LINE__);
+        free(tng_data->molecule_cnt_list);
+        tng_data->molecule_cnt_list = 0;
+        free(new_molecules);
+        return(TNG_CRITICAL);
+    }
+
+    tng_data->molecules = new_molecules;
+    tng_data->molecule_cnt_list = new_molecule_cnt_list;
+
+    *molecule = &new_molecules[tng_data->n_molecules];
+
+    tng_molecule_init(tng_data, *molecule);
+    tng_molecule_name_set(tng_data, *molecule, name);
+
+    /* FIXME: Should this be a function argument instead? */
+    tng_data->molecule_cnt_list[tng_data->n_molecules] = 0;
+
+    for(i = tng_data->n_molecules; i--;)
+    {
+        if(tng_data->molecules[i].id == id)
+        {
+            stat = TNG_FAILURE;
+            fprintf(stderr, "TNG library: Molecule ID %"PRId64" already in use. %s: %d\n", id,
+                   __FILE__, __LINE__);
+            break;
+        }
+    }
+
+    (*molecule)->id = id;
+
+    tng_data->n_molecules++;
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_existing_add
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t *molecule_p)
+{
+    tng_bool found_id = TNG_TRUE;
+    tng_molecule_t new_molecules, molecule;
+    int64_t *new_molecule_cnt_list, i, id;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    /* Find an unused ID */
+    id = 0;
+    while(found_id)
+    {
+        found_id = TNG_FALSE;
+        for(i = tng_data->n_molecules; i--;)
+        {
+            if(tng_data->molecules[i].id == id)
+            {
+                found_id = TNG_TRUE;
+                i = 0;
+            }
+        }
+        if(found_id)
+        {
+            id++;
+        }
+    }
+
+    new_molecules = realloc(tng_data->molecules,
+                            sizeof(struct tng_molecule) *
+                            (tng_data->n_molecules + 1));
+
+    if(!new_molecules)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_molecule) * (tng_data->n_molecules + 1),
+               __FILE__, __LINE__);
+        free(tng_data->molecules);
+        tng_data->molecules = 0;
+        return(TNG_CRITICAL);
+    }
+
+    new_molecule_cnt_list = realloc(tng_data->molecule_cnt_list,
+                                    sizeof(int64_t) *
+                                    (tng_data->n_molecules + 1));
+
+    if(!new_molecule_cnt_list)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(int64_t) * (tng_data->n_molecules + 1),
+               __FILE__, __LINE__);
+        free(tng_data->molecule_cnt_list);
+        tng_data->molecule_cnt_list = 0;
+        free(new_molecules);
+        return(TNG_CRITICAL);
+    }
+
+    molecule = *molecule_p;
+
+    tng_data->molecules = new_molecules;
+    tng_data->molecule_cnt_list = new_molecule_cnt_list;
+
+    new_molecules[tng_data->n_molecules] = *molecule;
+
+    tng_data->molecule_cnt_list[tng_data->n_molecules] = 0;
+
+    free(*molecule_p);
+
+    molecule = &new_molecules[tng_data->n_molecules];
+
+    *molecule_p = molecule;
+
+    molecule->id = id;
+
+    tng_data->n_molecules++;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_molecule_name_get(tng_trajectory_t tng_data,
+                                          const tng_molecule_t molecule,
+                                          char *name,
+                                          const int max_len)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be NULL");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, molecule->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(molecule->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_set
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *new_name)
+{
+    unsigned int len;
+    (void)tng_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer.");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(molecule->name && strlen(molecule->name) < len)
+    {
+        free(molecule->name);
+        molecule->name = 0;
+    }
+    if(!molecule->name)
+    {
+        molecule->name = malloc(len);
+        if(!molecule->name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(molecule->name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t *cnt)
+{
+    int64_t i, index = -1;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(cnt, "TNG library: cnt must not be a NULL pointer.");
+
+    for(i = tng_data->n_molecules; i--;)
+    {
+        if(&tng_data->molecules[i] == molecule)
+        {
+            index = i;
+            i = 0;
+        }
+    }
+    if(index == -1)
+    {
+        return(TNG_FAILURE);
+    }
+    *cnt = tng_data->molecule_cnt_list[index];
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_set
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const int64_t cnt)
+{
+    int64_t i, old_cnt, index = -1;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    for(i = tng_data->n_molecules; i--;)
+    {
+        if(&tng_data->molecules[i] == molecule)
+        {
+            index = i;
+            i = 0;
+        }
+    }
+    if(index == -1)
+    {
+        fprintf(stderr, "TNG library: Could not find molecule in TNG trajectory. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+    if(tng_data->var_num_atoms_flag == TNG_CONSTANT_N_ATOMS)
+    {
+        old_cnt = tng_data->molecule_cnt_list[index];
+        tng_data->molecule_cnt_list[index] = cnt;
+
+        tng_data->n_particles += (cnt-old_cnt) *
+                                 tng_data->molecules[index].n_atoms;
+    }
+    else
+    {
+        old_cnt = tng_data->current_trajectory_frame_set.molecule_cnt_list[index];
+        tng_data->current_trajectory_frame_set.molecule_cnt_list[index] = cnt;
+
+        tng_data->current_trajectory_frame_set.n_particles += (cnt-old_cnt) *
+                tng_data->molecules[index].n_atoms;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_find
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 int64_t nr,
+                 tng_molecule_t *molecule)
+{
+    int64_t i, n_molecules;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+    TNG_ASSERT(molecule, "TNG library: molecule must not be a NULL pointer.");
+
+    n_molecules = tng_data->n_molecules;
+
+    for(i = 0; i < n_molecules; i++)
+    {
+        *molecule = &tng_data->molecules[i];
+        if(name[0] == 0 || strcmp(name, (*molecule)->name) == 0)
+        {
+            if(nr == -1 || nr == (*molecule)->id)
+            {
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    *molecule = 0;
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_of_index_get
+                (tng_trajectory_t tng_data,
+                 int64_t index,
+                 tng_molecule_t *molecule)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(molecule, "TNG library: molecule must not be a NULL pointer.");
+
+    if(index >= tng_data->n_molecules)
+    {
+        *molecule = 0;
+        return(TNG_FAILURE);
+    }
+    *molecule = &tng_data->molecules[index];
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_system_copy(tng_trajectory_t tng_data_src,
+                                                               tng_trajectory_t tng_data_dest)
+{
+    tng_molecule_t molecule, molecule_temp;
+    tng_chain_t chain, chain_temp;
+    tng_residue_t residue, residue_temp;
+    tng_atom_t atom, atom_temp;
+    tng_bond_t bond_temp;
+    tng_function_status stat;
+    int64_t i, j, k, l, *list_temp;
+
+    TNG_ASSERT(tng_data_src, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(tng_data_dest, "TNG library: Trajectory container not properly setup.");
+
+    for(i = 0; i < tng_data_dest->n_molecules; i++)
+    {
+        molecule = &tng_data_dest->molecules[i];
+        tng_molecule_destroy(tng_data_dest, molecule);
+    }
+
+    tng_data_dest->n_molecules = 0;
+    tng_data_dest->n_particles = 0;
+
+    molecule_temp = realloc(tng_data_dest->molecules,
+                    sizeof(struct tng_molecule) * tng_data_src->n_molecules);
+    if(!molecule_temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_molecule) * tng_data_src->n_molecules,
+               __FILE__, __LINE__);
+        free(tng_data_dest->molecules);
+        tng_data_dest->molecules = 0;
+        return(TNG_CRITICAL);
+    }
+    list_temp = realloc(tng_data_dest->molecule_cnt_list,
+                                     sizeof(int64_t) * tng_data_src->n_molecules);
+    if(!list_temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(int64_t) * tng_data_src->n_molecules,
+               __FILE__, __LINE__);
+        free(tng_data_dest->molecule_cnt_list);
+        tng_data_dest->molecule_cnt_list = 0;
+        free(molecule_temp);
+        return(TNG_CRITICAL);
+    }
+
+    tng_data_dest->molecules = molecule_temp;
+    tng_data_dest->molecule_cnt_list = list_temp;
+
+    for(i = 0; i < tng_data_src->n_molecules; i++)
+    {
+        molecule = &tng_data_src->molecules[i];
+        stat = tng_molecule_w_id_add(tng_data_dest, molecule->name, molecule->id,
+                                     &molecule_temp);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create new molecule to make a copy. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(stat);
+        }
+        molecule_temp->quaternary_str = molecule->quaternary_str;
+        for(j = 0; j < molecule->n_chains; j++)
+        {
+            chain = &molecule->chains[j];
+            stat = tng_molecule_chain_w_id_add(tng_data_dest, molecule_temp,
+                                               chain->name, chain->id,
+                                               &chain_temp);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot create new chain to make a copy. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            for(k = 0; k < chain->n_residues; k++)
+            {
+                residue = &chain->residues[k];
+                stat = tng_chain_residue_w_id_add(tng_data_dest, chain_temp,
+                                                  residue->name, residue->id,
+                                                  &residue_temp);
+                if(stat != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot create new residue to make a copy. %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(stat);
+                }
+                for(l = 0; l < residue->n_atoms; l++)
+                {
+                    atom = &molecule->atoms[residue->atoms_offset + l];
+                    stat = tng_residue_atom_w_id_add(tng_data_dest, residue_temp,
+                                                     atom->name, atom->atom_type,
+                                                     atom->id, &atom_temp);
+                    if(stat != TNG_SUCCESS)
+                    {
+                    fprintf(stderr, "TNG library: Cannot create new atom to make a copy. %s: %d\n",
+                           __FILE__, __LINE__);
+                        return(stat);
+                    }
+                }
+            }
+        }
+        molecule_temp->n_bonds = molecule->n_bonds;
+        bond_temp = realloc(molecule_temp->bonds, sizeof(struct tng_bond) *
+                            molecule->n_bonds);
+        if(!bond_temp)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   sizeof(struct tng_bond) * molecule->n_bonds,
+                   __FILE__, __LINE__);
+            free(molecule_temp->bonds);
+            molecule_temp->n_bonds = 0;
+            return(TNG_CRITICAL);
+        }
+        molecule_temp->bonds = bond_temp;
+        for(j = 0; j < molecule->n_bonds; j++)
+        {
+            molecule_temp->bonds[j] = molecule->bonds[j];
+        }
+        stat = tng_molecule_cnt_set(tng_data_dest, molecule_temp,
+                                    tng_data_src->molecule_cnt_list[i]);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot set molecule count. %s: %d.\n",
+                   __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_chains_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *n)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be NULL");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = molecule->n_chains;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t index,
+                 tng_chain_t *chain)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be a NULL pointer.");
+    TNG_ASSERT(chain, "TNG library: chain must not be a NULL pointer.");
+
+    if(index >= molecule->n_chains)
+    {
+        *chain = 0;
+        return(TNG_FAILURE);
+    }
+    *chain = &molecule->chains[index];
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_residues_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *n)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be NULL");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = molecule->n_residues;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_residue_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t index,
+                 tng_residue_t *residue)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be a NULL pointer.");
+    TNG_ASSERT(residue, "TNG library: residue must not be a NULL pointer.");
+
+    if(index >= molecule->n_residues)
+    {
+        *residue = 0;
+        return(TNG_FAILURE);
+    }
+    *residue = &molecule->residues[index];
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_atoms_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *n)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be NULL");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = molecule->n_atoms;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_atom_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 int64_t index,
+                 tng_atom_t *atom)
+{
+    (void) tng_data;
+    TNG_ASSERT(molecule, "TNG library: molecule must not be a NULL pointer.");
+    TNG_ASSERT(atom, "TNG library: atom must not be a NULL pointer.");
+
+    if(index >= molecule->n_atoms)
+    {
+        *atom = 0;
+        return(TNG_FAILURE);
+    }
+    *atom = &molecule->atoms[index];
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_find
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 int64_t nr,
+                 tng_chain_t *chain)
+{
+    int64_t i, n_chains;
+    (void)tng_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    n_chains = molecule->n_chains;
+
+    for(i = 0; i < n_chains; i++)
+    {
+        *chain = &molecule->chains[i];
+        if(name[0] == 0 || strcmp(name, (*chain)->name) == 0)
+        {
+            if(nr == -1 || nr == (*chain)->id)
+            {
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    *chain = 0;
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_add
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 tng_chain_t *chain)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    return(tng_molecule_chain_w_id_add(tng_data, molecule, name,
+                                       molecule->n_chains + 1, chain));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_w_id_add
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 const int64_t id,
+                 tng_chain_t *chain)
+{
+    int64_t i;
+    tng_chain_t new_chains;
+    tng_function_status stat = TNG_SUCCESS;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    new_chains = realloc(molecule->chains,
+                         sizeof(struct tng_chain) *
+                         (molecule->n_chains + 1));
+
+    if(!new_chains)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_chain) * (molecule->n_chains + 1),
+               __FILE__, __LINE__);
+        free(molecule->chains);
+        molecule->chains = 0;
+        return(TNG_CRITICAL);
+    }
+
+    molecule->chains = new_chains;
+
+    *chain = &new_chains[molecule->n_chains];
+    (*chain)->name = 0;
+
+    tng_chain_name_set(tng_data, *chain, name);
+
+    (*chain)->molecule = molecule;
+    (*chain)->n_residues = 0;
+
+    for(i = molecule->n_chains; i--;)
+    {
+        if(molecule->chains[i].id == id)
+        {
+            stat = TNG_FAILURE;
+            fprintf(stderr, "TNG library: Chain ID already in use. %s: %d\n", __FILE__, __LINE__);
+            break;
+        }
+    }
+
+    molecule->n_chains++;
+
+    (*chain)->id = id;
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_bond_add
+                (const tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const int64_t from_atom_id,
+                 const int64_t to_atom_id,
+                 tng_bond_t *bond)
+{
+    int64_t i;
+    tng_bond_t new_bonds;
+    (void)tng_data;
+
+    for(i = 0; i < molecule->n_bonds; i++)
+    {
+        *bond = &molecule->bonds[i];
+        /* Check if the bond already exists */
+        if(((*bond)->from_atom_id == from_atom_id && (*bond)->to_atom_id == to_atom_id) ||
+           ((*bond)->to_atom_id == from_atom_id && (*bond)->from_atom_id == to_atom_id))
+        {
+            return(TNG_SUCCESS);
+        }
+    }
+
+    new_bonds = realloc(molecule->bonds,
+                        sizeof(struct tng_bond) *
+                        (molecule->n_bonds + 1));
+
+    if(!new_bonds)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_bond) * (molecule->n_bonds + 1),
+               __FILE__, __LINE__);
+        *bond = 0;
+        free(molecule->bonds);
+        molecule->bonds = 0;
+        return(TNG_CRITICAL);
+    }
+
+    molecule->bonds = new_bonds;
+
+    *bond = &new_bonds[molecule->n_bonds];
+
+    (*bond)->from_atom_id = from_atom_id;
+    (*bond)->to_atom_id = to_atom_id;
+
+    molecule->n_bonds++;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_atom_find
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t molecule,
+                 const char *name,
+                 int64_t id,
+                 tng_atom_t *atom)
+{
+    int64_t i, n_atoms;
+    (void)tng_data;
+
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    n_atoms = molecule->n_atoms;
+
+    for(i = 0; i < n_atoms; i++)
+    {
+        *atom = &molecule->atoms[i];
+        if(name[0] == 0 || strcmp(name, (*atom)->name) == 0)
+        {
+            if(id == -1 || id == (*atom)->id)
+            {
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    *atom = 0;
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status tng_chain_name_get(const tng_trajectory_t tng_data,
+                                       const tng_chain_t chain,
+                                       char *name,
+                                       const int max_len)
+{
+    (void) tng_data;
+    TNG_ASSERT(chain, "TNG library: chain must not be NULL");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, chain->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(chain->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_set
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *new_name)
+{
+    unsigned int len;
+    (void)tng_data;
+
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer.");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(chain->name && strlen(chain->name) < len)
+    {
+        free(chain->name);
+        chain->name = 0;
+    }
+    if(!chain->name)
+    {
+        chain->name = malloc(len);
+        if(!chain->name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(chain->name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_num_residues_get
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 int64_t *n)
+{
+    (void) tng_data;
+    TNG_ASSERT(chain, "TNG library: chain must not be NULL");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = chain->n_residues;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 int64_t index,
+                 tng_residue_t *residue)
+{
+    (void) tng_data;
+    TNG_ASSERT(chain, "TNG library: chain must not be a NULL pointer.");
+    TNG_ASSERT(residue, "TNG library: residue must not be a NULL pointer.");
+
+    if(index >= chain->n_residues)
+    {
+        *residue = 0;
+        return(TNG_FAILURE);
+    }
+    *residue = &chain->residues[index];
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_find
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *name,
+                 int64_t id,
+                 tng_residue_t *residue)
+{
+    int64_t i, n_residues;
+    (void)tng_data;
+
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    n_residues = chain->n_residues;
+
+    for(i = 0; i < n_residues; i++)
+    {
+        *residue = &chain->residues[i];
+        if(name[0] == 0 || strcmp(name, (*residue)->name) == 0)
+        {
+            if(id == -1 || id == (*residue)->id)
+            {
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    *residue = 0;
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_add
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *name,
+                 tng_residue_t *residue)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    return(tng_chain_residue_w_id_add(tng_data, chain, name,
+                                      chain->n_residues + 1, residue));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_w_id_add
+                (tng_trajectory_t tng_data,
+                 tng_chain_t chain,
+                 const char *name,
+                 const int64_t id,
+                 tng_residue_t *residue)
+{
+    int64_t i, curr_index;
+    tng_residue_t new_residues, temp_residue, last_residue;
+    tng_molecule_t molecule = chain->molecule;
+    tng_function_status stat = TNG_SUCCESS;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    if(chain->n_residues)
+    {
+        curr_index = chain->residues - molecule->residues;
+    }
+    else
+    {
+        curr_index = -1;
+    }
+
+    new_residues = realloc(molecule->residues,
+                           sizeof(struct tng_residue) *
+                           (molecule->n_residues + 1));
+
+    if(!new_residues)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_residue) * (molecule->n_residues + 1),
+               __FILE__, __LINE__);
+        free(molecule->residues);
+        molecule->residues = 0;
+        return(TNG_CRITICAL);
+    }
+
+    molecule->residues = new_residues;
+
+    if(curr_index != -1)
+    {
+        chain->residues = new_residues + curr_index;
+        if(molecule->n_residues)
+        {
+            last_residue = &new_residues[molecule->n_residues - 1];
+
+            temp_residue = chain->residues + (chain->n_residues - 1);
+            /* Make space in list of residues to add the new residues together with the other
+            * residues of this chain */
+            if(temp_residue != last_residue)
+            {
+                ++temp_residue;
+                memmove(temp_residue + 1, temp_residue,
+                        last_residue - temp_residue);
+            }
+        }
+    }
+    else
+    {
+        curr_index = molecule->n_residues;
+    }
+
+    *residue = &molecule->residues[curr_index + chain->n_residues];
+
+    if(!chain->n_residues)
+    {
+        chain->residues = *residue;
+    }
+
+    (*residue)->name = 0;
+    tng_residue_name_set(tng_data, *residue, name);
+
+    (*residue)->chain = chain;
+    (*residue)->n_atoms = 0;
+    (*residue)->atoms_offset = 0;
+
+    for(i = chain->n_residues; i--;)
+    {
+        if(chain->residues[i].id == id)
+        {
+            stat = TNG_FAILURE;
+            fprintf(stderr, "TNG library: Residue ID already in use. %s: %d\n", __FILE__, __LINE__);
+            break;
+        }
+    }
+
+    chain->n_residues++;
+    molecule->n_residues++;
+
+    (*residue)->id = id;
+
+    return(stat);
+}
+
+tng_function_status tng_residue_name_get(tng_trajectory_t tng_data,
+                                         const tng_residue_t residue,
+                                         char *name,
+                                         const int max_len)
+{
+    (void) tng_data;
+    TNG_ASSERT(residue, "TNG library: residue must not be NULL");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, residue->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(residue->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_set(tng_trajectory_t tng_data,
+                                                           tng_residue_t residue,
+                                                           const char *new_name)
+{
+    unsigned int len;
+    (void)tng_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(residue->name && strlen(residue->name) < len)
+    {
+        free(residue->name);
+        residue->name = 0;
+    }
+    if(!residue->name)
+    {
+        residue->name = malloc(len);
+        if(!residue->name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(residue->name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_num_atoms_get
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 int64_t *n)
+{
+    (void) tng_data;
+    TNG_ASSERT(residue, "TNG library: residue must not be NULL");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = residue->n_atoms;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_of_index_get
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 int64_t index,
+                 tng_atom_t *atom)
+{
+    tng_chain_t chain;
+    tng_molecule_t molecule;
+
+    (void) tng_data;
+    TNG_ASSERT(residue, "TNG library: residue must not be a NULL pointer.");
+    TNG_ASSERT(atom, "TNG library: atom must not be a NULL pointer.");
+
+    if(index >= residue->n_atoms)
+    {
+        *atom = 0;
+        return(TNG_FAILURE);
+    }
+    chain = residue->chain;
+    molecule = chain->molecule;
+
+    if(index + residue->atoms_offset >= molecule->n_atoms)
+    {
+        *atom = 0;
+        return(TNG_FAILURE);
+    }
+
+    *atom = &molecule->atoms[residue->atoms_offset + index];
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_add
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 const char *atom_name,
+                 const char *atom_type,
+                 tng_atom_t *atom)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(atom_name, "TNG library: atom_name must not be a NULL pointer.");
+    TNG_ASSERT(atom_type, "TNG library: atom_type must not be a NULL pointer.");
+
+    return(tng_residue_atom_w_id_add(tng_data, residue, atom_name, atom_type,
+                                     residue->chain->molecule->n_atoms + 1,
+                                     atom));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_w_id_add
+                (tng_trajectory_t tng_data,
+                 tng_residue_t residue,
+                 const char *atom_name,
+                 const char *atom_type,
+                 const int64_t id,
+                 tng_atom_t *atom)
+{
+    int64_t i;
+    tng_atom_t new_atoms;
+    tng_molecule_t molecule = residue->chain->molecule;
+    tng_function_status stat = TNG_SUCCESS;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(atom_name, "TNG library: atom_name must not be a NULL pointer.");
+    TNG_ASSERT(atom_type, "TNG library: atom_type must not be a NULL pointer.");
+
+    if(!residue->n_atoms)
+    {
+        residue->atoms_offset = molecule->n_atoms;
+    }
+
+    new_atoms = realloc(molecule->atoms,
+                        sizeof(struct tng_atom) *
+                        (molecule->n_atoms + 1));
+
+    if(!new_atoms)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_atom) * (molecule->n_atoms + 1),
+               __FILE__, __LINE__);
+        free(molecule->atoms);
+        molecule->atoms = 0;
+        return(TNG_CRITICAL);
+    }
+
+    molecule->atoms = new_atoms;
+
+    *atom = &new_atoms[molecule->n_atoms];
+
+    tng_atom_init(*atom);
+    tng_atom_name_set(tng_data, *atom, atom_name);
+    tng_atom_type_set(tng_data, *atom, atom_type);
+
+    (*atom)->residue = residue;
+
+    for(i = molecule->n_atoms; i--;)
+    {
+        if(molecule->atoms[i].id == id)
+        {
+            stat = TNG_FAILURE;
+            fprintf(stderr, "TNG library: Atom ID %"PRId64" already in use. %s: %d\n", id, __FILE__, __LINE__);
+            break;
+        }
+    }
+
+    residue->n_atoms++;
+    molecule->n_atoms++;
+
+    (*atom)->id = id;
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_alloc(const tng_trajectory_t tng_data,
+                                                         tng_molecule_t *molecule_p)
+{
+    *molecule_p = malloc(sizeof(struct tng_molecule));
+    if(!*molecule_p)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+               sizeof(struct tng_molecule), __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_molecule_init(tng_data, *molecule_p);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_free(const tng_trajectory_t tng_data,
+                                                        tng_molecule_t *molecule_p)
+{
+    if(!*molecule_p)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    tng_molecule_destroy(tng_data, *molecule_p);
+
+    free(*molecule_p);
+    *molecule_p = 0;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_init(const tng_trajectory_t tng_data,
+                                                        tng_molecule_t molecule)
+{
+    (void)tng_data;
+    molecule->quaternary_str = 1;
+    molecule->name = 0;
+    molecule->n_chains = 0;
+    molecule->chains = 0;
+    molecule->n_residues = 0;
+    molecule->residues = 0;
+    molecule->n_atoms = 0;
+    molecule->atoms = 0;
+    molecule->n_bonds = 0;
+    molecule->bonds = 0;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_destroy(const tng_trajectory_t tng_data,
+                                                           tng_molecule_t molecule)
+{
+    int64_t i;
+    (void)tng_data;
+
+    if(molecule->name)
+    {
+        free(molecule->name);
+        molecule->name = 0;
+    }
+
+    if(molecule->chains)
+    {
+        for(i = molecule->n_chains; i--;)
+        {
+            if(molecule->chains[i].name)
+            {
+                free(molecule->chains[i].name);
+                molecule->chains[i].name = 0;
+            }
+        }
+        free(molecule->chains);
+        molecule->chains = 0;
+    }
+    molecule->n_chains = 0;
+
+    if(molecule->residues)
+    {
+        for(i = molecule->n_residues; i--;)
+        {
+            if(molecule->residues[i].name)
+            {
+                free(molecule->residues[i].name);
+                molecule->residues[i].name = 0;
+            }
+        }
+        free(molecule->residues);
+        molecule->residues = 0;
+    }
+    molecule->n_residues = 0;
+
+    if(molecule->atoms)
+    {
+        for(i = molecule->n_atoms; i--;)
+        {
+            tng_atom_destroy(&molecule->atoms[i]);
+        }
+        free(molecule->atoms);
+        molecule->atoms = 0;
+    }
+    molecule->n_atoms = 0;
+
+    if(molecule->bonds)
+    {
+        free(molecule->bonds);
+        molecule->bonds = 0;
+    }
+    molecule->n_bonds = 0;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+
+    strncpy(name, mol->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(mol->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(id, "TNG library: id must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+
+    *id = mol->id;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molsystem_bonds_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n_bonds,
+                 int64_t **from_atoms,
+                 int64_t **to_atoms)
+{
+    int64_t atom_cnt = 0, cnt, mol_cnt, i, j, k;
+    int64_t from_atom, to_atom, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_bond_t bond;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_bonds, "TNG library: n_bonds must not be a NULL pointer.");
+    TNG_ASSERT(from_atoms, "TNG library: from_atoms must not be a NULL pointer.");
+    TNG_ASSERT(to_atoms, "TNG library: to_atoms must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    *n_bonds = 0;
+    /* First count the total number of bonds to allocate memory */
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        mol_cnt = molecule_cnt_list[i];
+        *n_bonds += mol_cnt * mol->n_bonds;
+    }
+    if(*n_bonds == 0)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    *from_atoms = malloc(sizeof(int64_t) * (*n_bonds));
+    if(!*from_atoms)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(int64_t) * (*n_bonds), __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+    *to_atoms = malloc(sizeof(int64_t) * (*n_bonds));
+    if(!*to_atoms)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(int64_t) * (*n_bonds), __FILE__, __LINE__);
+        free(*from_atoms);
+        *from_atoms = 0;
+        return(TNG_CRITICAL);
+    }
+
+    cnt = 0;
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        mol_cnt = molecule_cnt_list[i];
+        for(j = 0; j < mol_cnt; j++)
+        {
+            for(k = 0; k < mol->n_bonds; k++)
+            {
+                bond = &mol->bonds[k];
+                from_atom = atom_cnt + bond->from_atom_id;
+                to_atom = atom_cnt + bond->to_atom_id;
+                (*from_atoms)[cnt] = from_atom;
+                (*to_atoms)[cnt++] = to_atom;
+            }
+            atom_cnt += mol->n_atoms;
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_atom_t atom;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        atom = &mol->atoms[nr % mol->n_atoms];
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+    if(!atom->residue || !atom->residue->chain)
+    {
+        return(TNG_FAILURE);
+    }
+
+    strncpy(name, atom->residue->chain->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(atom->residue->chain->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_atom_t atom;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        atom = &mol->atoms[nr % mol->n_atoms];
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+    if(!atom->residue)
+    {
+        return(TNG_FAILURE);
+    }
+
+    strncpy(name, atom->residue->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(atom->residue->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_atom_t atom;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(id, "TNG library: id must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        atom = &mol->atoms[nr % mol->n_atoms];
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+    if(!atom->residue)
+    {
+        return(TNG_FAILURE);
+    }
+
+    *id = atom->residue->id;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_global_residue_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id)
+{
+    int64_t cnt = 0, i, offset = 0, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_atom_t atom;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(id, "TNG library: id must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            offset += mol->n_residues * molecule_cnt_list[i];
+            continue;
+        }
+        atom = &mol->atoms[nr % mol->n_atoms];
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+    if(!atom->residue)
+    {
+        return(TNG_FAILURE);
+    }
+
+    offset += mol->n_residues * ((nr - cnt) / mol->n_atoms);
+
+    *id = atom->residue->id + offset;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_atom_t atom;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        atom = &mol->atoms[nr % mol->n_atoms];
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+
+    strncpy(name, atom->name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(atom->name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_atom_type_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *type,
+                 int max_len)
+{
+    int64_t cnt = 0, i, *molecule_cnt_list = 0;
+    tng_molecule_t mol;
+    tng_atom_t atom;
+    tng_bool found = TNG_FALSE;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    tng_molecule_cnt_list_get(tng_data, &molecule_cnt_list);
+
+    if(!molecule_cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = 0; i < tng_data->n_molecules; i++)
+    {
+        mol = &tng_data->molecules[i];
+        if(cnt + mol->n_atoms * molecule_cnt_list[i] - 1 < nr)
+        {
+            cnt += mol->n_atoms * molecule_cnt_list[i];
+            continue;
+        }
+        atom = &mol->atoms[nr % mol->n_atoms];
+        found = TNG_TRUE;
+        break;
+    }
+    if(!found)
+    {
+        return(TNG_FAILURE);
+    }
+
+    strncpy(type, atom->atom_type, max_len - 1);
+    type[max_len - 1] = 0;
+
+    if(strlen(atom->atom_type) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_mapping_add
+                (tng_trajectory_t tng_data,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t *mapping_table)
+{
+    int64_t i;
+    tng_particle_mapping_t mapping;
+    tng_trajectory_frame_set_t frame_set;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    /* Sanity check of the particle ranges. Split into multiple if
+     * statements for improved readability */
+    for(i = 0; i < frame_set->n_mapping_blocks; i++)
+    {
+        mapping = &frame_set->mappings[i];
+        if(num_first_particle >= mapping->num_first_particle &&
+           num_first_particle < mapping->num_first_particle +
+                                   mapping->n_particles)
+        {
+            fprintf(stderr, "TNG library: Particle mapping overlap. %s: %d\n", __FILE__, __LINE__);
+            return(TNG_FAILURE);
+        }
+        if(num_first_particle + n_particles >=
+           mapping->num_first_particle &&
+           num_first_particle + n_particles <
+           mapping->num_first_particle + mapping->n_particles)
+        {
+            fprintf(stderr, "TNG library: Particle mapping overlap. %s: %d\n", __FILE__, __LINE__);
+            return(TNG_FAILURE);
+        }
+        if(mapping->num_first_particle >= num_first_particle &&
+           mapping->num_first_particle < num_first_particle +
+                                            n_particles)
+        {
+            fprintf(stderr, "TNG library: Particle mapping overlap. %s: %d\n", __FILE__, __LINE__);
+            return(TNG_FAILURE);
+        }
+        if(mapping->num_first_particle + mapping->n_particles >
+           num_first_particle &&
+           mapping->num_first_particle + mapping->n_particles <
+           num_first_particle + n_particles)
+        {
+            fprintf(stderr, "TNG library: Particle mapping overlap. %s: %d\n", __FILE__, __LINE__);
+            return(TNG_FAILURE);
+        }
+    }
+
+    frame_set->n_mapping_blocks++;
+
+    mapping = realloc(frame_set->mappings, sizeof(struct tng_particle_mapping) *
+                      frame_set->n_mapping_blocks);
+
+    if(!mapping)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(struct tng_particle_mapping)*frame_set->n_mapping_blocks,
+               __FILE__, __LINE__);
+        free(frame_set->mappings);
+        frame_set->mappings = 0;
+        return(TNG_CRITICAL);
+    }
+    frame_set->mappings = mapping;
+
+    frame_set->mappings[frame_set->n_mapping_blocks - 1].num_first_particle = num_first_particle;
+    frame_set->mappings[frame_set->n_mapping_blocks - 1].n_particles = n_particles;
+
+    frame_set->mappings[frame_set->n_mapping_blocks - 1].real_particle_numbers = malloc(sizeof(int64_t) * n_particles);
+    if(!frame_set->mappings[frame_set->n_mapping_blocks - 1].real_particle_numbers)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(int64_t) * n_particles, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    for(i=0; i<n_particles; i++)
+    {
+        frame_set->mappings[frame_set->n_mapping_blocks - 1].real_particle_numbers[i] = mapping_table[i];
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_particle_mapping_free(tng_trajectory_t tng_data)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_mapping_t mapping;
+    int64_t i;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(frame_set->n_mapping_blocks && frame_set->mappings)
+    {
+        for(i = frame_set->n_mapping_blocks; i--;)
+        {
+            mapping = &frame_set->mappings[i];
+            if(mapping->real_particle_numbers)
+            {
+                free(mapping->real_particle_numbers);
+                mapping->real_particle_numbers = 0;
+            }
+        }
+        free(frame_set->mappings);
+        frame_set->mappings = 0;
+        frame_set->n_mapping_blocks = 0;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init(tng_trajectory_t *tng_data_p)
+{
+    time_t seconds;
+    tng_trajectory_frame_set_t frame_set;
+    tng_trajectory_t tng_data;
+
+    *tng_data_p = malloc(sizeof(struct tng_trajectory));
+    if(!*tng_data_p)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+               sizeof(struct tng_trajectory), __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_data = *tng_data_p;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    tng_data->input_file_path = 0;
+    tng_data->input_file = 0;
+    tng_data->input_file_len = 0;
+    tng_data->output_file_path = 0;
+    tng_data->output_file = 0;
+
+    tng_data->first_program_name = 0;
+    tng_data->first_user_name = 0;
+    tng_data->first_computer_name = 0;
+    tng_data->first_pgp_signature = 0;
+    tng_data->last_program_name = 0;
+    tng_data->last_user_name = 0;
+    tng_data->last_computer_name = 0;
+    tng_data->last_pgp_signature = 0;
+    tng_data->forcefield_name = 0;
+
+    seconds = time(0);
+    if ( seconds == -1)
+    {
+        fprintf(stderr, "TNG library: Cannot get time. %s: %d\n", __FILE__, __LINE__);
+    }
+    else
+    {
+        tng_data->time = seconds;
+    }
+
+    tng_data->var_num_atoms_flag = TNG_CONSTANT_N_ATOMS;
+    tng_data->first_trajectory_frame_set_input_file_pos = -1;
+    tng_data->last_trajectory_frame_set_input_file_pos = -1;
+    tng_data->current_trajectory_frame_set_input_file_pos = -1;
+    tng_data->first_trajectory_frame_set_output_file_pos = -1;
+    tng_data->last_trajectory_frame_set_output_file_pos = -1;
+    tng_data->current_trajectory_frame_set_output_file_pos = -1;
+    tng_data->frame_set_n_frames = 100;
+    tng_data->n_trajectory_frame_sets = 0;
+    tng_data->n_trajectory_blocks = 0;
+    tng_data->medium_stride_length = 100;
+    tng_data->long_stride_length = 10000;
+
+    tng_data->time_per_frame = -1;
+
+    tng_data->n_particle_data_blocks = 0;
+    tng_data->n_data_blocks = 0;
+
+    tng_data->non_tr_particle_data = 0;
+    tng_data->non_tr_data = 0;
+
+    tng_data->compress_algo_pos = 0;
+    tng_data->compress_algo_vel = 0;
+    tng_data->compression_precision = 1000;
+    tng_data->distance_unit_exponential = -9;
+
+    frame_set->first_frame = -1;
+    frame_set->n_mapping_blocks = 0;
+    frame_set->mappings = 0;
+    frame_set->molecule_cnt_list = 0;
+
+    frame_set->n_particle_data_blocks = 0;
+    frame_set->n_data_blocks = 0;
+
+    frame_set->tr_particle_data = 0;
+    frame_set->tr_data = 0;
+
+    frame_set->n_written_frames = 0;
+    frame_set->n_unwritten_frames = 0;
+
+    frame_set->next_frame_set_file_pos = -1;
+    frame_set->prev_frame_set_file_pos = -1;
+    frame_set->medium_stride_next_frame_set_file_pos = -1;
+    frame_set->medium_stride_prev_frame_set_file_pos = -1;
+    frame_set->long_stride_next_frame_set_file_pos = -1;
+    frame_set->long_stride_prev_frame_set_file_pos = -1;
+
+    frame_set->first_frame_time = -1;
+
+    tng_data->n_molecules = 0;
+    tng_data->molecules = 0;
+    tng_data->molecule_cnt_list = 0;
+    tng_data->n_particles = 0;
+
+    {
+      /* Check the endianness of the computer */
+      static int32_t endianness_32 = 0x01234567;
+      /* 0x01234567 */
+      if ( *(const unsigned char*)&endianness_32 == 0x01 )
+        {
+          tng_data->endianness_32 = TNG_BIG_ENDIAN_32;
+        }
+
+      /* 0x67452301 */
+      else if( *(const unsigned char*)&endianness_32 == 0x67 )
+        {
+          tng_data->endianness_32 = TNG_LITTLE_ENDIAN_32;
+
+        }
+
+      /* 0x45670123 */
+      else if ( *(const unsigned char*)&endianness_32 == 0x45 )
+        {
+          tng_data->endianness_32 = TNG_BYTE_PAIR_SWAP_32;
+        }
+    }
+    {
+      static int64_t endianness_64 = 0x0123456789ABCDEFLL;
+      /* 0x0123456789ABCDEF */
+      if ( *(const unsigned char*)&endianness_64 == 0x01 )
+        {
+          tng_data->endianness_64 = TNG_BIG_ENDIAN_64;
+        }
+
+      /* 0xEFCDAB8967452301 */
+      else if ( *(const unsigned char*)&endianness_64 == 0xEF )
+        {
+          tng_data->endianness_64 = TNG_LITTLE_ENDIAN_64;
+        }
+
+      /* 0x89ABCDEF01234567 */
+      else if ( *(const unsigned char*)&endianness_64 == 0x89 )
+        {
+          tng_data->endianness_64 = TNG_QUAD_SWAP_64;
+        }
+
+      /* 0x45670123CDEF89AB */
+      else if ( *(const unsigned char*)&endianness_64 == 0x45 )
+        {
+          tng_data->endianness_64 = TNG_BYTE_PAIR_SWAP_64;
+        }
+
+      /* 0x23016745AB89EFCD */
+      else if ( *(const unsigned char*)&endianness_64 == 0x23 )
+        {
+          tng_data->endianness_64 = TNG_BYTE_SWAP_64;
+        }
+    }
+
+    /* By default do not swap the byte order, i.e. keep the byte order of the
+     * architecture. The input file endianness will be set when reading the
+     * header. The output endianness can be changed - before the file is
+     * written. */
+    tng_data->input_endianness_swap_func_32 = 0;
+    tng_data->input_endianness_swap_func_64 = 0;
+    tng_data->output_endianness_swap_func_32 = 0;
+    tng_data->output_endianness_swap_func_64 = 0;
+
+    tng_data->current_trajectory_frame_set.next_frame_set_file_pos = -1;
+    tng_data->current_trajectory_frame_set.prev_frame_set_file_pos = -1;
+    tng_data->current_trajectory_frame_set.n_frames = 0;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_destroy(tng_trajectory_t *tng_data_p)
+{
+    int64_t i, j, k, l;
+    int64_t n_particles, n_values_per_frame;
+    tng_trajectory_t tng_data = *tng_data_p;
+    tng_trajectory_frame_set_t frame_set;
+
+    if(!*tng_data_p)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(tng_data->input_file_path)
+    {
+        free(tng_data->input_file_path);
+        tng_data->input_file_path = 0;
+    }
+
+    if(tng_data->input_file)
+    {
+        fclose(tng_data->input_file);
+        tng_data->input_file = 0;
+    }
+
+    if(tng_data->output_file_path)
+    {
+        free(tng_data->output_file_path);
+        tng_data->output_file_path = 0;
+    }
+
+    if(tng_data->output_file)
+    {
+        /* FIXME: Do not always write the hash */
+        tng_frame_set_finalize(tng_data, TNG_USE_HASH);
+        fclose(tng_data->output_file);
+        tng_data->output_file = 0;
+    }
+
+    if(tng_data->first_program_name)
+    {
+        free(tng_data->first_program_name);
+        tng_data->first_program_name = 0;
+    }
+
+    if(tng_data->last_program_name)
+    {
+        free(tng_data->last_program_name);
+        tng_data->last_program_name = 0;
+    }
+
+    if(tng_data->first_user_name)
+    {
+        free(tng_data->first_user_name);
+        tng_data->first_user_name = 0;
+    }
+
+    if(tng_data->last_user_name)
+    {
+        free(tng_data->last_user_name);
+        tng_data->last_user_name = 0;
+    }
+
+    if(tng_data->first_computer_name)
+    {
+        free(tng_data->first_computer_name);
+        tng_data->first_computer_name = 0;
+    }
+
+    if(tng_data->last_computer_name)
+    {
+        free(tng_data->last_computer_name);
+        tng_data->last_computer_name = 0;
+    }
+
+    if(tng_data->first_pgp_signature)
+    {
+        free(tng_data->first_pgp_signature);
+        tng_data->first_pgp_signature = 0;
+    }
+
+    if(tng_data->last_pgp_signature)
+    {
+        free(tng_data->last_pgp_signature);
+        tng_data->last_pgp_signature = 0;
+    }
+
+    if(tng_data->forcefield_name)
+    {
+        free(tng_data->forcefield_name);
+        tng_data->forcefield_name = 0;
+    }
+
+    tng_frame_set_particle_mapping_free(tng_data);
+
+    if(frame_set->molecule_cnt_list)
+    {
+        free(frame_set->molecule_cnt_list);
+        frame_set->molecule_cnt_list = 0;
+    }
+
+    if(tng_data->var_num_atoms_flag)
+    {
+        n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        n_particles = tng_data->n_particles;
+    }
+
+    if(tng_data->non_tr_particle_data)
+    {
+        for(i = tng_data->n_particle_data_blocks; i--; )
+        {
+            if(tng_data->non_tr_particle_data[i].values)
+            {
+                free(tng_data->non_tr_particle_data[i].values);
+                tng_data->non_tr_particle_data[i].values = 0;
+            }
+
+            if(tng_data->non_tr_particle_data[i].strings)
+            {
+                n_values_per_frame = tng_data->non_tr_particle_data[i].
+                                     n_values_per_frame;
+                if(tng_data->non_tr_particle_data[i].strings[0])
+                {
+                    for(j = n_particles; j--;)
+                    {
+                        if(tng_data->non_tr_particle_data[i].strings[0][j])
+                        {
+                            for(k = n_values_per_frame; k--;)
+                            {
+                                if(tng_data->non_tr_particle_data[i].
+                                   strings[0][j][k])
+                                {
+                                    free(tng_data->non_tr_particle_data[i].
+                                         strings[0][j][k]);
+                                    tng_data->non_tr_particle_data[i].
+                                    strings[0][j][k] = 0;
+                                }
+                            }
+                            free(tng_data->non_tr_particle_data[i].
+                                 strings[0][j]);
+                            tng_data->non_tr_particle_data[i].strings[0][j] = 0;
+                        }
+                    }
+                    free(tng_data->non_tr_particle_data[i].strings[0]);
+                    tng_data->non_tr_particle_data[i].strings[0] = 0;
+                }
+                free(tng_data->non_tr_particle_data[i].strings);
+                tng_data->non_tr_particle_data[i].strings = 0;
+            }
+
+            if(tng_data->non_tr_particle_data[i].block_name)
+            {
+                free(tng_data->non_tr_particle_data[i].block_name);
+                tng_data->non_tr_particle_data[i].block_name = 0;
+            }
+        }
+        free(tng_data->non_tr_particle_data);
+        tng_data->non_tr_particle_data = 0;
+    }
+
+    if(tng_data->non_tr_data)
+    {
+        for(i = tng_data->n_data_blocks; i--;)
+        {
+            if(tng_data->non_tr_data[i].values)
+            {
+                free(tng_data->non_tr_data[i].values);
+                tng_data->non_tr_data[i].values = 0;
+            }
+
+            if(tng_data->non_tr_data[i].strings)
+            {
+                n_values_per_frame = tng_data->non_tr_data[i].
+                                     n_values_per_frame;
+                if(tng_data->non_tr_data[i].strings[0])
+                {
+                    for(j = n_values_per_frame; j--;)
+                    {
+                        if(tng_data->non_tr_data[i].strings[0][j])
+                        {
+                            free(tng_data->non_tr_data[i].strings[0][j]);
+                            tng_data->non_tr_data[i].strings[0][j] = 0;
+                        }
+                    }
+                    free(tng_data->non_tr_data[i].strings[0]);
+                    tng_data->non_tr_data[i].strings[0] = 0;
+                }
+                free(tng_data->non_tr_data[i].strings);
+                tng_data->non_tr_data[i].strings = 0;
+            }
+
+            if(tng_data->non_tr_data[i].block_name)
+            {
+                free(tng_data->non_tr_data[i].block_name);
+                tng_data->non_tr_data[i].block_name = 0;
+            }
+        }
+        free(tng_data->non_tr_data);
+        tng_data->non_tr_data = 0;
+    }
+
+    tng_data->n_particle_data_blocks = 0;
+    tng_data->n_data_blocks = 0;
+
+    if(tng_data->compress_algo_pos)
+    {
+        free(tng_data->compress_algo_pos);
+        tng_data->compress_algo_pos = 0;
+    }
+    if(tng_data->compress_algo_vel)
+    {
+        free(tng_data->compress_algo_vel);
+        tng_data->compress_algo_vel = 0;
+    }
+
+    if(frame_set->tr_particle_data)
+    {
+        for(i = frame_set->n_particle_data_blocks; i--; )
+        {
+            if(frame_set->tr_particle_data[i].values)
+            {
+                free(frame_set->tr_particle_data[i].values);
+                frame_set->tr_particle_data[i].values = 0;
+            }
+
+            if(frame_set->tr_particle_data[i].strings)
+            {
+                n_values_per_frame = frame_set->tr_particle_data[i].
+                                     n_values_per_frame;
+                for(j = frame_set->tr_particle_data[i].n_frames; j--;)
+                {
+                    if(frame_set->tr_particle_data[i].strings[j])
+                    {
+                        for(k = n_particles; k--;)
+                        {
+                            if(frame_set->tr_particle_data[i].
+                                strings[j][k])
+                            {
+                                for(l = n_values_per_frame; l--;)
+                                {
+                                    if(frame_set->tr_particle_data[i].
+                                        strings[j][k][l])
+                                    {
+                                        free(frame_set->tr_particle_data[i].
+                                                strings[j][k][l]);
+                                        frame_set->tr_particle_data[i].
+                                        strings[j][k][l] = 0;
+                                    }
+                                }
+                                free(frame_set->tr_particle_data[i].
+                                        strings[j][k]);
+                                frame_set->tr_particle_data[i].
+                                strings[j][k] = 0;
+                            }
+                        }
+                        free(frame_set->tr_particle_data[i].strings[j]);
+                        frame_set->tr_particle_data[i].strings[j] = 0;
+                    }
+                }
+                free(frame_set->tr_particle_data[i].strings);
+                frame_set->tr_particle_data[i].strings = 0;
+            }
+
+            if(frame_set->tr_particle_data[i].block_name)
+            {
+                free(frame_set->tr_particle_data[i].block_name);
+                frame_set->tr_particle_data[i].block_name = 0;
+            }
+        }
+        free(frame_set->tr_particle_data);
+        frame_set->tr_particle_data = 0;
+    }
+
+    if(frame_set->tr_data)
+    {
+        for(i = frame_set->n_data_blocks; i--;)
+        {
+            if(frame_set->tr_data[i].values)
+            {
+                free(frame_set->tr_data[i].values);
+                frame_set->tr_data[i].values = 0;
+            }
+
+            if(frame_set->tr_data[i].strings)
+            {
+                n_values_per_frame = frame_set->tr_data[i].
+                                     n_values_per_frame;
+                for(j = frame_set->tr_data[i].n_frames; j--;)
+                {
+                    if(frame_set->tr_data[i].strings[j])
+                    {
+                        for(k = n_values_per_frame; k--;)
+                        {
+                            if(frame_set->tr_data[i].strings[j][k])
+                            {
+                                free(frame_set->tr_data[i].strings[j][k]);
+                                frame_set->tr_data[i].strings[j][k] = 0;
+                            }
+                        }
+                        free(frame_set->tr_data[i].strings[j]);
+                        frame_set->tr_data[i].strings[j] = 0;
+                    }
+                }
+                free(frame_set->tr_data[i].strings);
+                frame_set->tr_data[i].strings = 0;
+            }
+
+            if(frame_set->tr_data[i].block_name)
+            {
+                free(frame_set->tr_data[i].block_name);
+                frame_set->tr_data[i].block_name = 0;
+            }
+        }
+        free(frame_set->tr_data);
+        frame_set->tr_data = 0;
+    }
+
+    frame_set->n_particle_data_blocks = 0;
+    frame_set->n_data_blocks = 0;
+
+    if(tng_data->molecules)
+    {
+        for(i=tng_data->n_molecules; i--;)
+        {
+            tng_molecule_destroy(tng_data, &tng_data->molecules[i]);
+        }
+        free(tng_data->molecules);
+        tng_data->molecules = 0;
+        tng_data->n_molecules = 0;
+    }
+    if(tng_data->molecule_cnt_list)
+    {
+        free(tng_data->molecule_cnt_list);
+        tng_data->molecule_cnt_list = 0;
+    }
+
+    free(*tng_data_p);
+    *tng_data_p = 0;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init_from_src(tng_trajectory_t src,
+                                                 tng_trajectory_t *dest_p)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_trajectory_t dest;
+
+    TNG_ASSERT(src != 0, "TNG library: Source trajectory must not be NULL.");
+
+    *dest_p = malloc(sizeof(struct tng_trajectory));
+    if(!*dest_p)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%lu bytes). %s: %d\n",
+               sizeof(struct tng_trajectory), __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    dest = *dest_p;
+
+    frame_set = &dest->current_trajectory_frame_set;
+
+    strcpy(dest->input_file_path, src->input_file_path);
+    dest->input_file = 0;
+    dest->input_file_len = src->input_file_len;
+    strcpy(dest->output_file_path, src->output_file_path);
+    dest->output_file = 0;
+
+    dest->first_program_name = 0;
+    dest->first_user_name = 0;
+    dest->first_computer_name = 0;
+    dest->first_pgp_signature = 0;
+    dest->last_program_name = 0;
+    dest->last_user_name = 0;
+    dest->last_computer_name = 0;
+    dest->last_pgp_signature = 0;
+    dest->forcefield_name = 0;
+
+    dest->var_num_atoms_flag = src->var_num_atoms_flag;
+    dest->first_trajectory_frame_set_input_file_pos =
+    src->first_trajectory_frame_set_input_file_pos;
+    dest->last_trajectory_frame_set_input_file_pos =
+    src->last_trajectory_frame_set_input_file_pos;
+    dest->current_trajectory_frame_set_input_file_pos =
+    src->current_trajectory_frame_set_input_file_pos;
+    dest->first_trajectory_frame_set_output_file_pos =
+    src->first_trajectory_frame_set_output_file_pos;
+    dest->last_trajectory_frame_set_output_file_pos =
+    src->last_trajectory_frame_set_output_file_pos;
+    dest->current_trajectory_frame_set_output_file_pos =
+    src->current_trajectory_frame_set_output_file_pos;
+    dest->frame_set_n_frames = src->frame_set_n_frames;
+    dest->n_trajectory_frame_sets = src->n_trajectory_frame_sets;
+    dest->n_trajectory_blocks = src->n_trajectory_blocks;
+    dest->medium_stride_length = src->medium_stride_length;
+    dest->long_stride_length = src->long_stride_length;
+
+    dest->time_per_frame = src->time_per_frame;
+
+    /* Currently the non trajectory data blocks are not copied since it
+     * can lead to problems when freeing memory in a parallel block. */
+    dest->n_particle_data_blocks = 0;
+    dest->n_data_blocks = 0;
+    dest->non_tr_particle_data = 0;
+    dest->non_tr_data = 0;
+
+    dest->compress_algo_pos = 0;
+    dest->compress_algo_vel = 0;
+    dest->distance_unit_exponential = -9;
+    dest->compression_precision = 1000;
+
+    frame_set->n_mapping_blocks = 0;
+    frame_set->mappings = 0;
+    frame_set->molecule_cnt_list = 0;
+
+    frame_set->n_particle_data_blocks = 0;
+    frame_set->n_data_blocks = 0;
+
+    frame_set->tr_particle_data = 0;
+    frame_set->tr_data = 0;
+
+    frame_set->next_frame_set_file_pos = -1;
+    frame_set->prev_frame_set_file_pos = -1;
+    frame_set->medium_stride_next_frame_set_file_pos = -1;
+    frame_set->medium_stride_prev_frame_set_file_pos = -1;
+    frame_set->long_stride_next_frame_set_file_pos = -1;
+    frame_set->long_stride_prev_frame_set_file_pos = -1;
+    frame_set->first_frame = -1;
+
+    dest->n_molecules = 0;
+    dest->molecules = 0;
+    dest->molecule_cnt_list = 0;
+    dest->n_particles = src->n_particles;
+
+    dest->endianness_32 = src->endianness_32;
+    dest->endianness_64 = src->endianness_64;
+    dest->input_endianness_swap_func_32 = src->input_endianness_swap_func_32;
+    dest->input_endianness_swap_func_64 = src->input_endianness_swap_func_64;
+    dest->output_endianness_swap_func_32 = src->output_endianness_swap_func_32;
+    dest->output_endianness_swap_func_64 = src->output_endianness_swap_func_64;
+
+    dest->current_trajectory_frame_set.next_frame_set_file_pos = -1;
+    dest->current_trajectory_frame_set.prev_frame_set_file_pos = -1;
+    dest->current_trajectory_frame_set.n_frames = 0;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_input_file_get(const tng_trajectory_t tng_data,
+                                       char *file_name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(file_name, "TNG library: file_name must not be a NULL pointer");
+
+    strncpy(file_name, tng_data->input_file_path, max_len - 1);
+    file_name[max_len - 1] = 0;
+
+    if(strlen(tng_data->input_file_path) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_input_file_set(tng_trajectory_t tng_data,
+                                                         const char *file_name)
+{
+    unsigned int len;
+    char *temp;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(file_name, "TNG library: file_name must not be a NULL pointer");
+
+
+    if(tng_data->input_file_path && strcmp(tng_data->input_file_path,
+                                           file_name) == 0)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    if(tng_data->input_file)
+    {
+        fclose(tng_data->input_file);
+    }
+
+    len = tng_min_i((int)strlen(file_name) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->input_file_path, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->input_file_path);
+        tng_data->input_file_path = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->input_file_path = temp;
+
+    strncpy(tng_data->input_file_path, file_name, len);
+
+    return(tng_input_file_init(tng_data));
+}
+
+tng_function_status tng_output_file_get(const tng_trajectory_t tng_data,
+                                       char *file_name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(file_name, "TNG library: file_name must not be a NULL pointer");
+
+    strncpy(file_name, tng_data->output_file_path, max_len - 1);
+    file_name[max_len - 1] = 0;
+
+    if(strlen(tng_data->output_file_path) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_set(tng_trajectory_t tng_data,
+                                                          const char *file_name)
+{
+    int len;
+    char *temp;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(file_name, "TNG library: file_name must not be a NULL pointer");
+
+    if(tng_data->output_file_path &&
+       strcmp(tng_data->output_file_path, file_name) == 0)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    if(tng_data->output_file)
+    {
+        fclose(tng_data->output_file);
+    }
+
+    len = tng_min_i((int)strlen(file_name) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->output_file_path, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->output_file_path);
+        tng_data->output_file_path = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->output_file_path = temp;
+
+    strncpy(tng_data->output_file_path, file_name, len);
+
+    return(tng_output_file_init(tng_data));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_append_file_set
+                (tng_trajectory_t tng_data,
+                 const char *file_name)
+{
+    int len;
+    char *temp;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(file_name, "TNG library: file_name must not be a NULL pointer");
+
+    if(tng_data->output_file_path &&
+       strcmp(tng_data->output_file_path, file_name) == 0)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    if(tng_data->output_file)
+    {
+        fclose(tng_data->output_file);
+    }
+
+    len = tng_min_i((int)strlen(file_name) + 1, TNG_MAX_STR_LEN);
+    temp = realloc(tng_data->output_file_path, len);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n", len,
+               __FILE__, __LINE__);
+        free(tng_data->output_file_path);
+        tng_data->output_file_path = 0;
+        return(TNG_CRITICAL);
+    }
+    tng_data->output_file_path = temp;
+
+    strncpy(tng_data->output_file_path, file_name, len);
+
+    tng_data->output_file = fopen(tng_data->output_file_path, "r+");
+    if(!tng_data->output_file)
+    {
+        fprintf(stderr, "TNG library: Cannot open file %s. %s: %d\n",
+                tng_data->output_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_get
+                (tng_trajectory_t tng_data, tng_file_endianness *endianness)
+{
+    tng_endianness_32 end_32;
+    tng_endianness_64 end_64;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(endianness, "TNG library: endianness must not be a NULL pointer");
+
+    if(tng_data->output_endianness_swap_func_32)
+    {
+        /* If other endianness variants are added they must be added here as well */
+        if(tng_data->output_endianness_swap_func_32 ==
+           &tng_swap_byte_order_big_endian_32)
+        {
+            end_32 = TNG_BIG_ENDIAN_32;
+        }
+        else if(tng_data->output_endianness_swap_func_32 ==
+                &tng_swap_byte_order_little_endian_32)
+        {
+            end_32 = TNG_LITTLE_ENDIAN_32;
+        }
+        else
+        {
+            return(TNG_FAILURE);
+        }
+    }
+    else
+    {
+        end_32 = (tng_endianness_32)tng_data->endianness_32;
+    }
+
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        /* If other endianness variants are added they must be added here as well */
+        if(tng_data->output_endianness_swap_func_64 ==
+           &tng_swap_byte_order_big_endian_64)
+        {
+            end_64 = TNG_BIG_ENDIAN_64;
+        }
+        else if(tng_data->output_endianness_swap_func_64 ==
+                &tng_swap_byte_order_little_endian_64)
+        {
+            end_64 = TNG_LITTLE_ENDIAN_64;
+        }
+        else
+        {
+            return(TNG_FAILURE);
+        }
+    }
+    else
+    {
+        end_64 = (tng_endianness_64)tng_data->endianness_64;
+    }
+
+    if((int)end_32 != (int)end_64)
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(end_32 == TNG_LITTLE_ENDIAN_32)
+    {
+        *endianness = TNG_LITTLE_ENDIAN;
+    }
+
+    else if(end_32 == TNG_BIG_ENDIAN_32)
+    {
+        *endianness = TNG_BIG_ENDIAN;
+    }
+    else
+    {
+        return(TNG_FAILURE);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_set
+                (tng_trajectory_t tng_data,
+                 const tng_file_endianness endianness)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    /* Tne endianness cannot be changed if the data has already been written
+     * to the output file. */
+    if(ftell(tng_data->output_file) > 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(endianness == TNG_BIG_ENDIAN)
+    {
+        if(tng_data->endianness_32 == TNG_BIG_ENDIAN_32)
+        {
+            tng_data->output_endianness_swap_func_32 = 0;
+        }
+        else
+        {
+            tng_data->output_endianness_swap_func_32 =
+            &tng_swap_byte_order_big_endian_32;
+        }
+        if(tng_data->endianness_64 == TNG_BIG_ENDIAN_64)
+        {
+            tng_data->output_endianness_swap_func_64 = 0;
+        }
+        else
+        {
+            tng_data->output_endianness_swap_func_64 =
+            &tng_swap_byte_order_big_endian_64;
+        }
+        return(TNG_SUCCESS);
+    }
+    else if(endianness == TNG_LITTLE_ENDIAN)
+    {
+        if(tng_data->endianness_32 == TNG_LITTLE_ENDIAN_32)
+        {
+            tng_data->output_endianness_swap_func_32 = 0;
+        }
+        else
+        {
+            tng_data->output_endianness_swap_func_32 =
+            &tng_swap_byte_order_little_endian_32;
+        }
+        if(tng_data->endianness_64 == TNG_LITTLE_ENDIAN_64)
+        {
+            tng_data->output_endianness_swap_func_64 = 0;
+        }
+        else
+        {
+            tng_data->output_endianness_swap_func_64 =
+            &tng_swap_byte_order_little_endian_64;
+        }
+        return(TNG_SUCCESS);
+    }
+
+    /* If the specified endianness is neither big nor little endian return a
+     * failure. */
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->first_program_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->first_program_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_set(tng_trajectory_t tng_data,
+                                                                 const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    if(tng_data->first_program_name && strlen(tng_data->first_program_name) < len)
+    {
+        free(tng_data->first_program_name);
+        tng_data->first_program_name = 0;
+    }
+    if(!tng_data->first_program_name)
+    {
+        tng_data->first_program_name = malloc(len);
+        if(!tng_data->first_program_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->first_program_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->last_program_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->last_program_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_set
+                    (tng_trajectory_t tng_data,
+                     const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    if(tng_data->last_program_name && strlen(tng_data->last_program_name) < len)
+    {
+        free(tng_data->last_program_name);
+        tng_data->last_program_name = 0;
+    }
+    if(!tng_data->last_program_name)
+    {
+        tng_data->last_program_name = malloc(len);
+        if(!tng_data->last_program_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->last_program_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->first_user_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->first_user_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_set
+                    (tng_trajectory_t tng_data,
+                     const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->first_user_name && strlen(tng_data->first_user_name) < len)
+    {
+        free(tng_data->first_user_name);
+        tng_data->first_user_name = 0;
+    }
+    if(!tng_data->first_user_name)
+    {
+        tng_data->first_user_name = malloc(len);
+        if(!tng_data->first_user_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->first_user_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->last_user_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->last_user_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_set
+                    (tng_trajectory_t tng_data,
+                     const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->last_user_name && strlen(tng_data->last_user_name) < len)
+    {
+        free(tng_data->last_user_name);
+        tng_data->last_user_name = 0;
+    }
+    if(!tng_data->last_user_name)
+    {
+        tng_data->last_user_name = malloc(len);
+        if(!tng_data->last_user_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->last_user_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->first_computer_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->first_computer_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_set
+                    (tng_trajectory_t tng_data,
+                     const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->first_computer_name && strlen(tng_data->first_computer_name) < len)
+    {
+        free(tng_data->first_computer_name);
+        tng_data->first_computer_name = 0;
+    }
+    if(!tng_data->first_computer_name)
+    {
+        tng_data->first_computer_name = malloc(len);
+        if(!tng_data->first_computer_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->first_computer_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->last_computer_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->last_computer_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_set
+                    (tng_trajectory_t tng_data,
+                     const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->last_computer_name && strlen(tng_data->last_computer_name) <
+        len)
+    {
+        free(tng_data->last_computer_name);
+        tng_data->last_computer_name = 0;
+    }
+    if(!tng_data->last_computer_name)
+    {
+        tng_data->last_computer_name = malloc(len);
+        if(!tng_data->last_computer_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->last_computer_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_get
+                    (const tng_trajectory_t tng_data,
+                     char *signature, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(signature, "TNG library: signature must not be a NULL pointer");
+
+    strncpy(signature, tng_data->first_pgp_signature, max_len - 1);
+    signature[max_len - 1] = 0;
+
+    if(strlen(tng_data->first_pgp_signature) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_set
+                    (tng_trajectory_t tng_data,
+                     const char *signature)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(signature, "TNG library: signature must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(signature) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->first_pgp_signature && strlen(tng_data->first_pgp_signature) <
+        len)
+    {
+        free(tng_data->first_pgp_signature);
+        tng_data->first_pgp_signature = 0;
+    }
+    if(!tng_data->first_pgp_signature)
+    {
+        tng_data->first_pgp_signature = malloc(len);
+        if(!tng_data->first_pgp_signature)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->first_pgp_signature, signature, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_get
+                    (const tng_trajectory_t tng_data,
+                     char *signature, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(signature, "TNG library: signature must not be a NULL pointer");
+
+    strncpy(signature, tng_data->last_pgp_signature, max_len - 1);
+    signature[max_len - 1] = 0;
+
+    if(strlen(tng_data->last_pgp_signature) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_set
+                    (tng_trajectory_t tng_data,
+                     const char *signature)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(signature, "TNG library: signature must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(signature) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->last_pgp_signature && strlen(tng_data->last_pgp_signature) <
+        len)
+    {
+        free(tng_data->last_pgp_signature);
+        tng_data->last_pgp_signature = 0;
+    }
+    if(!tng_data->last_pgp_signature)
+    {
+        tng_data->last_pgp_signature = malloc(len);
+        if(!tng_data->last_pgp_signature)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->last_pgp_signature, signature, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_get
+                    (const tng_trajectory_t tng_data,
+                     char *name, const int max_len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+
+    strncpy(name, tng_data->forcefield_name, max_len - 1);
+    name[max_len - 1] = 0;
+
+    if(strlen(tng_data->forcefield_name) > (unsigned int)max_len - 1)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_set
+                    (tng_trajectory_t tng_data,
+                     const char *new_name)
+{
+    unsigned int len;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(new_name, "TNG library: new_name must not be a NULL pointer");
+
+    len = tng_min_i((int)strlen(new_name) + 1, TNG_MAX_STR_LEN);
+
+    /* If the currently stored string length is not enough to store the new
+     * string it is freed and reallocated. */
+    if(tng_data->forcefield_name && strlen(tng_data->forcefield_name) < len)
+    {
+        free(tng_data->forcefield_name);
+        tng_data->forcefield_name = 0;
+    }
+    if(!tng_data->forcefield_name)
+    {
+        tng_data->forcefield_name = malloc(len);
+        if(!tng_data->forcefield_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%ud bytes). %s: %d\n", len,
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    strncpy(tng_data->forcefield_name, new_name, len);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_get
+                    (const tng_trajectory_t tng_data,
+                     int64_t *len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(len, "TNG library: len must not be a NULL pointer");
+
+    *len = tng_data->medium_stride_length;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_set
+                    (tng_trajectory_t tng_data,
+                     const int64_t len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(len >= tng_data->long_stride_length)
+    {
+        return(TNG_FAILURE);
+    }
+    tng_data->medium_stride_length = len;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(len, "TNG library: len must not be a NULL pointer");
+
+    *len = tng_data->long_stride_length;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_set
+                (tng_trajectory_t tng_data,
+                 const int64_t len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(len <= tng_data->medium_stride_length)
+    {
+        return(TNG_FAILURE);
+    }
+    tng_data->long_stride_length = len;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_get
+                (const tng_trajectory_t tng_data,
+                 double *time)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(time, "TNG library: time must not be a NULL pointer");
+
+    *time = tng_data->time_per_frame;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_set
+                (tng_trajectory_t tng_data,
+                 const double time)
+{
+    tng_trajectory_frame_set_t frame_set;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(time >= 0, "TNG library: The time per frame must be >= 0.");
+
+    if(fabs(time - tng_data->time_per_frame) < 0.00001)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    /* If the current frame set is not finished write it to disk before
+       changing time per frame. */
+    if(tng_data->time_per_frame > 0 && frame_set->n_unwritten_frames > 0)
+    {
+        frame_set->n_frames = frame_set->n_unwritten_frames;
+        tng_frame_set_write(tng_data, TNG_USE_HASH);
+    }
+    tng_data->time_per_frame = time;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_input_file_len_get
+                    (const tng_trajectory_t tng_data,
+                     int64_t *len)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(len, "TNG library: len must not be a NULL pointer");
+
+    *len = tng_data->input_file_len;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_get
+                    (const tng_trajectory_t tng_data,
+                     int64_t *n)
+{
+    tng_gen_block_t block;
+    tng_function_status stat;
+    long file_pos;
+    int64_t last_file_pos, first_frame, n_frames;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(tng_data->input_file, "TNG library: An input file must be open to find the next frame set");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    file_pos = ftell(tng_data->input_file);
+    last_file_pos = tng_data->last_trajectory_frame_set_input_file_pos;
+
+    if(last_file_pos <= 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    tng_block_init(&block);
+    fseek(tng_data->input_file,
+          (long)last_file_pos,
+          SEEK_SET);
+    /* Read block headers first to see that a frame set block is found. */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n", last_file_pos,
+                __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_FAILURE);
+    }
+    tng_block_destroy(&block);
+
+    if(fread(&first_frame, sizeof(int64_t), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read first frame of frame set. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+    if(fread(&n_frames, sizeof(int64_t), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read n frames of frame set. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+    fseek(tng_data->input_file, file_pos, SEEK_SET);
+
+    *n = first_frame + n_frames;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_compression_precision_get
+                (const tng_trajectory_t tng_data,
+                 double *precision)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    *precision = tng_data->compression_precision;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_compression_precision_set
+                (tng_trajectory_t tng_data,
+                 const double precision)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    tng_data->compression_precision = precision;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_implicit_num_particles_set
+                (tng_trajectory_t tng_data,
+                 const int64_t n)
+{
+    tng_molecule_t mol;
+    tng_chain_t chain;
+    tng_residue_t res;
+    tng_atom_t atom;
+    tng_function_status stat;
+    int64_t diff, n_mod, n_impl;
+
+    TNG_ASSERT(n >= 0, "TNG library: The number of molecules must be >= 0");
+
+    diff = n - tng_data->n_particles;
+
+    stat = tng_molecule_find(tng_data, "TNG_IMPLICIT_MOL", -1, &mol);
+    if(stat == TNG_SUCCESS)
+    {
+        tng_molecule_cnt_get(tng_data, mol, &n_impl);
+        diff -= n_impl * mol->n_atoms;
+    }
+
+    if(diff == 0)
+    {
+        if(stat == TNG_SUCCESS)
+        {
+            stat = tng_molecule_cnt_set(tng_data, mol, 0);
+            return(stat);
+        }
+        return(TNG_SUCCESS);
+    }
+    else if(diff < 0)
+    {
+        fprintf(stderr, "TNG library: Already more actual particles than requested implicit ");
+        fprintf(stderr, "particle count.\n");
+        fprintf(stderr, "TNG library: Cannot set implicit particle count. %s: %d\n",
+                __FILE__, __LINE__);
+        /* FIXME: Should we set the count of all other molecules to 0 and add
+         * implicit molecules? */
+        return(TNG_FAILURE);
+    }
+    if(stat != TNG_SUCCESS)
+    {
+        stat = tng_molecule_add(tng_data,
+                                "TNG_IMPLICIT_MOL",
+                                &mol);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_molecule_chain_add(tng_data, mol, "", &chain);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_chain_residue_add(tng_data, chain, "", &res);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_residue_atom_add(tng_data, res, "", "", &atom);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+    }
+    else
+    {
+        if(mol->n_atoms > 1)
+        {
+            n_mod = diff % mol->n_atoms;
+            if(n_mod != 0)
+            {
+                fprintf(stderr, "TNG library: Number of atoms in implicit molecule ");
+                fprintf(stderr, "not compatible with requested implicit particle cnt.\n");
+                fprintf(stderr, "TNG library: Cannot set implicit particle count. %s: %d\n",
+                        __FILE__, __LINE__);
+                return(TNG_FAILURE);
+            }
+            diff /= mol->n_atoms;
+        }
+    }
+    stat = tng_molecule_cnt_set(tng_data, mol, diff);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    if(tng_data->var_num_atoms_flag == TNG_CONSTANT_N_ATOMS)
+    {
+        *n = tng_data->n_particles;
+    }
+    else
+    {
+        *n = tng_data->current_trajectory_frame_set.n_particles;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_variable_get
+                (const tng_trajectory_t tng_data,
+                 char *variable)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(variable, "TNG library: variable must not be a NULL pointer");
+
+    *variable = tng_data->var_num_atoms_flag;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_molecule_types_get
+                    (const tng_trajectory_t tng_data,
+                     int64_t *n)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = tng_data->n_molecules;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_molecules_get
+                    (const tng_trajectory_t tng_data,
+                     int64_t *n)
+{
+    int64_t *cnt_list = 0, cnt = 0, i;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    tng_molecule_cnt_list_get(tng_data, &cnt_list);
+
+    if(!cnt_list)
+    {
+        return(TNG_FAILURE);
+    }
+
+    for(i = tng_data->n_molecules; i --;)
+    {
+        cnt += cnt_list[i];
+    }
+
+    *n = cnt;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_list_get
+                (const tng_trajectory_t tng_data,
+                 int64_t **mol_cnt_list)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(tng_data->var_num_atoms_flag)
+    {
+        *mol_cnt_list = tng_data->current_trajectory_frame_set.
+                       molecule_cnt_list;
+    }
+    else
+    {
+        *mol_cnt_list = tng_data->molecule_cnt_list;
+    }
+    if(*mol_cnt_list == 0)
+    {
+        return(TNG_FAILURE);
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *exp)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(exp, "TNG library: exp must not be a NULL pointer");
+
+    *exp = tng_data->distance_unit_exponential;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t exp)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    tng_data->distance_unit_exponential = exp;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    *n = tng_data->frame_set_n_frames;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t n)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    tng_data->frame_set_n_frames = n;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frame_sets_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    int64_t long_stride_length, medium_stride_length;
+    int64_t file_pos, orig_frame_set_file_pos;
+    tng_trajectory_frame_set_t frame_set;
+    struct tng_trajectory_frame_set   orig_frame_set;
+    tng_gen_block_t block;
+    tng_function_status stat;
+    int64_t cnt = 0;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n, "TNG library: n must not be a NULL pointer");
+
+    orig_frame_set = tng_data->current_trajectory_frame_set;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    orig_frame_set_file_pos = tng_data->current_trajectory_frame_set_input_file_pos;
+    file_pos = tng_data->first_trajectory_frame_set_input_file_pos;
+
+    tng_block_init(&block);
+    fseek(tng_data->input_file,
+          (long)file_pos,
+          SEEK_SET);
+    tng_data->current_trajectory_frame_set_input_file_pos = (long)file_pos;
+    /* Read block headers first to see what block is found. */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n", file_pos,
+                __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(tng_block_read_next(tng_data, block,
+                        TNG_SKIP_HASH) != TNG_SUCCESS)
+    {
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    ++cnt;
+
+    long_stride_length = tng_data->long_stride_length;
+    medium_stride_length = tng_data->medium_stride_length;
+
+    /* Take long steps forward until a long step forward would be too long or
+     * the last frame set is found */
+    file_pos = frame_set->long_stride_next_frame_set_file_pos;
+    while(file_pos > 0)
+    {
+        if(file_pos > 0)
+        {
+            cnt += long_stride_length;
+            fseek(tng_data->input_file, (long)file_pos, SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        file_pos = frame_set->long_stride_next_frame_set_file_pos;
+    }
+
+    /* Take medium steps forward until a medium step forward would be too long
+     * or the last frame set is found */
+    file_pos = frame_set->medium_stride_next_frame_set_file_pos;
+    while(file_pos > 0)
+    {
+        if(file_pos > 0)
+        {
+            cnt += medium_stride_length;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        file_pos = frame_set->medium_stride_next_frame_set_file_pos;
+    }
+
+    /* Take one step forward until the last frame set is found */
+    file_pos = frame_set->next_frame_set_file_pos;
+    while(file_pos > 0)
+    {
+        if(file_pos > 0)
+        {
+            ++cnt;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        file_pos = frame_set->next_frame_set_file_pos;
+    }
+
+    tng_block_destroy(&block);
+
+    *n = tng_data->n_trajectory_frame_sets = cnt;
+
+    *frame_set = orig_frame_set;
+
+    fseek(tng_data->input_file,
+          (long)tng_data->first_trajectory_frame_set_input_file_pos,
+          SEEK_SET);
+
+    tng_data->current_trajectory_frame_set_input_file_pos = orig_frame_set_file_pos;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_current_frame_set_get
+                (tng_trajectory_t tng_data,
+                 tng_trajectory_frame_set_t *frame_set_p)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    *frame_set_p = &tng_data->current_trajectory_frame_set;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_nr_find
+                (tng_trajectory_t tng_data,
+                 const int64_t nr)
+{
+    int64_t long_stride_length, medium_stride_length;
+    int64_t file_pos, curr_nr = 0, n_frame_sets;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(nr >= 0, "The frame set number (nr) must be >= 0");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    stat = tng_num_frame_sets_get(tng_data, &n_frame_sets);
+
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    if(nr >= n_frame_sets)
+    {
+        return(TNG_FAILURE);
+    }
+
+    long_stride_length = tng_data->long_stride_length;
+    medium_stride_length = tng_data->medium_stride_length;
+
+    /* FIXME: The frame set number of the current frame set is not stored */
+
+    if(nr < n_frame_sets - 1 - nr)
+    {
+        /* Start from the beginning */
+        file_pos = tng_data->first_trajectory_frame_set_input_file_pos;
+    }
+    else
+    {
+        /* Start from the end */
+        file_pos = tng_data->last_trajectory_frame_set_input_file_pos;
+        curr_nr = n_frame_sets - 1;
+    }
+    if(file_pos <= 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    tng_block_init(&block);
+    fseek(tng_data->input_file,
+          (long)file_pos,
+          SEEK_SET);
+    tng_data->current_trajectory_frame_set_input_file_pos = (long)file_pos;
+    /* Read block headers first to see what block is found. */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n", file_pos,
+                __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(tng_block_read_next(tng_data, block,
+                        TNG_SKIP_HASH) != TNG_SUCCESS)
+    {
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(curr_nr == nr)
+    {
+        tng_block_destroy(&block);
+        return(TNG_SUCCESS);
+    }
+
+    file_pos = tng_data->current_trajectory_frame_set_input_file_pos;
+
+    /* Take long steps forward until a long step forward would be too long or
+     * the right frame set is found */
+    while(file_pos > 0 && curr_nr + long_stride_length <= nr)
+    {
+        file_pos = frame_set->long_stride_next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            curr_nr += long_stride_length;
+            fseek(tng_data->input_file, (long)file_pos, SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos,  __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    /* Take medium steps forward until a medium step forward would be too long
+     * or the right frame set is found */
+    while(file_pos > 0 && curr_nr + medium_stride_length <= nr)
+    {
+        file_pos = frame_set->medium_stride_next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            curr_nr += medium_stride_length;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    /* Take one step forward until the right frame set is found */
+    while(file_pos > 0 && curr_nr < nr)
+    {
+        file_pos = frame_set->next_frame_set_file_pos;
+
+        if(file_pos > 0)
+        {
+            ++curr_nr;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    /* Take long steps backward until a long step backward would be too long
+     * or the right frame set is found */
+    while(file_pos > 0 && curr_nr - long_stride_length >= nr)
+    {
+        file_pos = frame_set->long_stride_prev_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            curr_nr -= long_stride_length;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    /* Take medium steps backward until a medium step backward would be too long
+     * or the right frame set is found */
+    while(file_pos > 0 && curr_nr - medium_stride_length >= nr)
+    {
+        file_pos = frame_set->medium_stride_prev_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            curr_nr -= medium_stride_length;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    /* Take one step backward until the right frame set is found */
+    while(file_pos > 0 && curr_nr > nr)
+    {
+        file_pos = frame_set->prev_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            --curr_nr;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    /* If for some reason the current frame set is not yet found,
+     * take one step forward until the right frame set is found */
+    while(file_pos > 0 && curr_nr < nr)
+    {
+        file_pos = frame_set->next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            ++curr_nr;
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+            if(curr_nr == nr)
+            {
+                tng_block_destroy(&block);
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    tng_block_destroy(&block);
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_of_frame_find
+                (tng_trajectory_t tng_data,
+                 const int64_t frame)
+{
+    int64_t first_frame, last_frame, n_frames_per_frame_set;
+    int64_t long_stride_length, medium_stride_length;
+    int64_t file_pos, temp_frame, n_frames;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame >= 0, "TNG library: frame must be >= 0.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    tng_block_init(&block);
+
+    if(tng_data->current_trajectory_frame_set_input_file_pos < 0)
+    {
+        file_pos = tng_data->first_trajectory_frame_set_input_file_pos;
+        fseek(tng_data->input_file,
+                (long)file_pos,
+                SEEK_SET);
+        tng_data->current_trajectory_frame_set_input_file_pos = (long)file_pos;
+        /* Read block headers first to see what block is found. */
+        stat = tng_block_header_read(tng_data, block);
+        if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        if(tng_block_read_next(tng_data, block,
+                            TNG_SKIP_HASH) != TNG_SUCCESS)
+        {
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    first_frame = tng_max_i64(frame_set->first_frame, 0);
+    last_frame = first_frame + frame_set->n_frames - 1;
+    /* Is this the right frame set? */
+    if(first_frame <= frame && frame <= last_frame)
+    {
+        tng_block_destroy(&block);
+        return(TNG_SUCCESS);
+    }
+
+    n_frames_per_frame_set = tng_data->frame_set_n_frames;
+    long_stride_length = tng_data->long_stride_length;
+    medium_stride_length = tng_data->medium_stride_length;
+
+    if(tng_first_frame_nr_of_next_frame_set_get(tng_data, &temp_frame) ==
+       TNG_SUCCESS)
+    {
+        if(temp_frame - first_frame > n_frames_per_frame_set)
+        {
+            n_frames_per_frame_set = temp_frame - first_frame;
+        }
+    }
+
+    tng_num_frames_get(tng_data, &n_frames);
+
+    if(frame >= n_frames)
+    {
+        tng_block_destroy(&block);
+        return(TNG_FAILURE);
+    }
+
+    if(first_frame - frame >= frame ||
+       frame - last_frame >
+       tng_data->n_trajectory_frame_sets * n_frames_per_frame_set - frame)
+    {
+        /* Start from the beginning */
+        if(first_frame - frame >= frame)
+        {
+            file_pos = tng_data->first_trajectory_frame_set_input_file_pos;
+
+            if(file_pos <= 0)
+            {
+                tng_block_destroy(&block);
+                return(TNG_FAILURE);
+            }
+        }
+        /* Start from the end */
+        else if(frame - first_frame > (n_frames - 1) - frame)
+        {
+            file_pos = tng_data->last_trajectory_frame_set_input_file_pos;
+
+            /* If the last frame set position is not set start from the current
+             * frame set, since it will be closer than the first frame set. */
+        }
+        /* Start from current */
+        else
+        {
+            file_pos = tng_data->current_trajectory_frame_set_input_file_pos;
+        }
+
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            tng_data->current_trajectory_frame_set_input_file_pos = (long)file_pos;
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+    }
+
+    first_frame = tng_max_i64(frame_set->first_frame, 0);
+    last_frame = first_frame + frame_set->n_frames - 1;
+
+    if(frame >= first_frame && frame <= last_frame)
+    {
+        tng_block_destroy(&block);
+        return(TNG_SUCCESS);
+    }
+
+    file_pos = tng_data->current_trajectory_frame_set_input_file_pos;
+
+    /* Take long steps forward until a long step forward would be too long or
+     * the right frame set is found */
+    while(file_pos > 0 && first_frame + long_stride_length *
+          n_frames_per_frame_set <= frame)
+    {
+        file_pos = frame_set->long_stride_next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file, (long)file_pos, SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    /* Take medium steps forward until a medium step forward would be too long
+     * or the right frame set is found */
+    while(file_pos > 0 && first_frame + medium_stride_length *
+          n_frames_per_frame_set <= frame)
+    {
+        file_pos = frame_set->medium_stride_next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    /* Take one step forward until the right frame set is found */
+    while(file_pos > 0 && first_frame < frame && last_frame < frame)
+    {
+        file_pos = frame_set->next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    /* Take long steps backward until a long step backward would be too long
+     * or the right frame set is found */
+    while(file_pos > 0 && first_frame - long_stride_length *
+          n_frames_per_frame_set >= frame)
+    {
+        file_pos = frame_set->long_stride_prev_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    /* Take medium steps backward until a medium step backward would be too long
+     * or the right frame set is found */
+    while(file_pos > 0 && first_frame - medium_stride_length *
+          n_frames_per_frame_set >= frame)
+    {
+        file_pos = frame_set->medium_stride_prev_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    /* Take one step backward until the right frame set is found */
+    while(file_pos > 0 && first_frame > frame && last_frame > frame)
+    {
+        file_pos = frame_set->prev_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    /* If for some reason the current frame set is not yet found,
+     * take one step forward until the right frame set is found */
+    while(file_pos > 0 && first_frame < frame && last_frame < frame)
+    {
+        file_pos = frame_set->next_frame_set_file_pos;
+        if(file_pos > 0)
+        {
+            fseek(tng_data->input_file,
+                  (long)file_pos,
+                  SEEK_SET);
+            /* Read block headers first to see what block is found. */
+            stat = tng_block_header_read(tng_data, block);
+            if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                       file_pos, __FILE__, __LINE__);
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_block_read_next(tng_data, block,
+                                TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+        }
+        first_frame = tng_max_i64(frame_set->first_frame, 0);
+        last_frame = first_frame + frame_set->n_frames - 1;
+        if(frame >= first_frame && frame <= last_frame)
+        {
+            tng_block_destroy(&block);
+            return(TNG_SUCCESS);
+        }
+    }
+
+    tng_block_destroy(&block);
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_next_frame_set_file_pos_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos)
+{
+    (void)tng_data;
+
+    TNG_ASSERT(frame_set, "TNG library: frame_set not initialised before accessing data.");
+    TNG_ASSERT(pos, "TNG library: pos must not be a NULL pointer");
+
+    *pos = frame_set->next_frame_set_file_pos;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_prev_frame_set_file_pos_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos)
+{
+    (void)tng_data;
+
+    TNG_ASSERT(frame_set, "TNG library: frame_set not initialised before accessing data.");
+    TNG_ASSERT(pos, "TNG library: pos must not be a NULL pointer");
+
+    *pos = frame_set->prev_frame_set_file_pos;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_frame_range_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *first_frame,
+                 int64_t *last_frame)
+{
+    (void)tng_data;
+
+    TNG_ASSERT(first_frame, "TNG library: first_frame must not be a NULL pointer");
+    TNG_ASSERT(last_frame, "TNG library: last_frame must not be a NULL pointer");
+    TNG_ASSERT(frame_set, "TNG library: frame_set must not be a NULL pointer");
+
+    *first_frame = frame_set->first_frame;
+    *last_frame = *first_frame + frame_set->n_frames - 1;
+
+    return(TNG_SUCCESS);
+}
+
+/** Translate from the particle numbering used in a frame set to the real
+ *  particle numbering - used in the molecule description.
+ * @param frame_set is the frame_set containing the mappings to use.
+ * @param local is the index number of the atom in this frame set
+ * @param real is set to the index of the atom in the molecular system.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the mapping
+ * cannot be found.
+ */
+static TNG_INLINE tng_function_status tng_particle_mapping_get_real_particle
+                (const tng_trajectory_frame_set_t frame_set,
+                 const int64_t local,
+                 int64_t *real)
+{
+    int64_t i, n_blocks = frame_set->n_mapping_blocks, first;
+    tng_particle_mapping_t mapping;
+    if(n_blocks <= 0)
+    {
+        *real = local;
+        return(TNG_SUCCESS);
+    }
+    for(i = 0; i < n_blocks; i++)
+    {
+        mapping = &frame_set->mappings[i];
+        first = mapping->num_first_particle;
+        if(local < first ||
+           local >= first + mapping->n_particles)
+        {
+            continue;
+        }
+        *real = mapping->real_particle_numbers[local-first];
+        return(TNG_SUCCESS);
+    }
+    *real = local;
+    return(TNG_FAILURE);
+}
+
+/** Translate from the real particle numbering to the particle numbering
+ *  used in a frame set.
+ * @param frame_set is the frame_set containing the mappings to use.
+ * @param real is the index number of the atom in the molecular system.
+ * @param local is set to the index of the atom in this frame set.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the mapping
+ * cannot be found.
+ */
+/*static TNG_INLINE tng_function_status tng_particle_mapping_get_local_particle
+                (const tng_trajectory_frame_set_t frame_set,
+                 const int64_t real,
+                 int64_t *local)
+{
+    int64_t i, j, n_blocks = frame_set->n_mapping_blocks;
+    tng_particle_mapping_t mapping;
+    if(n_blocks <= 0)
+    {
+        *local = real;
+        return(TNG_SUCCESS);
+    }
+    for(i = 0; i < n_blocks; i++)
+    {
+        mapping = &frame_set->mappings[i];
+        for(j = mapping->n_particles; j--;)
+        {
+            if(mapping->real_particle_numbers[j] == real)
+            {
+                *local = j;
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+    return(TNG_FAILURE);
+}
+*/
+
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_read
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    int cnt = 0, prev_pos = 0;
+    tng_gen_block_t block;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    tng_data->n_trajectory_frame_sets = 0;
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    if(!tng_data->input_file_len)
+    {
+        fseek(tng_data->input_file, 0, SEEK_END);
+        tng_data->input_file_len = ftell(tng_data->input_file);
+        fseek(tng_data->input_file, 0, SEEK_SET);
+    }
+
+    tng_block_init(&block);
+    /* Non trajectory blocks (they come before the trajectory
+     * blocks in the file) */
+    while (prev_pos < tng_data->input_file_len &&
+           tng_block_header_read(tng_data, block) != TNG_CRITICAL &&
+           block->id != -1 &&
+           block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        if(tng_block_read_next(tng_data, block,
+                               hash_mode) == TNG_SUCCESS)
+        {
+            cnt++;
+        }
+        prev_pos = ftell(tng_data->input_file);
+    }
+
+    /* Go back if a trajectory block was encountered */
+    if(block->id == TNG_TRAJECTORY_FRAME_SET)
+    {
+        fseek(tng_data->input_file, prev_pos, SEEK_SET);
+    }
+
+    tng_block_destroy(&block);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    int i;
+    tng_gen_block_t data_block;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    /* TODO: If there is already frame set data written to this file (e.g. when
+     * appending to an already existing file we might need to move frame sets to
+     * the end of the file. */
+
+    if(tng_general_info_block_write(tng_data, hash_mode)
+       != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Error writing general info block of file %s. %s: %d\n",
+                tng_data->input_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    if(tng_molecules_block_write(tng_data, hash_mode)
+        != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Error writing atom names block of file %s. %s: %d\n",
+                tng_data->input_file_path, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    /* FIXME: Currently writing non-trajectory data blocks here.
+     * Should perhaps be moved. */
+    tng_block_init(&data_block);
+    for(i = 0; i < tng_data->n_data_blocks; i++)
+    {
+        data_block->id = tng_data->non_tr_data[i].block_id;
+        tng_data_block_write(tng_data, data_block,
+                             i, hash_mode);
+    }
+
+    for(i = 0; i < tng_data->n_particle_data_blocks; i++)
+    {
+        data_block->id = tng_data->non_tr_particle_data[i].block_id;
+        tng_particle_data_block_write(tng_data, data_block,
+                                      i, 0, hash_mode);
+    }
+
+    tng_block_destroy(&data_block);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_block_read_next(tng_trajectory_t tng_data,
+                                        tng_gen_block_t block,
+                                        const char hash_mode)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(block, "TNG library: block must be initialised and must not be a NULL pointer.");
+
+    switch(block->id)
+    {
+    case TNG_TRAJECTORY_FRAME_SET:
+        return(tng_frame_set_block_read(tng_data, block, hash_mode));
+    case TNG_PARTICLE_MAPPING:
+        return(tng_trajectory_mapping_block_read(tng_data, block, hash_mode));
+    case TNG_GENERAL_INFO:
+        return(tng_general_info_block_read(tng_data, block, hash_mode));
+    case TNG_MOLECULES:
+        return(tng_molecules_block_read(tng_data, block, hash_mode));
+    default:
+        if(block->id >= TNG_TRAJ_BOX_SHAPE)
+        {
+            return(tng_data_block_contents_read(tng_data, block, hash_mode));
+        }
+        else
+        {
+            /* Skip to the next block */
+            fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+            return(TNG_FAILURE);
+        }
+    }
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    long file_pos;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    file_pos = ftell(tng_data->input_file);
+
+    tng_block_init(&block);
+
+    if(!tng_data->input_file_len)
+    {
+        fseek(tng_data->input_file, 0, SEEK_END);
+        tng_data->input_file_len = ftell(tng_data->input_file);
+        fseek(tng_data->input_file, file_pos, SEEK_SET);
+    }
+
+    /* Read block headers first to see what block is found. */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    tng_data->current_trajectory_frame_set_input_file_pos = file_pos;
+
+    if(tng_block_read_next(tng_data, block,
+                           hash_mode) == TNG_SUCCESS)
+    {
+        tng_data->n_trajectory_frame_sets++;
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+              stat != TNG_CRITICAL &&
+              block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            stat = tng_block_read_next(tng_data, block,
+                                       hash_mode);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+                   file_pos, __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(stat);
+        }
+
+        if(block->id == TNG_TRAJECTORY_FRAME_SET)
+        {
+            fseek(tng_data->input_file, file_pos, SEEK_SET);
+        }
+    }
+
+    tng_block_destroy(&block);
+
+    return(TNG_SUCCESS);
+}
+
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_current_only_data_from_block_id
+                (tng_trajectory_t tng_data,
+                 const char hash_mode,
+                 const int64_t block_id)
+{
+    long file_pos;
+    tng_gen_block_t block;
+    tng_function_status stat;
+    int found_flag = 1;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    file_pos = (long)tng_data->current_trajectory_frame_set_input_file_pos;
+
+    if(file_pos < 0)
+    {
+        /* No current frame set. This means that the first frame set must be
+         * read */
+        found_flag = 0;
+        file_pos = (long)tng_data->first_trajectory_frame_set_input_file_pos;
+    }
+
+    if(file_pos > 0)
+    {
+        fseek(tng_data->input_file,
+              file_pos,
+              SEEK_SET);
+    }
+    else
+    {
+        return(TNG_FAILURE);
+    }
+
+    tng_block_init(&block);
+
+    if(!tng_data->input_file_len)
+    {
+        fseek(tng_data->input_file, 0, SEEK_END);
+        tng_data->input_file_len = ftell(tng_data->input_file);
+        fseek(tng_data->input_file, file_pos, SEEK_SET);
+    }
+
+    /* Read block headers first to see what block is found. */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    /* If the current frame set had already been read skip its block contents */
+    if(found_flag)
+    {
+        fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+    }
+    /* Otherwiese read the frame set block */
+    else
+    {
+        stat = tng_block_read_next(tng_data, block,
+                                   hash_mode);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read frame set block. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(stat);
+        }
+    }
+    file_pos = ftell(tng_data->input_file);
+
+    found_flag = 0;
+
+    /* Read only blocks of the requested ID
+        * until next frame set block */
+    stat = tng_block_header_read(tng_data, block);
+    while(file_pos < tng_data->input_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        if(block->id == block_id)
+        {
+            stat = tng_block_read_next(tng_data, block,
+                                       hash_mode);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                found_flag = 1;
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        else
+        {
+            file_pos += (long)(block->block_contents_size + block->header_contents_size);
+            fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+            if(file_pos < tng_data->input_file_len)
+            {
+                stat = tng_block_header_read(tng_data, block);
+            }
+        }
+    }
+    if(stat == TNG_CRITICAL)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+                file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(stat);
+    }
+
+    if(block->id == TNG_TRAJECTORY_FRAME_SET)
+    {
+        fseek(tng_data->input_file, file_pos, SEEK_SET);
+    }
+
+    tng_block_destroy(&block);
+
+    if(found_flag)
+    {
+        return(TNG_SUCCESS);
+    }
+    else
+    {
+        return(TNG_FAILURE);
+    }
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    long file_pos;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    file_pos = (long)tng_data->current_trajectory_frame_set.next_frame_set_file_pos;
+
+    if(file_pos < 0 && tng_data->current_trajectory_frame_set_input_file_pos <= 0)
+    {
+        file_pos = (long)tng_data->first_trajectory_frame_set_input_file_pos;
+    }
+
+    if(file_pos > 0)
+    {
+        fseek(tng_data->input_file,
+              file_pos,
+              SEEK_SET);
+    }
+    else
+    {
+        return(TNG_FAILURE);
+    }
+
+    return(tng_frame_set_read(tng_data, hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next_only_data_from_block_id
+                (tng_trajectory_t tng_data,
+                 const char hash_mode,
+                 const int64_t block_id)
+{
+    long file_pos;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    if(tng_input_file_init(tng_data) != TNG_SUCCESS)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    file_pos = (long)tng_data->current_trajectory_frame_set.next_frame_set_file_pos;
+
+    if(file_pos < 0 && tng_data->current_trajectory_frame_set_input_file_pos <= 0)
+    {
+        file_pos = (long)tng_data->first_trajectory_frame_set_input_file_pos;
+    }
+
+    if(file_pos > 0)
+    {
+        fseek(tng_data->input_file,
+              file_pos,
+              SEEK_SET);
+    }
+    else
+    {
+        return(TNG_FAILURE);
+    }
+
+    tng_block_init(&block);
+
+    if(!tng_data->input_file_len)
+    {
+        fseek(tng_data->input_file, 0, SEEK_END);
+        tng_data->input_file_len = ftell(tng_data->input_file);
+        fseek(tng_data->input_file, file_pos, SEEK_SET);
+    }
+
+    /* Read block headers first to see what block is found. */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    tng_data->current_trajectory_frame_set_input_file_pos = file_pos;
+
+    if(tng_block_read_next(tng_data, block,
+                           hash_mode) == TNG_SUCCESS)
+    {
+        stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, hash_mode, block_id);
+    }
+
+    tng_block_destroy(&block);
+
+    return(stat);
+}
+
+tng_function_status tng_frame_set_write(tng_trajectory_t tng_data,
+                                        const char hash_mode)
+{
+    int i, j;
+    tng_gen_block_t block;
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(frame_set->n_written_frames == frame_set->n_frames)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    tng_data->current_trajectory_frame_set_output_file_pos =
+    ftell(tng_data->output_file);
+    tng_data->last_trajectory_frame_set_output_file_pos =
+    tng_data->current_trajectory_frame_set_output_file_pos;
+
+    if(tng_data->current_trajectory_frame_set_output_file_pos <= 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(tng_data->first_trajectory_frame_set_output_file_pos == -1)
+    {
+        tng_data->first_trajectory_frame_set_output_file_pos =
+        tng_data->current_trajectory_frame_set_output_file_pos;
+    }
+
+    tng_block_init(&block);
+
+    if(tng_frame_set_block_write(tng_data, block, hash_mode) != TNG_SUCCESS)
+    {
+        tng_block_destroy(&block);
+        return(TNG_FAILURE);
+    }
+
+    /* Write non-particle data blocks */
+    for(i = 0; i<frame_set->n_data_blocks; i++)
+    {
+        block->id = frame_set->tr_data[i].block_id;
+        tng_data_block_write(tng_data, block, i, hash_mode);
+    }
+    /* Write the mapping blocks and particle data blocks*/
+    if(frame_set->n_mapping_blocks)
+    {
+        for(i = 0; i < frame_set->n_mapping_blocks; i++)
+        {
+            block->id = TNG_PARTICLE_MAPPING;
+            if(frame_set->mappings[i].n_particles > 0)
+            {
+                tng_trajectory_mapping_block_write(tng_data, block, i, hash_mode);
+                for(j = 0; j<frame_set->n_particle_data_blocks; j++)
+                {
+                    block->id = frame_set->tr_particle_data[j].block_id;
+                    tng_particle_data_block_write(tng_data, block,
+                                                  j, &frame_set->mappings[i],
+                                                  hash_mode);
+                }
+            }
+        }
+    }
+    else
+    {
+        for(i = 0; i<frame_set->n_particle_data_blocks; i++)
+        {
+            block->id = frame_set->tr_particle_data[i].block_id;
+            tng_particle_data_block_write(tng_data, block,
+                                          i, 0, hash_mode);
+        }
+    }
+
+
+    /* Update pointers in the general info block */
+    stat = tng_header_pointers_update(tng_data, hash_mode);
+
+    if(stat == TNG_SUCCESS)
+    {
+        stat = tng_frame_set_pointers_update(tng_data, hash_mode);
+    }
+
+    tng_block_destroy(&block);
+
+    frame_set->n_unwritten_frames = 0;
+
+    fflush(tng_data->output_file);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_premature_write
+                (tng_trajectory_t tng_data,
+                 const char hash_mode)
+{
+    tng_trajectory_frame_set_t frame_set;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(frame_set->n_unwritten_frames == 0)
+    {
+        return(TNG_SUCCESS);
+    }
+    frame_set->n_frames = frame_set->n_unwritten_frames;
+
+    return(tng_frame_set_write(tng_data, hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_new
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t n_frames)
+{
+    tng_gen_block_t block;
+    tng_trajectory_frame_set_t frame_set;
+    FILE *temp = tng_data->input_file;
+    int64_t curr_pos;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(first_frame >= 0, "TNG library: first_frame must be >= 0.");
+    TNG_ASSERT(n_frames >= 0, "TNG library: n_frames must be >= 0.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    curr_pos = ftell(tng_data->output_file);
+
+    if(curr_pos <= 10)
+    {
+        tng_file_headers_write(tng_data, TNG_USE_HASH);
+    }
+
+    /* Set pointer to previous frame set to the one that was loaded
+     * before.
+     * FIXME: This is a bit risky. If they are not added in order
+     * it will be wrong. */
+    if(tng_data->n_trajectory_frame_sets)
+    {
+        frame_set->prev_frame_set_file_pos =
+        tng_data->current_trajectory_frame_set_output_file_pos;
+    }
+
+    tng_data->current_trajectory_frame_set_output_file_pos =
+    ftell(tng_data->output_file);
+
+    tng_data->n_trajectory_frame_sets++;
+
+    /* Set the medium range pointers */
+    if(tng_data->n_trajectory_frame_sets == tng_data->medium_stride_length + 1)
+    {
+        frame_set->medium_stride_prev_frame_set_file_pos =
+        tng_data->first_trajectory_frame_set_output_file_pos;
+    }
+    else if(tng_data->n_trajectory_frame_sets > tng_data->medium_stride_length + 1)
+    {
+        /* FIXME: Currently only working if the previous frame set has its
+         * medium stride pointer already set. This might need some fixing. */
+        if(frame_set->medium_stride_prev_frame_set_file_pos != -1 &&
+           frame_set->medium_stride_prev_frame_set_file_pos != 0)
+        {
+            tng_block_init(&block);
+            tng_data->input_file = tng_data->output_file;
+
+            curr_pos = ftell(tng_data->output_file);
+            fseek(tng_data->output_file,
+                  (long)frame_set->medium_stride_prev_frame_set_file_pos,
+                  SEEK_SET);
+
+            if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot read frame set header. %s: %d\n",
+                    __FILE__, __LINE__);
+                tng_data->input_file = temp;
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            /* Read the next frame set from the previous frame set and one
+             * medium stride step back */
+            fseek(tng_data->output_file, (long)block->block_contents_size - 6 *
+                sizeof(int64_t), SEEK_CUR);
+            if(fread(&frame_set->medium_stride_prev_frame_set_file_pos,
+               sizeof(frame_set->medium_stride_prev_frame_set_file_pos),
+               1, tng_data->output_file) == 0)
+            {
+                fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+                tng_data->input_file = temp;
+                tng_block_destroy(&block);
+                return(TNG_CRITICAL);
+            }
+
+            if(tng_data->input_endianness_swap_func_64)
+            {
+                if(tng_data->input_endianness_swap_func_64(tng_data,
+                   &frame_set->medium_stride_prev_frame_set_file_pos)
+                    != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                            __FILE__, __LINE__);
+                }
+            }
+
+            tng_block_destroy(&block);
+
+            /* Set the long range pointers */
+            if(tng_data->n_trajectory_frame_sets == tng_data->long_stride_length + 1)
+            {
+                frame_set->long_stride_prev_frame_set_file_pos =
+                tng_data->first_trajectory_frame_set_output_file_pos;
+            }
+            else if(tng_data->n_trajectory_frame_sets > tng_data->medium_stride_length + 1)
+            {
+                /* FIXME: Currently only working if the previous frame set has its
+                * long stride pointer already set. This might need some fixing. */
+                if(frame_set->long_stride_prev_frame_set_file_pos != -1 &&
+                frame_set->long_stride_prev_frame_set_file_pos != 0)
+                {
+                    tng_block_init(&block);
+                    tng_data->input_file = tng_data->output_file;
+
+                    fseek(tng_data->output_file,
+                          (long)frame_set->long_stride_prev_frame_set_file_pos,
+                          SEEK_SET);
+
+                    if(tng_block_header_read(tng_data, block) != TNG_SUCCESS)
+                    {
+                        fprintf(stderr, "TNG library: Cannot read frame set header. %s: %d\n",
+                            __FILE__, __LINE__);
+                        tng_data->input_file = temp;
+                        tng_block_destroy(&block);
+                        return(TNG_CRITICAL);
+                    }
+
+                    /* Read the next frame set from the previous frame set and one
+                    * long stride step back */
+                    fseek(tng_data->output_file, (long)block->block_contents_size - 6 *
+                          sizeof(int64_t), SEEK_CUR);
+
+                    tng_block_destroy(&block);
+
+                    if(fread(&frame_set->long_stride_prev_frame_set_file_pos,
+                    sizeof(frame_set->long_stride_prev_frame_set_file_pos),
+                    1, tng_data->output_file) == 0)
+                    {
+                        fprintf(stderr, "TNG library: Cannot read block. %s: %d\n", __FILE__, __LINE__);
+                        tng_data->input_file = temp;
+                        return(TNG_CRITICAL);
+                    }
+
+                    if(tng_data->input_endianness_swap_func_64)
+                    {
+                        if(tng_data->input_endianness_swap_func_64(tng_data,
+                           &frame_set->long_stride_prev_frame_set_file_pos)
+                            != TNG_SUCCESS)
+                        {
+                            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                                    __FILE__, __LINE__);
+                        }
+                    }
+
+                }
+            }
+
+            tng_data->input_file = temp;
+            fseek(tng_data->output_file, (long)curr_pos, SEEK_SET);
+        }
+    }
+
+    frame_set->first_frame = first_frame;
+    frame_set->n_frames = n_frames;
+    frame_set->n_written_frames = 0;
+    frame_set->n_unwritten_frames = 0;
+    frame_set->first_frame_time = -1;
+
+    if(tng_data->first_trajectory_frame_set_output_file_pos == -1 ||
+       tng_data->first_trajectory_frame_set_output_file_pos == 0)
+    {
+        tng_data->first_trajectory_frame_set_output_file_pos =
+        tng_data->current_trajectory_frame_set_output_file_pos;
+    }
+    /* FIXME: Should check the frame number instead of the file_pos,
+     * in case frame sets are not in order */
+    if(tng_data->last_trajectory_frame_set_output_file_pos == -1 ||
+       tng_data->last_trajectory_frame_set_output_file_pos == 0 ||
+       tng_data->last_trajectory_frame_set_output_file_pos <
+       tng_data->current_trajectory_frame_set_output_file_pos)
+    {
+        tng_data->last_trajectory_frame_set_output_file_pos =
+        tng_data->current_trajectory_frame_set_output_file_pos;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_with_time_new
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t n_frames,
+                 const double first_frame_time)
+{
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(first_frame >= 0, "TNG library: first_frame must be >= 0.");
+    TNG_ASSERT(n_frames >= 0, "TNG library: n_frames must be >= 0.");
+    TNG_ASSERT(first_frame_time >= 0, "TNG library: first_frame_time must be >= 0.");
+
+
+    stat = tng_frame_set_new(tng_data, first_frame, n_frames);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+    stat = tng_frame_set_first_frame_time_set(tng_data, first_frame_time);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_first_frame_time_set
+                (tng_trajectory_t tng_data,
+                 const double first_frame_time)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(first_frame_time >= 0, "TNG library: first_frame_time must be >= 0.");
+
+    tng_data->current_trajectory_frame_set.first_frame_time = first_frame_time;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_frame_nr_of_next_frame_set_get
+                (tng_trajectory_t tng_data,
+                 int64_t *frame)
+{
+    long file_pos, next_frame_set_file_pos;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    tng_trajectory_frame_set_t frame_set;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(tng_data->input_file, "TNG library: An input file must be open to find the next frame set");
+    TNG_ASSERT(frame, "TNG library: frame must not be a NULL pointer");
+
+    file_pos = ftell(tng_data->input_file);
+
+    if(tng_data->current_trajectory_frame_set_input_file_pos <= 0)
+    {
+        next_frame_set_file_pos = (long)tng_data->first_trajectory_frame_set_input_file_pos;
+    }
+    else
+    {
+        frame_set = &tng_data->current_trajectory_frame_set;
+        next_frame_set_file_pos = (long)frame_set->next_frame_set_file_pos;
+    }
+
+    if(next_frame_set_file_pos <= 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    fseek(tng_data->input_file, (long)next_frame_set_file_pos, SEEK_SET);
+    /* Read block headers first to see that a frame set block is found. */
+    tng_block_init(&block);
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL || block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+/*    if(tng_data->current_trajectory_frame_set_input_file_pos <= 0)
+    {
+        tng_block_read_next(tng_data, block, TNG_USE_HASH);
+    }*/
+    tng_block_destroy(&block);
+
+    if(fread(frame, sizeof(int64_t), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Cannot read first frame of next frame set. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+    fseek(tng_data->input_file, file_pos, SEEK_SET);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_block_add
+                (tng_trajectory_t tng_data,
+                 const int64_t id,
+                 const char *block_name,
+                 const char datatype,
+                 const char block_type_flag,
+                 int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 int64_t stride_length,
+                 const int64_t codec_id,
+                 void *new_data)
+{
+    int i, j, size, len;
+    tng_trajectory_frame_set_t frame_set;
+    tng_non_particle_data_t data;
+    char **first_dim_values;
+    char *new_data_c=new_data;
+    int64_t n_frames_div;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(block_name, "TNG library: block_name must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame > 0, "TNG library: n_values_per_frame must be a positive integer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(stride_length <= 0)
+    {
+        stride_length = 1;
+    }
+
+    /* If the block does not exist, create it */
+    if(tng_data_find(tng_data, id, &data) != TNG_SUCCESS)
+    {
+        if(tng_data_block_create(tng_data, block_type_flag) !=
+            TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create data block. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            data = &frame_set->tr_data[frame_set->n_data_blocks - 1];
+        }
+        else
+        {
+            data = &tng_data->non_tr_data[tng_data->n_data_blocks - 1];
+        }
+        data->block_id = id;
+
+        data->block_name = malloc(strlen(block_name) + 1);
+        if(!data->block_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                   (int)strlen(block_name)+1, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        strncpy(data->block_name, block_name, strlen(block_name) + 1);
+
+        data->values = 0;
+        /* FIXME: Memory leak from strings. */
+        data->strings = 0;
+        data->last_retrieved_frame = -1;
+    }
+
+    data->datatype = datatype;
+    data->stride_length = tng_max_i64(stride_length, 1);
+    data->n_values_per_frame = n_values_per_frame;
+    data->n_frames = n_frames;
+    data->codec_id = codec_id;
+    data->compression_multiplier = 1.0;
+    /* FIXME: This can cause problems. */
+    data->first_frame_with_data = frame_set->first_frame;
+
+    switch(datatype)
+    {
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+        break;
+    }
+
+    if(new_data_c)
+    {
+        /* Allocate memory */
+        if(tng_allocate_data_mem(tng_data, data, n_frames, stride_length,
+                                 n_values_per_frame) !=
+        TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate data memory. %s: %d\n",
+                __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+
+        if(n_frames > frame_set->n_unwritten_frames)
+        {
+            frame_set->n_unwritten_frames = n_frames;
+        }
+
+        n_frames_div = (n_frames % stride_length) ?
+                     n_frames / stride_length + 1:
+                     n_frames / stride_length;
+
+        if(datatype == TNG_CHAR_DATA)
+        {
+            for(i = 0; i < n_frames_div; i++)
+            {
+                first_dim_values = data->strings[i];
+                for(j = 0; j < n_values_per_frame; j++)
+                {
+                    len = tng_min_i((int)strlen(new_data_c) + 1,
+                                TNG_MAX_STR_LEN);
+                    if(first_dim_values[j])
+                    {
+                        free(first_dim_values[j]);
+                    }
+                    first_dim_values[j] = malloc(len);
+                    if(!first_dim_values[j])
+                    {
+                        fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                            len, __FILE__, __LINE__);
+                        return(TNG_CRITICAL);
+                    }
+                    strncpy(first_dim_values[j],
+                            new_data_c, len);
+                    new_data_c += len;
+                }
+            }
+        }
+        else
+        {
+            memcpy(data->values, new_data, size * n_frames_div *
+                   n_values_per_frame);
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_block_add
+                (tng_trajectory_t tng_data,
+                 const int64_t id,
+                 const char *block_name,
+                 const char datatype,
+                 const char block_type_flag,
+                 int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 int64_t stride_length,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t codec_id,
+                 void *new_data)
+{
+    int i, size, len;
+    int64_t j, k;
+    int64_t tot_n_particles, n_frames_div;
+    char ***first_dim_values, **second_dim_values;
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t data;
+    char *new_data_c=new_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(block_name, "TNG library: block_name mustnot be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame > 0, "TNG library: n_values_per_frame must be a positive integer.");
+    TNG_ASSERT(num_first_particle >= 0, "TNG library: num_first_particle must be >= 0.");
+    TNG_ASSERT(n_particles >= 0, "TNG library: n_particles must be >= 0.");
+
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(stride_length <= 0)
+    {
+        stride_length = 1;
+    }
+
+    /* If the block does not exist, create it */
+    if(tng_particle_data_find(tng_data, id, &data) != TNG_SUCCESS)
+    {
+        if(tng_particle_data_block_create(tng_data, block_type_flag) !=
+            TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create particle data block. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            data = &frame_set->tr_particle_data[frame_set->
+                                                n_particle_data_blocks - 1];
+        }
+        else
+        {
+            data = &tng_data->non_tr_particle_data[tng_data->
+                                                   n_particle_data_blocks - 1];
+        }
+        data->block_id = id;
+
+        data->block_name = malloc(strlen(block_name) + 1);
+        if(!data->block_name)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                   (int)strlen(block_name)+1, __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+        strncpy(data->block_name, block_name, strlen(block_name) + 1);
+
+        data->datatype = datatype;
+
+        data->values = 0;
+        /* FIXME: Memory leak from strings. */
+        data->strings = 0;
+        data->last_retrieved_frame = -1;
+    }
+
+    data->stride_length = tng_max_i64(stride_length, 1);
+    data->n_values_per_frame = n_values_per_frame;
+    data->n_frames = n_frames;
+    data->codec_id = codec_id;
+    data->compression_multiplier = 1.0;
+    /* FIXME: This can cause problems. */
+    data->first_frame_with_data = frame_set->first_frame;
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK && tng_data->var_num_atoms_flag)
+    {
+        tot_n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        tot_n_particles = tng_data->n_particles;
+    }
+
+    /* If data values are supplied add that data to the data block. */
+    if(new_data_c)
+    {
+        /* Allocate memory */
+        if(tng_allocate_particle_data_mem(tng_data, data, n_frames,
+                                          stride_length, tot_n_particles,
+                                          n_values_per_frame) !=
+        TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate particle data memory. %s: %d\n",
+                __FILE__, __LINE__);
+            return(TNG_CRITICAL);
+        }
+
+        if(n_frames > frame_set->n_unwritten_frames)
+        {
+            frame_set->n_unwritten_frames = n_frames;
+        }
+
+        n_frames_div = (n_frames % stride_length) ?
+                     n_frames / stride_length + 1:
+                     n_frames / stride_length;
+
+        if(datatype == TNG_CHAR_DATA)
+        {
+            for(i = 0; i < n_frames_div; i++)
+            {
+                first_dim_values = data->strings[i];
+                for(j = num_first_particle; j < num_first_particle + n_particles;
+                    j++)
+                {
+                    second_dim_values = first_dim_values[j];
+                    for(k = 0; k < n_values_per_frame; k++)
+                    {
+                        len = tng_min_i((int)strlen(new_data_c) + 1,
+                                TNG_MAX_STR_LEN);
+                        if(second_dim_values[k])
+                        {
+                            free(second_dim_values[k]);
+                        }
+                        second_dim_values[k] = malloc(len);
+                        if(!second_dim_values[k])
+                        {
+                            fprintf(stderr, "TNG library: Cannot allocate memory (%d bytes). %s: %d\n",
+                                len, __FILE__, __LINE__);
+                            return(TNG_CRITICAL);
+                        }
+                        strncpy(second_dim_values[k],
+                                new_data_c, len);
+                        new_data_c += len;
+                    }
+                }
+            }
+        }
+        else
+        {
+            switch(datatype)
+            {
+            case TNG_INT_DATA:
+                size = sizeof(int64_t);
+                break;
+            case TNG_FLOAT_DATA:
+                size = sizeof(float);
+                break;
+            case TNG_DOUBLE_DATA:
+            default:
+                size = sizeof(double);
+            }
+
+            memcpy(data->values, new_data, size * n_frames_div *
+                   n_particles * n_values_per_frame);
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_block_name_get
+                (tng_trajectory_t tng_data,
+                 int64_t block_id,
+                 char *name,
+                 int max_len)
+{
+    int64_t i;
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    int block_type = -1;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer.");
+
+    for(i = 0; i < tng_data->n_particle_data_blocks; i++)
+    {
+        p_data = &tng_data->non_tr_particle_data[i];
+        if(p_data->block_id == block_id)
+        {
+            strncpy(name, p_data->block_name, max_len);
+            name[max_len - 1] = '\0';
+            return(TNG_SUCCESS);
+        }
+    }
+    for(i = 0; i < tng_data->n_data_blocks; i++)
+    {
+        np_data = &tng_data->non_tr_data[i];
+        if(np_data->block_id == block_id)
+        {
+            strncpy(name, np_data->block_name, max_len);
+            name[max_len - 1] = '\0';
+            return(TNG_SUCCESS);
+        }
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    stat = tng_particle_data_find(tng_data, block_id, &p_data);
+    if(stat == TNG_SUCCESS)
+    {
+        block_type = TNG_PARTICLE_BLOCK_DATA;
+    }
+    else
+    {
+        stat = tng_data_find(tng_data, block_id, &np_data);
+        if(stat == TNG_SUCCESS)
+        {
+            block_type = TNG_NON_PARTICLE_BLOCK_DATA;
+        }
+        else
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+            stat = tng_particle_data_find(tng_data, block_id, &p_data);
+            if(stat == TNG_SUCCESS)
+            {
+                block_type = TNG_PARTICLE_BLOCK_DATA;
+            }
+            else
+            {
+                stat = tng_data_find(tng_data, block_id, &np_data);
+                if(stat == TNG_SUCCESS)
+                {
+                    block_type = TNG_NON_PARTICLE_BLOCK_DATA;
+                }
+            }
+        }
+    }
+    if(block_type == TNG_PARTICLE_BLOCK_DATA)
+    {
+        for(i = 0; i < frame_set->n_particle_data_blocks; i++)
+        {
+            p_data = &frame_set->tr_particle_data[i];
+            if(p_data->block_id == block_id)
+            {
+                strncpy(name, p_data->block_name, max_len);
+                name[max_len - 1] = '\0';
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+    else if(block_type == TNG_NON_PARTICLE_BLOCK_DATA)
+    {
+        for(i = 0; i < frame_set->n_data_blocks; i++)
+        {
+            np_data = &frame_set->tr_data[i];
+            if(np_data->block_id == block_id)
+            {
+                strncpy(name, np_data->block_name, max_len);
+                name[max_len - 1] = '\0';
+                return(TNG_SUCCESS);
+            }
+        }
+    }
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_block_dependency_get
+                (tng_trajectory_t tng_data,
+                 int64_t block_id,
+                 int *block_dependency)
+{
+    int64_t i;
+    tng_function_status stat;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(block_dependency, "TNG library: block_dependency must not be a NULL pointer.");
+
+    for(i = 0; i < tng_data->n_particle_data_blocks; i++)
+    {
+        p_data = &tng_data->non_tr_particle_data[i];
+        if(p_data->block_id == block_id)
+        {
+            *block_dependency = TNG_PARTICLE_DEPENDENT;
+            return(TNG_SUCCESS);
+        }
+    }
+    for(i = 0; i < tng_data->n_data_blocks; i++)
+    {
+        np_data = &tng_data->non_tr_data[i];
+        if(np_data->block_id == block_id)
+        {
+            *block_dependency = 0;
+            return(TNG_SUCCESS);
+        }
+    }
+
+    stat = tng_particle_data_find(tng_data, block_id, &p_data);
+    if(stat == TNG_SUCCESS)
+    {
+        *block_dependency = TNG_PARTICLE_DEPENDENT + TNG_FRAME_DEPENDENT;
+        return(TNG_SUCCESS);
+    }
+    else
+    {
+        stat = tng_data_find(tng_data, block_id, &np_data);
+        if(stat == TNG_SUCCESS)
+        {
+            *block_dependency = TNG_FRAME_DEPENDENT;
+            return(TNG_SUCCESS);
+        }
+        else
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+            stat = tng_particle_data_find(tng_data, block_id, &p_data);
+            if(stat == TNG_SUCCESS)
+            {
+                *block_dependency = TNG_PARTICLE_DEPENDENT + TNG_FRAME_DEPENDENT;
+                return(TNG_SUCCESS);
+            }
+            else
+            {
+                stat = tng_data_find(tng_data, block_id, &np_data);
+                if(stat == TNG_SUCCESS)
+                {
+                    *block_dependency = TNG_FRAME_DEPENDENT;
+                    return(TNG_SUCCESS);
+                }
+            }
+        }
+    }
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_block_num_values_per_frame_get
+                (tng_trajectory_t tng_data,
+                 int64_t block_id,
+                 int64_t *n_values_per_frame)
+{
+    int64_t i;
+    tng_function_status stat;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+
+    for(i = 0; i < tng_data->n_particle_data_blocks; i++)
+    {
+        p_data = &tng_data->non_tr_particle_data[i];
+        if(p_data->block_id == block_id)
+        {
+            *n_values_per_frame = p_data->n_values_per_frame;
+            return(TNG_SUCCESS);
+        }
+    }
+    for(i = 0; i < tng_data->n_data_blocks; i++)
+    {
+        np_data = &tng_data->non_tr_data[i];
+        if(np_data->block_id == block_id)
+        {
+            *n_values_per_frame = np_data->n_values_per_frame;
+            return(TNG_SUCCESS);
+        }
+    }
+
+    stat = tng_particle_data_find(tng_data, block_id, &p_data);
+    if(stat == TNG_SUCCESS)
+    {
+        *n_values_per_frame = p_data->n_values_per_frame;
+        return(TNG_SUCCESS);
+    }
+    else
+    {
+        stat = tng_data_find(tng_data, block_id, &np_data);
+        if(stat == TNG_SUCCESS)
+        {
+            *n_values_per_frame = np_data->n_values_per_frame;
+            return(TNG_SUCCESS);
+        }
+        else
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+            stat = tng_particle_data_find(tng_data, block_id, &p_data);
+            if(stat == TNG_SUCCESS)
+            {
+                *n_values_per_frame = p_data->n_values_per_frame;
+                return(TNG_SUCCESS);
+            }
+            else
+            {
+                stat = tng_data_find(tng_data, block_id, &np_data);
+                if(stat == TNG_SUCCESS)
+                {
+                    *n_values_per_frame = np_data->n_values_per_frame;
+                    return(TNG_SUCCESS);
+                }
+            }
+        }
+    }
+
+    return(TNG_FAILURE);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_data_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const int64_t block_id,
+                 const void *values,
+                 const char hash_mode)
+{
+    int64_t header_pos, file_pos;
+    int64_t output_file_len, n_values_per_frame, size, contents_size;
+    int64_t header_size, temp_first, temp_last;
+    int64_t i, last_frame;
+    long temp_current;
+    tng_gen_block_t block;
+    tng_trajectory_frame_set_t frame_set;
+    FILE *temp = tng_data->input_file;
+    struct tng_non_particle_data data;
+    tng_function_status stat;
+    char dependency, sparse_data, datatype;
+    void *copy;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(values, "TNG library: values must not be a NULL pointer.");
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    temp_first = tng_data->first_trajectory_frame_set_input_file_pos;
+    temp_last = tng_data->last_trajectory_frame_set_input_file_pos;
+    temp_current = tng_data->current_trajectory_frame_set_input_file_pos;
+    tng_data->first_trajectory_frame_set_input_file_pos =
+    tng_data->first_trajectory_frame_set_output_file_pos;
+    tng_data->last_trajectory_frame_set_input_file_pos =
+    tng_data->last_trajectory_frame_set_output_file_pos;
+    tng_data->current_trajectory_frame_set_input_file_pos =
+    tng_data->current_trajectory_frame_set_output_file_pos;
+
+    tng_data->input_file = tng_data->output_file;
+
+    stat = tng_frame_set_of_frame_find(tng_data, frame_nr);
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(stat != TNG_SUCCESS)
+    {
+        last_frame = frame_set->first_frame +
+                     frame_set->n_frames - 1;
+        /* If the wanted frame would be in the frame set after the last
+            * frame set create a new frame set. */
+        if(stat == TNG_FAILURE &&
+            last_frame < frame_nr)
+/*           (last_frame < frame_nr &&
+            tng_data->current_trajectory_frame_set.first_frame +
+            tng_data->frame_set_n_frames >= frame_nr))*/
+        {
+            if(last_frame + tng_data->frame_set_n_frames < frame_nr)
+            {
+                last_frame = frame_nr - 1;
+            }
+            tng_frame_set_new(tng_data,
+                              last_frame+1,
+                              tng_data->frame_set_n_frames);
+            file_pos = ftell(tng_data->output_file);
+            fseek(tng_data->output_file, 0, SEEK_END);
+            output_file_len = ftell(tng_data->output_file);
+            fseek(tng_data->output_file, (long)file_pos, SEEK_SET);
+
+            /* Read mapping blocks from the last frame set */
+            tng_block_init(&block);
+
+            stat = tng_block_header_read(tng_data, block);
+            while(file_pos < output_file_len &&
+                  stat != TNG_CRITICAL &&
+                  block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                if(block->id == TNG_PARTICLE_MAPPING)
+                {
+                    tng_trajectory_mapping_block_read(tng_data, block,
+                                                      hash_mode);
+                }
+                else
+                {
+                    fseek(tng_data->output_file, (long)block->block_contents_size,
+                        SEEK_CUR);
+                }
+                file_pos = ftell(tng_data->output_file);
+                if(file_pos < output_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+
+            tng_block_destroy(&block);
+            /* Write the frame set to disk */
+            if(tng_frame_set_write(tng_data, hash_mode) != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error writing frame set. %s: %d\n", __FILE__, __LINE__);
+                return(TNG_CRITICAL);
+            }
+        }
+        else
+        {
+            tng_data->input_file = temp;
+            tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+            tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+            tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+            return(stat);
+        }
+    }
+
+    tng_block_init(&block);
+
+    file_pos = tng_data->current_trajectory_frame_set_output_file_pos;
+
+    fseek(tng_data->output_file, 0, SEEK_END);
+    output_file_len = ftell(tng_data->output_file);
+    fseek(tng_data->output_file, (long)file_pos, SEEK_SET);
+
+    /* Read past the frame set block first */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        tng_data->input_file = temp;
+
+        tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+        tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+        tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+        return(stat);
+    }
+    fseek(tng_data->output_file, (long)block->block_contents_size,
+            SEEK_CUR);
+
+    /* Read all block headers until next frame set block or
+     * until the wanted block id is found */
+    stat = tng_block_header_read(tng_data, block);
+    while(file_pos < output_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != block_id &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fseek(tng_data->output_file, (long)block->block_contents_size, SEEK_CUR);
+        file_pos = ftell(tng_data->output_file);
+        if(file_pos < output_file_len)
+        {
+            stat = tng_block_header_read(tng_data, block);
+        }
+    }
+    if(stat == TNG_CRITICAL)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        tng_data->input_file = temp;
+        tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+        tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+        tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+        return(stat);
+    }
+
+    contents_size = block->block_contents_size;
+    header_size = block->header_contents_size;
+
+    header_pos = ftell(tng_data->output_file) - header_size;
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(fread(&datatype, sizeof(datatype), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(fread(&dependency, sizeof(dependency), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    data.datatype = datatype;
+
+    if(!(dependency & TNG_FRAME_DEPENDENT) ||
+       (dependency & TNG_PARTICLE_DEPENDENT))
+    {
+        tng_block_destroy(&block);
+        tng_data->input_file = temp;
+
+        tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+        tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+        tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+        return(TNG_FAILURE);
+    }
+
+    if(fread(&sparse_data, sizeof(sparse_data), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(fread(&data.n_values_per_frame, sizeof(data.n_values_per_frame), 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+            &data.n_values_per_frame)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(fread(&data.codec_id, sizeof(data.codec_id), 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+            &data.codec_id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(data.codec_id != TNG_UNCOMPRESSED)
+    {
+        if(fread(&data.compression_multiplier,
+                 sizeof(data.compression_multiplier), 1, tng_data->input_file)
+            == 0)
+        {
+            fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                (int64_t *)&data.compression_multiplier)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+    }
+    else
+    {
+        data.compression_multiplier = 1;
+    }
+
+    if(sparse_data)
+    {
+        if(fread(&data.first_frame_with_data, sizeof(data.first_frame_with_data),
+                 1, tng_data->input_file) == 0)
+        {
+            fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                &data.first_frame_with_data)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+
+        if(fread(&data.stride_length, sizeof(data.stride_length),
+                 1, tng_data->input_file) == 0)
+        {
+            fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                &data.stride_length)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+    }
+    else
+    {
+        data.first_frame_with_data = 0;
+        data.stride_length = 1;
+    }
+    data.n_frames = tng_data->current_trajectory_frame_set.n_frames;
+
+    tng_data->input_file = temp;
+
+    tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+    tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+    tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+
+    switch(data.datatype)
+    {
+        case(TNG_INT_DATA):
+            size = sizeof(int64_t);
+            break;
+        case(TNG_FLOAT_DATA):
+            size = sizeof(float);
+            break;
+        case(TNG_DOUBLE_DATA):
+            size = sizeof(double);
+            break;
+        default:
+            fprintf(stderr, "TNG library: Cannot calculate writing locations. %s: %d.\n", __FILE__,
+                   __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_FAILURE);
+    }
+
+    n_values_per_frame = data.n_values_per_frame;
+
+    file_pos = (frame_nr - tng_max_i64(frame_set->first_frame,
+                               data.first_frame_with_data)) /
+                data.stride_length;
+    file_pos *= size * n_values_per_frame;
+
+    if(file_pos > contents_size)
+    {
+        fprintf(stderr, "TNG library: Attempting to write outside the block. %s: %d\n", __FILE__,
+               __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_FAILURE);
+    }
+
+    fseek(tng_data->output_file, (long)file_pos, SEEK_CUR);
+
+    /* If the endianness is not big endian the data needs to be swapped */
+    if((data.datatype == TNG_INT_DATA ||
+        data.datatype == TNG_DOUBLE_DATA) &&
+       tng_data->output_endianness_swap_func_64)
+    {
+        copy = malloc(n_values_per_frame * size);
+        memcpy(copy, values, n_values_per_frame * size);
+        for(i = 0; i < n_values_per_frame; i++)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                (int64_t *)copy+i)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        fwrite(copy, n_values_per_frame, size,
+               tng_data->output_file);
+        free(copy);
+    }
+    else if(data.datatype == TNG_FLOAT_DATA &&
+            tng_data->output_endianness_swap_func_32)
+    {
+        copy = malloc(n_values_per_frame * size);
+        memcpy(copy, values, n_values_per_frame * size);
+        for(i = 0; i < n_values_per_frame; i++)
+        {
+            if(tng_data->output_endianness_swap_func_32(tng_data,
+                (int32_t *)copy+i)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        fwrite(copy, n_values_per_frame, size,
+               tng_data->output_file);
+        free(copy);
+    }
+
+    else
+    {
+        fwrite(values, n_values_per_frame, size, tng_data->output_file);
+    }
+
+    fflush(tng_data->output_file);
+
+    /* Update the number of written frames in the frame set. */
+    if(frame_nr - frame_set->first_frame + 1 > frame_set->n_written_frames)
+    {
+        frame_set->n_written_frames = frame_nr - frame_set->first_frame + 1;
+    }
+
+    /* If the last frame has been written update the hash */
+    if(hash_mode == TNG_USE_HASH && (frame_nr + data.stride_length -
+       data.first_frame_with_data) >=
+       frame_set->n_frames)
+    {
+        tng_md5_hash_update(tng_data, block, header_pos, header_pos +
+                            header_size);
+    }
+
+    tng_block_destroy(&block);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_particle_data_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const int64_t block_id,
+                 const int64_t val_first_particle,
+                 const int64_t val_n_particles,
+                 const void *values,
+                 const char hash_mode)
+{
+    int64_t header_pos, file_pos, tot_n_particles;
+    int64_t output_file_len, n_values_per_frame, size, contents_size;
+    int64_t header_size, temp_first, temp_last;
+    int64_t mapping_block_end_pos, num_first_particle, block_n_particles;
+    int64_t i, last_frame;
+    long temp_current;
+    tng_gen_block_t block;
+    tng_trajectory_frame_set_t frame_set;
+    FILE *temp = tng_data->input_file;
+    struct tng_particle_data data;
+    tng_function_status stat;
+    tng_particle_mapping_t mapping;
+    char dependency, sparse_data, datatype;
+    void *copy;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(values, "TNG library: values must not be a NULL pointer.");
+    TNG_ASSERT(val_first_particle >= 0, "TNG library: val_first_particle must be >= 0.");
+    TNG_ASSERT(val_n_particles >= 0, "TNG library: val_n_particles must be >= 0.");
+
+    if(tng_output_file_init(tng_data) != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot initialise destination file. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    temp_first = tng_data->first_trajectory_frame_set_input_file_pos;
+    temp_last = tng_data->last_trajectory_frame_set_input_file_pos;
+    temp_current = tng_data->current_trajectory_frame_set_input_file_pos;
+    tng_data->first_trajectory_frame_set_input_file_pos =
+    tng_data->first_trajectory_frame_set_output_file_pos;
+    tng_data->last_trajectory_frame_set_input_file_pos =
+    tng_data->last_trajectory_frame_set_output_file_pos;
+    tng_data->current_trajectory_frame_set_input_file_pos =
+    tng_data->current_trajectory_frame_set_output_file_pos;
+
+    tng_data->input_file = tng_data->output_file;
+
+    stat = tng_frame_set_of_frame_find(tng_data, frame_nr);
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(stat != TNG_SUCCESS)
+    {
+        last_frame = frame_set->first_frame +
+                     frame_set->n_frames - 1;
+/*         fprintf(stderr, "TNG library: Frame %"PRId64" not found. Last frame: %"PRId64"\n", frame_nr,
+                  last_frame); */
+        /* If the wanted frame would be in the frame set after the last
+         * frame set create a new frame set. */
+        if(stat == TNG_FAILURE &&
+           (last_frame < frame_nr &&
+            last_frame + tng_data->frame_set_n_frames >= frame_nr))
+        {
+            if(last_frame + tng_data->frame_set_n_frames < frame_nr)
+            {
+                last_frame = frame_nr - 1;
+            }
+            tng_frame_set_new(tng_data,
+                              last_frame+1,
+                              tng_data->frame_set_n_frames);
+
+            file_pos = ftell(tng_data->output_file);
+            fseek(tng_data->output_file, 0, SEEK_END);
+            output_file_len = ftell(tng_data->output_file);
+            fseek(tng_data->output_file, (long)file_pos, SEEK_SET);
+
+            /* Read mapping blocks from the last frame set */
+            tng_block_init(&block);
+
+            stat = tng_block_header_read(tng_data, block);
+            while(file_pos < output_file_len &&
+                  stat != TNG_CRITICAL &&
+                  block->id != TNG_TRAJECTORY_FRAME_SET)
+            {
+                if(block->id == TNG_PARTICLE_MAPPING)
+                {
+                    tng_trajectory_mapping_block_read(tng_data, block,
+                                                      hash_mode);
+                }
+                else
+                {
+                    fseek(tng_data->output_file, (long)block->block_contents_size,
+                        SEEK_CUR);
+                }
+                file_pos = ftell(tng_data->output_file);
+                if(file_pos < output_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+
+            tng_block_destroy(&block);
+            /* Write the frame set to disk */
+            if(tng_frame_set_write(tng_data, hash_mode) != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error writing frame set. %s: %d\n", __FILE__, __LINE__);
+                exit(1);
+            }
+        }
+        else
+        {
+            tng_data->input_file = temp;
+            tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+            tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+            tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+            return(stat);
+        }
+    }
+
+
+    tng_block_init(&block);
+
+    file_pos = tng_data->current_trajectory_frame_set_output_file_pos;
+
+    fseek(tng_data->output_file, 0, SEEK_END);
+    output_file_len = ftell(tng_data->output_file);
+    fseek(tng_data->output_file, (long)file_pos, SEEK_SET);
+
+    /* Read past the frame set block first */
+    stat = tng_block_header_read(tng_data, block);
+    if(stat == TNG_CRITICAL)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+               file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        tng_data->input_file = temp;
+
+        tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+        tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+        tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+        return(stat);
+    }
+    fseek(tng_data->output_file, (long)block->block_contents_size,
+            SEEK_CUR);
+
+    if(tng_data->var_num_atoms_flag)
+    {
+        tot_n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        tot_n_particles = tng_data->n_particles;
+    }
+
+    if(val_n_particles < tot_n_particles)
+    {
+        mapping_block_end_pos = -1;
+        /* Read all mapping blocks to find the right place to put the data */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < output_file_len &&
+                stat != TNG_CRITICAL &&
+                block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            if(block->id == TNG_PARTICLE_MAPPING)
+            {
+                tng_trajectory_mapping_block_read(tng_data, block, hash_mode);
+            }
+            else
+            {
+                fseek(tng_data->output_file, (long)block->block_contents_size,
+                      SEEK_CUR);
+            }
+            file_pos = ftell(tng_data->output_file);
+            if(block->id == TNG_PARTICLE_MAPPING)
+            {
+                mapping = &frame_set->mappings[frame_set->n_mapping_blocks - 1];
+                if(val_first_particle >= mapping->num_first_particle &&
+                   val_first_particle < mapping->num_first_particle +
+                   mapping->n_particles &&
+                   val_first_particle + val_n_particles <=
+                   mapping->num_first_particle + mapping->n_particles)
+                {
+                    mapping_block_end_pos = file_pos;
+                }
+            }
+            if(file_pos < output_file_len)
+            {
+                stat = tng_block_header_read(tng_data, block);
+            }
+        }
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                   file_pos, __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            tng_data->input_file = temp;
+
+            tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+            tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+            tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+            return(stat);
+        }
+        if(mapping_block_end_pos < 0)
+        {
+            tng_block_destroy(&block);
+            tng_data->input_file = temp;
+
+            tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+            tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+            tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+            return(TNG_FAILURE);
+        }
+        fseek(tng_data->output_file, (long)mapping_block_end_pos, SEEK_SET);
+    }
+
+    /* Read all block headers until next frame set block or
+     * until the wanted block id is found */
+    stat = tng_block_header_read(tng_data, block);
+    while(file_pos < output_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != block_id &&
+            block->id != TNG_PARTICLE_MAPPING &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+    {
+        fseek(tng_data->output_file, (long)block->block_contents_size, SEEK_CUR);
+        file_pos = ftell(tng_data->output_file);
+        if(file_pos < output_file_len)
+        {
+            stat = tng_block_header_read(tng_data, block);
+        }
+    }
+    if(stat == TNG_CRITICAL)
+    {
+        fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                file_pos, __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        tng_data->input_file = temp;
+
+        tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+        tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+        tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+        return(stat);
+    }
+
+    contents_size = block->block_contents_size;
+    header_size = block->header_contents_size;
+
+    header_pos = ftell(tng_data->output_file) - header_size;
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(fread(&datatype, sizeof(datatype), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    data.datatype = datatype;
+
+    if(fread(&dependency, sizeof(dependency), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(!(dependency & TNG_FRAME_DEPENDENT) ||
+       !(dependency & TNG_PARTICLE_DEPENDENT))
+    {
+        tng_block_destroy(&block);
+        tng_data->input_file = temp;
+
+        tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+        tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+        tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+        return(TNG_FAILURE);
+    }
+
+    if(fread(&sparse_data, sizeof(sparse_data), 1, tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+
+    if(fread(&data.n_values_per_frame, sizeof(data.n_values_per_frame), 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+            &data.n_values_per_frame)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(fread(&data.codec_id, sizeof(data.codec_id), 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+            &data.codec_id)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(data.codec_id != TNG_UNCOMPRESSED)
+    {
+        if(fread(&data.compression_multiplier,
+                 sizeof(data.compression_multiplier), 1, tng_data->input_file)
+            == 0)
+        {
+            fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+               (int64_t *)&data.compression_multiplier)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+    }
+    else
+    {
+        data.compression_multiplier = 1;
+    }
+
+    if(sparse_data)
+    {
+        if(fread(&data.first_frame_with_data,
+                 sizeof(data.first_frame_with_data),
+                 1, tng_data->input_file) == 0)
+        {
+            fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                &data.first_frame_with_data)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+
+        if(fread(&data.stride_length, sizeof(data.stride_length),
+                 1, tng_data->input_file) == 0)
+        {
+            fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_CRITICAL);
+        }
+        if(tng_data->output_endianness_swap_func_64)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                &data.stride_length)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+    }
+    else
+    {
+        data.first_frame_with_data = 0;
+        data.stride_length = 1;
+    }
+    data.n_frames = tng_data->current_trajectory_frame_set.n_frames;
+
+    if(fread(&num_first_particle, sizeof(num_first_particle), 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+            &num_first_particle)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+    if(fread(&block_n_particles, sizeof(block_n_particles), 1,
+             tng_data->input_file) == 0)
+    {
+        fprintf(stderr, "TNG library: Error reading file. %s: %d\n", __FILE__, __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_CRITICAL);
+    }
+    if(tng_data->output_endianness_swap_func_64)
+    {
+        if(tng_data->output_endianness_swap_func_64(tng_data,
+            &block_n_particles)
+            != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                    __FILE__, __LINE__);
+        }
+    }
+
+
+    tng_data->input_file = temp;
+
+    tng_data->first_trajectory_frame_set_input_file_pos = temp_first;
+    tng_data->last_trajectory_frame_set_input_file_pos = temp_last;
+    tng_data->current_trajectory_frame_set_input_file_pos = temp_current;
+
+
+    switch(data.datatype)
+    {
+        case(TNG_INT_DATA):
+            size = sizeof(int64_t);
+            break;
+        case(TNG_FLOAT_DATA):
+            size = sizeof(float);
+            break;
+        case(TNG_DOUBLE_DATA):
+            size = sizeof(double);
+            break;
+        default:
+            fprintf(stderr, "TNG library: Cannot calculate writing locations. %s: %d.\n", __FILE__,
+                   __LINE__);
+            tng_block_destroy(&block);
+            return(TNG_FAILURE);
+    }
+
+    n_values_per_frame = data.n_values_per_frame;
+
+    file_pos = (frame_nr - tng_max_i64(frame_set->first_frame,
+                               data.first_frame_with_data)) /
+                data.stride_length;
+    file_pos *= block_n_particles * size * n_values_per_frame;
+
+    if(file_pos > contents_size)
+    {
+        fprintf(stderr, "TNG library: Attempting to write outside the block. %s: %d\n", __FILE__,
+               __LINE__);
+        tng_block_destroy(&block);
+        return(TNG_FAILURE);
+    }
+
+    fseek(tng_data->output_file, (long)file_pos, SEEK_CUR);
+
+    /* If the endianness is not big endian the data needs to be swapped */
+    if((data.datatype == TNG_INT_DATA ||
+        data.datatype == TNG_DOUBLE_DATA) &&
+       tng_data->output_endianness_swap_func_64)
+    {
+        copy = malloc(val_n_particles * n_values_per_frame * size);
+        memcpy(copy, values, val_n_particles * n_values_per_frame * size);
+        for(i = 0; i < val_n_particles * n_values_per_frame; i++)
+        {
+            if(tng_data->output_endianness_swap_func_64(tng_data,
+                (int64_t *) copy+i)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        fwrite(copy, val_n_particles * n_values_per_frame, size,
+               tng_data->output_file);
+        free(copy);
+    }
+    else if(data.datatype == TNG_FLOAT_DATA &&
+       tng_data->output_endianness_swap_func_32)
+    {
+        copy = malloc(val_n_particles * n_values_per_frame * size);
+        memcpy(copy, values, val_n_particles * n_values_per_frame * size);
+        for(i = 0; i < val_n_particles * n_values_per_frame; i++)
+        {
+            if(tng_data->output_endianness_swap_func_32(tng_data,
+                (int32_t *) copy+i)
+                != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot swap byte order. %s: %d\n",
+                        __FILE__, __LINE__);
+            }
+        }
+        fwrite(copy, val_n_particles * n_values_per_frame, size,
+               tng_data->output_file);
+        free(copy);
+    }
+
+    else
+    {
+        fwrite(values, val_n_particles * n_values_per_frame, size,
+               tng_data->output_file);
+    }
+    fflush(tng_data->output_file);
+
+    /* Update the number of written frames in the frame set. */
+    if(frame_nr - frame_set->first_frame + 1 > frame_set->n_written_frames)
+    {
+        frame_set->n_written_frames = frame_nr - frame_set->first_frame + 1;
+    }
+
+    /* If the last frame has been written update the hash */
+    if(hash_mode == TNG_USE_HASH && (frame_nr + data.stride_length -
+       data.first_frame_with_data) >=
+       frame_set->n_frames)
+    {
+        tng_md5_hash_update(tng_data, block, header_pos, header_pos +
+                            header_size);
+    }
+
+    tng_block_destroy(&block);
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_data_values_alloc
+                (const tng_trajectory_t tng_data,
+                 union data_values ***values,
+                 const int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 const char type)
+{
+    int64_t i;
+    tng_function_status stat;
+
+    if(n_frames <= 0 || n_values_per_frame <= 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(*values)
+    {
+        stat = tng_data_values_free(tng_data, *values, n_frames,
+                                    n_values_per_frame,
+                                    type);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot free particle data values. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+    *values = malloc(sizeof(union data_values *) * n_frames);
+    if(!*values)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(union data_values **) * n_frames,
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+
+    }
+
+    for(i = n_frames; i--;)
+    {
+        (*values)[i] = malloc(sizeof(union data_values) *
+                           n_values_per_frame);
+        if(!(*values)[i])
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   sizeof(union data_values) * n_values_per_frame,
+                   __FILE__, __LINE__);
+            free(values);
+            values = 0;
+            return(TNG_CRITICAL);
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+/* FIXME: This needs ***values */
+tng_function_status DECLSPECDLLEXPORT tng_data_values_free
+                (const tng_trajectory_t tng_data,
+                 union data_values **values,
+                 const int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 const char type)
+{
+    int64_t i, j;
+    (void)tng_data;
+
+    if(values)
+    {
+        for(i = 0; i < n_frames; i++)
+        {
+            if(values[i])
+            {
+                if(type == TNG_CHAR_DATA)
+                {
+                    for(j = n_values_per_frame; j--;)
+                    {
+                        if(values[i][j].c)
+                        {
+                            free(values[i][j].c);
+                            values[i][j].c = 0;
+                        }
+                    }
+                }
+                free(values[i]);
+                values[i] = 0;
+            }
+        }
+        free(values);
+        values = 0;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_particle_data_values_alloc
+                (const tng_trajectory_t tng_data,
+                 union data_values ****values,
+                 const int64_t n_frames,
+                 const int64_t n_particles,
+                 const int64_t n_values_per_frame,
+                 const char type)
+{
+    int64_t i, j;
+    tng_function_status stat;
+
+    if(n_particles == 0 || n_values_per_frame == 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(*values)
+    {
+        stat = tng_particle_data_values_free(tng_data, *values, n_frames,
+                                             n_particles, n_values_per_frame,
+                                             type);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot free particle data values. %s: %d\n",
+                   __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+    *values = malloc(sizeof(union data_values **) * n_frames);
+    if(!*values)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               sizeof(union data_values **) * n_frames,
+               __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+
+    }
+
+    for(i = n_frames; i--;)
+    {
+        (*values)[i] = malloc(sizeof(union data_values *) *
+                           n_particles);
+        if(!(*values)[i])
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                   sizeof(union data_values *) * n_particles,
+                   __FILE__, __LINE__);
+            free(*values);
+            *values = 0;
+            return(TNG_CRITICAL);
+        }
+        for(j = n_particles; j--;)
+        {
+            (*values)[i][j] = malloc(sizeof(union data_values) *
+                                  n_values_per_frame);
+            if(!(*values)[i][j])
+            {
+                fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                    sizeof(union data_values *) * n_particles,
+                    __FILE__, __LINE__);
+                tng_particle_data_values_free(tng_data, *values, n_frames,
+                                              n_particles, n_values_per_frame,
+                                              type);
+                *values = 0;
+                return(TNG_CRITICAL);
+            }
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+/* FIXME: This needs ****values */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_values_free
+                (const tng_trajectory_t tng_data,
+                 union data_values ***values,
+                 const int64_t n_frames,
+                 const int64_t n_particles,
+                 const int64_t n_values_per_frame,
+                 const char type)
+{
+    int64_t i, j, k;
+    (void)tng_data;
+
+    if(values)
+    {
+        for(i = 0; i < n_frames; i++)
+        {
+            if(values[i])
+            {
+                for(j = 0; j < n_particles; j++)
+                {
+                    if(type == TNG_CHAR_DATA)
+                    {
+                        for(k = n_values_per_frame; k--;)
+                        {
+                            if(values[i][j][k].c)
+                            {
+                                free(values[i][j][k].c);
+                                values[i][j][k].c = 0;
+                            }
+                        }
+                    }
+                    free(values[i][j]);
+                    values[i][j] = 0;
+                }
+                free(values[i]);
+                values[i] = 0;
+            }
+        }
+        free(values);
+        values = 0;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+
+tng_function_status DECLSPECDLLEXPORT tng_data_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 union data_values ***values,
+                 int64_t *n_frames,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t i, j, file_pos, block_index;
+    int size;
+    size_t len;
+    tng_non_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_frames, "TNG library: n_frames must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    block_index = -1;
+    data = 0;
+
+    if(tng_data_find(tng_data, block_id, &data) != TNG_SUCCESS)
+    {
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+                stat != TNG_CRITICAL &&
+                block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            /* Use hash by default */
+            stat = tng_block_read_next(tng_data, block,
+                                    TNG_USE_HASH);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+
+        for(i = frame_set->n_data_blocks; i-- ;)
+        {
+            data = &frame_set->tr_data[i];
+            if(data->block_id == block_id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+        if(block_index < 0)
+        {
+            return(TNG_FAILURE);
+        }
+    }
+
+    *n_frames = tng_max_i64(1, data->n_frames);
+    *n_values_per_frame = data->n_values_per_frame;
+    *type = data->datatype;
+
+    if(*values == 0)
+    {
+        if(tng_data_values_alloc(tng_data, values, *n_frames,
+                                 *n_values_per_frame,
+                                 *type)
+        != TNG_SUCCESS)
+        {
+            return(TNG_CRITICAL);
+        }
+    }
+
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_values_per_frame; j--;)
+            {
+                len = strlen(data->strings[i][j]) + 1;
+                (*values)[i][j].c = malloc(len);
+                strncpy((*values)[i][j].c, data->strings[i][j], len);
+            }
+        }
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int);
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_values_per_frame; j--;)
+            {
+                (*values)[i][j].i = *(int *)((char *)data->values + size *
+                                             (i*(*n_values_per_frame) + j));
+            }
+        }
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_values_per_frame; j--;)
+            {
+                (*values)[i][j].f = *(float *)((char *)data->values + size *
+                                               (i*(*n_values_per_frame) + j));
+            }
+        }
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_values_per_frame; j--;)
+            {
+                (*values)[i][j].d = *(double *)((char *)data->values + size *
+                                                (i*(*n_values_per_frame) + j));
+            }
+        }
+    }
+
+    data->last_retrieved_frame = frame_set->first_frame + data->n_frames - 1;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status tng_data_vector_get(tng_trajectory_t tng_data,
+                                        const int64_t block_id,
+                                        void **values,
+                                        int64_t *n_frames,
+                                        int64_t *stride_length,
+                                        int64_t *n_values_per_frame,
+                                        char *type)
+{
+    int64_t file_pos, data_size, n_frames_div, block_index;
+    int i, size;
+    tng_non_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    void *temp;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_frames, "TNG library: n_frames must not be a NULL pointer.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    block_index = -1;
+    data = 0;
+
+    if(tng_data_find(tng_data, block_id, &data) != TNG_SUCCESS)
+    {
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+                stat != TNG_CRITICAL &&
+                block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            /* Use hash by default */
+            stat = tng_block_read_next(tng_data, block,
+                                    TNG_USE_HASH);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+
+        for(i = frame_set->n_data_blocks; i-- ;)
+        {
+            data = &frame_set->tr_data[i];
+            if(data->block_id == block_id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+        if(block_index < 0)
+        {
+            return(TNG_FAILURE);
+        }
+    }
+
+    *type = data->datatype;
+
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        return(TNG_FAILURE);
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    *n_frames = data->n_frames;
+    *n_values_per_frame = data->n_values_per_frame;
+    *stride_length = data->stride_length;
+    n_frames_div = (*n_frames % *stride_length) ? *n_frames / *stride_length + 1:
+                   *n_frames / *stride_length;
+
+    data_size = n_frames_div * size *
+                *n_values_per_frame;
+
+    temp = realloc(*values, data_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               data_size, __FILE__, __LINE__);
+        free(*values);
+        *values = 0;
+        return(TNG_CRITICAL);
+    }
+
+    *values = temp;
+
+    memcpy(*values, data->values, data_size);
+
+    data->last_retrieved_frame = frame_set->first_frame + data->n_frames - 1;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 union data_values ***values,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t i, j, n_frames, file_pos, current_frame_pos, first_frame;
+    int64_t block_index;
+    int size;
+    size_t len;
+    tng_non_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(start_frame_nr <= end_frame_nr, "TNG library: start_frame_nr must not be higher than tne end_frame_nr.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    block_index = -1;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+    first_frame = frame_set->first_frame;
+
+    stat = tng_frame_set_of_frame_find(tng_data, start_frame_nr);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+
+    /* Do not re-read the frame set. */
+    if(first_frame != frame_set->first_frame ||
+       frame_set->n_data_blocks <= 0)
+    {
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            stat = tng_block_read_next(tng_data, block,
+                                    hash_mode);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+
+
+    /* See if there is a data block of this ID.
+     * Start checking the last read frame set */
+    for(i = frame_set->n_data_blocks; i-- ;)
+    {
+        data = &frame_set->tr_data[i];
+        if(data->block_id == block_id)
+        {
+            block_index = i;
+            break;
+        }
+    }
+
+    if(block_index < 0)
+    {
+        fprintf(stderr, "TNG library: Could not find non-particle data block with id %"PRId64". %s: %d\n",
+                block_id, __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    n_frames = end_frame_nr - start_frame_nr + 1;
+    *n_values_per_frame = data->n_values_per_frame;
+    *type = data->datatype;
+
+    if(*values == 0)
+    {
+        if(tng_data_values_alloc(tng_data, values, n_frames,
+                                 *n_values_per_frame,
+                                 *type) != TNG_SUCCESS)
+        {
+            return(TNG_CRITICAL);
+        }
+    }
+
+    current_frame_pos = start_frame_nr - frame_set->first_frame;
+    /* It's not very elegant to reuse so much of the code in the different case
+     * statements, but it's unnecessarily slow to have the switch-case block
+     * inside the for loops. */
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_values_per_frame; j--;)
+            {
+                len = strlen(data->strings[current_frame_pos][j]) + 1;
+                (*values)[i][j].c = malloc(len);
+                strncpy((*values)[i][j].c, data->strings[current_frame_pos][j], len);
+            }
+            current_frame_pos++;
+        }
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int);
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_values_per_frame; j--;)
+            {
+                (*values)[i][j].i = *(int *)((char *)data->values + size *
+                                            (current_frame_pos *
+                                             (*n_values_per_frame) + j));
+            }
+            current_frame_pos++;
+        }
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_values_per_frame; j--;)
+            {
+                (*values)[i][j].f = *(float *)((char *)data->values + size *
+                                               (current_frame_pos *
+                                                (*n_values_per_frame) + j));
+            }
+            current_frame_pos++;
+        }
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_values_per_frame; j--;)
+            {
+                (*values)[i][j].d = *(double *)((char *)data->values + size *
+                                                (current_frame_pos *
+                                                 (*n_values_per_frame) + j));
+            }
+            current_frame_pos++;
+        }
+    }
+
+    data->last_retrieved_frame = end_frame_nr;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_vector_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t n_frames, tot_n_frames, n_frames_div, n_frames_div_2, first_frame;
+    int64_t file_pos, current_frame_pos, data_size, frame_size;
+    int64_t last_frame_pos;
+    int size;
+    tng_trajectory_frame_set_t frame_set;
+    tng_non_particle_data_t np_data;
+    tng_gen_block_t block;
+    void *current_values = 0, *temp;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(start_frame_nr <= end_frame_nr, "TNG library: start_frame_nr must not be higher than the end_frame_nr.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+    first_frame = frame_set->first_frame;
+
+    stat = tng_frame_set_of_frame_find(tng_data, start_frame_nr);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    /* Do not re-read the frame set and only need the requested block. */
+    /* TODO: Test that blocks are read correctly now that not all of them are read at the same time. */
+    stat = tng_data_find(tng_data, block_id, &np_data);
+    if(first_frame != frame_set->first_frame ||
+       stat != TNG_SUCCESS)
+    {
+        tng_block_init(&block);
+        if(stat != TNG_SUCCESS)
+        {
+            fseek(tng_data->input_file,
+                  (long)tng_data->current_trajectory_frame_set_input_file_pos,
+                  SEEK_SET);
+            stat = tng_block_header_read(tng_data, block);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header. %s: %d\n",
+                        __FILE__, __LINE__);
+                return(stat);
+            }
+
+            fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+        }
+        file_pos = ftell(tng_data->input_file);
+        /* Read until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            if(block->id == block_id)
+            {
+                stat = tng_block_read_next(tng_data, block,
+                                        hash_mode);
+                if(stat != TNG_CRITICAL)
+                {
+                    file_pos = ftell(tng_data->input_file);
+                    if(file_pos < tng_data->input_file_len)
+                    {
+                        stat = tng_block_header_read(tng_data, block);
+                    }
+                }
+            }
+            else
+            {
+                file_pos += block->block_contents_size + block->header_contents_size;
+                fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+
+    stat = tng_data_find(tng_data, block_id, &np_data);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    stat = tng_data_vector_get(tng_data, block_id, &current_values,
+                               &n_frames, stride_length,
+                               n_values_per_frame, type);
+
+    if(stat != TNG_SUCCESS)
+    {
+        if(current_values)
+        {
+            free(current_values);
+        }
+        return(stat);
+    }
+
+    if(n_frames == 1 && n_frames < frame_set->n_frames)
+    {
+        tot_n_frames = 1;
+    }
+    else
+    {
+        tot_n_frames = end_frame_nr - start_frame_nr + 1;
+    }
+
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        return(TNG_FAILURE);
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    n_frames_div = (tot_n_frames % *stride_length) ?
+                 tot_n_frames / *stride_length + 1:
+                 tot_n_frames / *stride_length;
+    data_size = n_frames_div * size * (*n_values_per_frame);
+
+/*     fprintf(stderr, "TNG library: size: %d, n_frames_div: %"PRId64", data_size: %"PRId64"\n",
+              size, n_frames_div, data_size);
+*/
+    temp = realloc(*values, data_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               data_size, __FILE__, __LINE__);
+        free(*values);
+        *values = 0;
+        return(TNG_CRITICAL);
+    }
+
+    *values = temp;
+
+    if( n_frames == 1 && n_frames < frame_set->n_frames)
+    {
+        memcpy(*values, current_values, size * (*n_values_per_frame));
+    }
+    else
+    {
+        current_frame_pos = start_frame_nr - frame_set->first_frame;
+
+        frame_size = size * (*n_values_per_frame);
+
+        last_frame_pos = tng_min_i64(n_frames,
+                                     end_frame_nr - start_frame_nr);
+
+        n_frames_div = current_frame_pos / *stride_length;
+        n_frames_div_2 = (last_frame_pos % *stride_length) ?
+                       last_frame_pos / *stride_length + 1:
+                       last_frame_pos / *stride_length;
+        n_frames_div_2 = tng_max_i64(1, n_frames_div_2);
+
+        memcpy(*values, (char *)current_values + n_frames_div * frame_size,
+               n_frames_div_2 * frame_size);
+
+        current_frame_pos += n_frames - current_frame_pos;
+
+        while(current_frame_pos <= end_frame_nr - start_frame_nr)
+        {
+            stat = tng_frame_set_read_next(tng_data, hash_mode);
+            if(stat != TNG_SUCCESS)
+            {
+                if(current_values)
+                {
+                    free(current_values);
+                }
+                free(*values);
+                *values = 0;
+                return(stat);
+            }
+
+            stat = tng_data_vector_get(tng_data, block_id, &current_values,
+                                    &n_frames, stride_length,
+                                    n_values_per_frame, type);
+
+            if(stat != TNG_SUCCESS)
+            {
+                if(current_values)
+                {
+                    free(current_values);
+                }
+                free(*values);
+                *values = 0;
+                return(stat);
+            }
+
+            last_frame_pos = tng_min_i64(n_frames,
+                                         end_frame_nr - current_frame_pos);
+
+            n_frames_div = current_frame_pos / *stride_length;
+            n_frames_div_2 = (last_frame_pos % *stride_length) ?
+                           last_frame_pos / *stride_length + 1:
+                           last_frame_pos / *stride_length;
+            n_frames_div_2 = tng_max_i64(1, n_frames_div_2);
+
+            memcpy(((char *)*values) + n_frames_div * frame_size,
+                   current_values,
+                   n_frames_div_2 * frame_size);
+
+            current_frame_pos += n_frames;
+        }
+    }
+
+    if(current_values)
+    {
+        free(current_values);
+    }
+
+    np_data->last_retrieved_frame = end_frame_nr;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 union data_values ****values,
+                 int64_t *n_frames,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t i, j, k, mapping, file_pos, i_step, block_index;
+    int size;
+    size_t len;
+    tng_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    char block_type_flag;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_frames, "TNG library: n_frames must not be a NULL pointer.");
+    TNG_ASSERT(n_particles, "TNG library: n_particles must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    block_index = -1;
+    data = 0;
+
+    if(tng_particle_data_find(tng_data, block_id, &data) != TNG_SUCCESS)
+    {
+        if(tng_data->current_trajectory_frame_set_input_file_pos > 0)
+        {
+            block_type_flag = TNG_TRAJECTORY_BLOCK;
+        }
+        else
+        {
+            block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+        }
+
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+                stat != TNG_CRITICAL &&
+                block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            /* Use hash by default */
+            stat = tng_block_read_next(tng_data, block,
+                                    TNG_USE_HASH);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+
+        for(i = frame_set->n_particle_data_blocks; i-- ;)
+        {
+            data = &frame_set->tr_particle_data[i];
+            if(data->block_id == block_id)
+            {
+                block_index = i;
+                block_type_flag = TNG_TRAJECTORY_BLOCK;
+                break;
+            }
+        }
+        if(block_index < 0)
+        {
+            return(TNG_FAILURE);
+        }
+    }
+    else
+    {
+        if(tng_data->current_trajectory_frame_set_input_file_pos > 0)
+        {
+            block_type_flag = TNG_TRAJECTORY_BLOCK;
+        }
+        else
+        {
+            block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+        }
+    }
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK &&
+       tng_data->var_num_atoms_flag)
+    {
+        *n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        *n_particles = tng_data->n_particles;
+    }
+
+    *n_frames = tng_max_i64(1, data->n_frames);
+    *n_values_per_frame = data->n_values_per_frame;
+    *type = data->datatype;
+
+    if(*values == 0)
+    {
+        if(tng_particle_data_values_alloc(tng_data, values, *n_frames,
+                                         *n_particles, *n_values_per_frame,
+                                         *type)
+            != TNG_SUCCESS)
+        {
+            return(TNG_CRITICAL);
+        }
+    }
+
+    /* It's not very elegant to reuse so much of the code in the different case
+     * statements, but it's unnecessarily slow to have the switch-case block
+     * inside the for loops. */
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    len = strlen(data->strings[i][j][k]) + 1;
+                    (*values)[i][mapping][k].c = malloc(len);
+                    strncpy((*values)[i][mapping][k].c,
+                            data->strings[i][j][k], len);
+                }
+            }
+        }
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int);
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    (*values)[i][mapping][k].i = *(int *)
+                                                 ((char *)data->values + size *
+                                                 (i * i_step + j *
+                                                  (*n_values_per_frame) + k));
+                }
+            }
+        }
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    (*values)[i][mapping][k].f = *(float *)
+                                                 ((char *)data->values + size *
+                                                 (i * i_step + j *
+                                                  (*n_values_per_frame) + k));
+                }
+            }
+        }
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i=*n_frames; i--;)
+        {
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    (*values)[i][mapping][k].d = *(double *)
+                                                 ((char *)data->values + size *
+                                                 (i * i_step + j *
+                                                  (*n_values_per_frame) + k));
+                }
+            }
+        }
+    }
+
+    data->last_retrieved_frame = frame_set->first_frame + data->n_frames - 1;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_vector_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t i, j, mapping, file_pos, i_step, data_size, n_frames_div;
+    int64_t block_index;
+    int size;
+    tng_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    void *temp;
+    char block_type_flag;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_particles, "TNG library: n_particles must not be a NULL pointer.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    block_index = -1;
+    data = 0;
+
+    if(tng_particle_data_find(tng_data, block_id, &data) != TNG_SUCCESS)
+    {
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+                stat != TNG_CRITICAL &&
+                block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            /* Use hash by default */
+            stat = tng_block_read_next(tng_data, block,
+                                    TNG_USE_HASH);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+
+        for(i = frame_set->n_particle_data_blocks; i-- ;)
+        {
+            data = &frame_set->tr_particle_data[i];
+            if(data->block_id == block_id)
+            {
+                block_index = i;
+                break;
+            }
+        }
+        if(block_index < 0)
+        {
+            return(TNG_FAILURE);
+        }
+    }
+
+    if(tng_data->current_trajectory_frame_set_input_file_pos > 0)
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+    }
+    else
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+    }
+
+   if(block_type_flag == TNG_TRAJECTORY_BLOCK &&
+      tng_data->var_num_atoms_flag)
+    {
+        *n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        *n_particles = tng_data->n_particles;
+    }
+
+    *type = data->datatype;
+
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        return(TNG_FAILURE);
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    *n_frames = tng_max_i64(1, data->n_frames);
+    *n_values_per_frame = data->n_values_per_frame;
+    *stride_length = data->stride_length;
+
+    n_frames_div = (*n_frames % *stride_length) ?
+                   *n_frames / *stride_length + 1:
+                   *n_frames / *stride_length;
+
+    data_size = n_frames_div * size * (*n_particles) *
+                (*n_values_per_frame);
+
+    temp = realloc(*values, data_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               data_size, __FILE__, __LINE__);
+        free(*values);
+        *values = 0;
+        return(TNG_CRITICAL);
+    }
+
+    *values = temp;
+
+    if(frame_set->n_mapping_blocks <= 0)
+    {
+        memcpy(*values, data->values, data_size);
+    }
+    else
+    {
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i = *n_frames; i--;)
+        {
+            for(j = *n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                memcpy(((char *)*values) + size * (i * i_step + mapping *
+                       (*n_values_per_frame)),
+                       (char *)data->values + size *
+                       (i * i_step + j * (*n_values_per_frame)),
+                       size * (*n_values_per_frame));
+            }
+        }
+    }
+
+    data->last_retrieved_frame = frame_set->first_frame + data->n_frames - 1;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 union data_values ****values,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t i, j, k, mapping, n_frames, file_pos, current_frame_pos, i_step;
+    int64_t first_frame, block_index;
+    int size;
+    size_t len;
+    tng_particle_data_t data;
+    tng_trajectory_frame_set_t frame_set;
+    tng_gen_block_t block;
+    char block_type_flag;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(start_frame_nr <= end_frame_nr, "TNG library: start_frame_nr must not be higher than tne end_frame_nr.");
+    TNG_ASSERT(n_particles, "TNG library: n_particles must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    block_index = -1;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+    first_frame = frame_set->first_frame;
+
+    stat = tng_frame_set_of_frame_find(tng_data, start_frame_nr);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    /* Do not re-read the frame set. */
+    if(first_frame != frame_set->first_frame ||
+       frame_set->n_particle_data_blocks <= 0)
+    {
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+                stat != TNG_CRITICAL &&
+                block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            stat = tng_block_read_next(tng_data, block,
+                                    hash_mode);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+
+    /* See if there is already a data block of this ID.
+     * Start checking the last read frame set */
+    for(i = frame_set->n_particle_data_blocks; i-- ;)
+    {
+        data = &frame_set->tr_particle_data[i];
+        if(data->block_id == block_id)
+        {
+            block_index = i;
+            block_type_flag = TNG_TRAJECTORY_BLOCK;
+            break;
+        }
+    }
+
+    if(block_index < 0)
+    {
+        fprintf(stderr, "TNG library: Could not find particle data block with id %"PRId64". %s: %d\n",
+                block_id, __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    if(block_type_flag == TNG_TRAJECTORY_BLOCK &&
+       tng_data->var_num_atoms_flag)
+    {
+        *n_particles = frame_set->n_particles;
+    }
+    else
+    {
+        *n_particles = tng_data->n_particles;
+    }
+
+    n_frames = end_frame_nr - start_frame_nr + 1;
+    *n_values_per_frame = data->n_values_per_frame;
+    *type = data->datatype;
+
+    if(*values == 0)
+    {
+        if(tng_particle_data_values_alloc(tng_data, values, n_frames,
+                                         *n_particles, *n_values_per_frame,
+                                         *type)
+            != TNG_SUCCESS)
+        {
+            return(TNG_CRITICAL);
+        }
+    }
+
+    current_frame_pos = start_frame_nr - frame_set->first_frame;
+    /* It's not very elegant to reuse so much of the code in the different case
+     * statements, but it's unnecessarily slow to have the switch-case block
+     * inside the for loops. */
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    len = strlen(data->strings[current_frame_pos][j][k]) + 1;
+                    (*values)[i][mapping][k].c = malloc(len);
+                    strncpy((*values)[i][mapping][k].c, data->strings[current_frame_pos][j][k], len);
+                }
+            }
+            current_frame_pos++;
+        }
+        break;
+    case TNG_INT_DATA:
+        size = sizeof(int);
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    (*values)[i][mapping][k].i = *(int *)
+                                                 ((char *)data->values + size *
+                                                  (current_frame_pos *
+                                                   i_step + j *
+                                                   (*n_values_per_frame) + k));
+                }
+            }
+            current_frame_pos++;
+        }
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    (*values)[i][mapping][k].f = *(float *)
+                                                 ((char *)data->values + size *
+                                                  (current_frame_pos *
+                                                   i_step + j *
+                                                   (*n_values_per_frame) + k));
+                }
+            }
+            current_frame_pos++;
+        }
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+        i_step = (*n_particles) * (*n_values_per_frame);
+        for(i=0; i<n_frames; i++)
+        {
+            if(current_frame_pos == frame_set->n_frames)
+            {
+                stat = tng_frame_set_read_next(tng_data, hash_mode);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                current_frame_pos = 0;
+            }
+            for(j=*n_particles; j--;)
+            {
+                tng_particle_mapping_get_real_particle(frame_set, j, &mapping);
+                for(k=*n_values_per_frame; k--;)
+                {
+                    (*values)[i][mapping][k].d = *(double *)
+                                                 ((char *)data->values + size *
+                                                  (current_frame_pos *
+                                                   i_step + j *
+                                                   (*n_values_per_frame) + k));
+                }
+            }
+            current_frame_pos++;
+        }
+    }
+
+    data->last_retrieved_frame = end_frame_nr;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_vector_interval_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *n_particles,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    int64_t n_frames, tot_n_frames, n_frames_div, n_frames_div_2, first_frame;
+    int64_t file_pos, current_frame_pos, last_frame_pos, data_size, frame_size;
+    int size;
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t p_data;
+    tng_gen_block_t block;
+    void *current_values = 0, *temp;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(start_frame_nr <= end_frame_nr, "TNG library: start_frame_nr must not be higher than tne end_frame_nr.");
+    TNG_ASSERT(n_particles, "TNG library: n_particles must not be a NULL pointer.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer.");
+    TNG_ASSERT(n_values_per_frame, "TNG library: n_values_per_frame must not be a NULL pointer.");
+    TNG_ASSERT(type, "TNG library: type must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+    first_frame = frame_set->first_frame;
+
+    stat = tng_frame_set_of_frame_find(tng_data, start_frame_nr);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    /* Do not re-read the frame set and only need the requested block + particle mapping blocks. */
+    /* TODO: Test that blocks are read correctly now that now all of them are read at the same time. */
+    stat = tng_particle_data_find(tng_data, block_id, &p_data);
+    if(first_frame != frame_set->first_frame ||
+       stat != TNG_SUCCESS)
+    {
+        tng_block_init(&block);
+        if(stat != TNG_SUCCESS)
+        {
+            fseek(tng_data->input_file,
+                  (long)tng_data->current_trajectory_frame_set_input_file_pos,
+                  SEEK_SET);
+            stat = tng_block_header_read(tng_data, block);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot read block header. %s: %d\n",
+                        __FILE__, __LINE__);
+                return(stat);
+            }
+
+            fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+        }
+        file_pos = ftell(tng_data->input_file);
+        /* Read until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            if(block->id == block_id || block->id == TNG_PARTICLE_MAPPING)
+            {
+                stat = tng_block_read_next(tng_data, block,
+                                        hash_mode);
+                if(stat != TNG_CRITICAL)
+                {
+                    file_pos = ftell(tng_data->input_file);
+                    if(file_pos < tng_data->input_file_len)
+                    {
+                        stat = tng_block_header_read(tng_data, block);
+                    }
+                }
+            }
+            else
+            {
+                file_pos += block->block_contents_size + block->header_contents_size;
+                fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %"PRId64". %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+    stat = tng_particle_data_find(tng_data, block_id, &p_data);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    stat = tng_particle_data_vector_get(tng_data, block_id, &current_values,
+                                        &n_frames, stride_length, n_particles,
+                                        n_values_per_frame, type);
+
+    if(stat != TNG_SUCCESS || *n_particles == 0)
+    {
+        if(current_values)
+        {
+            free(current_values);
+        }
+        return(stat);
+    }
+
+    if(n_frames == 1 && n_frames < frame_set->n_frames)
+    {
+        tot_n_frames = 1;
+    }
+    else
+    {
+        tot_n_frames = end_frame_nr - start_frame_nr + 1;
+    }
+
+    switch(*type)
+    {
+    case TNG_CHAR_DATA:
+        return(TNG_FAILURE);
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    n_frames_div = (tot_n_frames % *stride_length) ?
+                 tot_n_frames / *stride_length + 1:
+                 tot_n_frames / *stride_length;
+
+    data_size = n_frames_div * size * (*n_particles) *
+                (*n_values_per_frame);
+
+    temp = realloc(*values, data_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               data_size, __FILE__, __LINE__);
+        free(*values);
+        *values = 0;
+        return(TNG_CRITICAL);
+    }
+
+    *values = temp;
+
+    if( n_frames == 1 && n_frames < frame_set->n_frames)
+    {
+        memcpy(*values, current_values, size * (*n_particles) *
+               (*n_values_per_frame));
+    }
+    else
+    {
+        current_frame_pos = start_frame_nr - frame_set->first_frame;
+
+        frame_size = size * (*n_particles) * (*n_values_per_frame);
+
+        last_frame_pos = tng_min_i64(n_frames,
+                                     end_frame_nr - start_frame_nr);
+
+        n_frames_div = current_frame_pos / *stride_length;
+        n_frames_div_2 = (last_frame_pos % *stride_length) ?
+                       last_frame_pos / *stride_length + 1:
+                       last_frame_pos / *stride_length;
+        n_frames_div_2 = tng_max_i64(1, n_frames_div_2 + 1);
+
+        memcpy(*values, (char *)current_values + n_frames_div * frame_size,
+               n_frames_div_2 * frame_size);
+
+        current_frame_pos += n_frames - current_frame_pos;
+
+        while(current_frame_pos <= end_frame_nr - start_frame_nr)
+        {
+            stat = tng_frame_set_read_next(tng_data, hash_mode);
+            if(stat != TNG_SUCCESS)
+            {
+                if(current_values)
+                {
+                    free(current_values);
+                }
+                free(*values);
+                *values = 0;
+                return(stat);
+            }
+
+            stat = tng_particle_data_vector_get(tng_data, block_id, &current_values,
+                                                &n_frames, stride_length, n_particles,
+                                                n_values_per_frame, type);
+
+            if(stat != TNG_SUCCESS)
+            {
+                if(current_values)
+                {
+                    free(current_values);
+                }
+                free(*values);
+                *values = 0;
+                return(stat);
+            }
+
+            last_frame_pos = tng_min_i64(n_frames,
+                                         end_frame_nr - current_frame_pos);
+
+            n_frames_div = current_frame_pos / *stride_length;
+            n_frames_div_2 = (last_frame_pos % *stride_length) ?
+                           last_frame_pos / *stride_length + 1:
+                           last_frame_pos / *stride_length;
+            n_frames_div_2 = tng_max_i64(1, n_frames_div_2);
+
+            memcpy(((char *)*values) + n_frames_div * frame_size,
+                   current_values,
+                   n_frames_div_2 * frame_size);
+
+            current_frame_pos += n_frames;
+        }
+    }
+
+    if(current_values)
+    {
+        free(current_values);
+    }
+
+    p_data->last_retrieved_frame = end_frame_nr;
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_get_stride_length
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int64_t frame,
+                 int64_t *stride_length)
+{
+    tng_function_status stat;
+    tng_non_particle_data_t np_data;
+    tng_particle_data_t p_data;
+    long file_pos;
+    int is_particle_data;
+
+    if(tng_data->current_trajectory_frame_set_input_file_pos <= 0)
+    {
+        frame = 0;
+    }
+
+    if(frame >= 0)
+    {
+        stat = tng_frame_set_of_frame_find(tng_data, frame);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+    }
+    stat = tng_data_find(tng_data, block_id, &np_data);
+    if(stat != TNG_SUCCESS)
+    {
+        stat = tng_particle_data_find(tng_data, block_id, &p_data);
+        if(stat != TNG_SUCCESS)
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            /* If no specific frame was required read until this data block is found */
+            if(frame < 0)
+            {
+                file_pos = ftell(tng_data->input_file);
+                while(stat != TNG_SUCCESS && file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_frame_set_read_next_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+                    file_pos = ftell(tng_data->input_file);
+                }
+            }
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+            stat = tng_data_find(tng_data, block_id, &np_data);
+            if(stat != TNG_SUCCESS)
+            {
+                stat = tng_particle_data_find(tng_data, block_id, &p_data);
+                if(stat != TNG_SUCCESS)
+                {
+                    return(stat);
+                }
+                else
+                {
+                    is_particle_data = 1;
+                }
+            }
+            else
+            {
+                is_particle_data = 0;
+            }
+        }
+        else
+        {
+            is_particle_data = 1;
+        }
+    }
+    else
+    {
+        is_particle_data = 0;
+    }
+    if(is_particle_data)
+    {
+        *stride_length = p_data->stride_length;
+    }
+    else
+    {
+        *stride_length = np_data->stride_length;
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_time_get_str
+                (const tng_trajectory_t tng_data,
+                 char *time)
+{
+    struct tm *time_data;
+    time_t secs;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(time, "TNG library: time must not be a NULL pointer");
+
+    secs = tng_data->time;
+
+    time_data = localtime(&secs); /* Returns a statically allocated variable. */
+    TNG_SNPRINTF(time, TNG_MAX_DATE_STR_LEN,
+             "%4d-%02d-%02d %02d:%02d:%02d",
+             time_data->tm_year+1900, time_data->tm_mon+1, time_data->tm_mday,
+             time_data->tm_hour, time_data->tm_min, time_data->tm_sec);
+
+    return(TNG_SUCCESS);
+}
+
+
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_open
+                (const char *filename,
+                 const char mode,
+                 tng_trajectory_t *tng_data_p)
+{
+    tng_function_status stat;
+
+    TNG_ASSERT(filename, "TNG library: filename must not be a NULL pointer.");
+
+    if(mode != 'r' && mode != 'w' && mode != 'a')
+    {
+        return(TNG_FAILURE);
+    }
+
+    if(tng_trajectory_init(tng_data_p) != TNG_SUCCESS)
+    {
+        tng_trajectory_destroy(tng_data_p);
+        return(TNG_CRITICAL);
+    }
+
+    if(mode == 'r' || mode == 'a')
+    {
+        tng_input_file_set(*tng_data_p, filename);
+
+        /* Read the file headers */
+        tng_file_headers_read(*tng_data_p, TNG_USE_HASH);
+
+        tng_num_frame_sets_get(*tng_data_p, &(*tng_data_p)->n_trajectory_frame_sets);
+    }
+
+    if(mode == 'w')
+    {
+        tng_output_file_set(*tng_data_p, filename);
+    }
+    else if(mode == 'a')
+    {
+        fseek((*tng_data_p)->input_file,
+                (long)(*tng_data_p)->last_trajectory_frame_set_input_file_pos,
+                SEEK_SET);
+
+        stat = tng_frame_set_read(*tng_data_p, TNG_USE_HASH);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read frame set and related blocks. %s: %d\n",
+                   __FILE__, __LINE__);
+        }
+
+        (*tng_data_p)->first_trajectory_frame_set_output_file_pos =
+        (*tng_data_p)->first_trajectory_frame_set_input_file_pos;
+        (*tng_data_p)->last_trajectory_frame_set_output_file_pos =
+        (*tng_data_p)->last_trajectory_frame_set_input_file_pos;
+        (*tng_data_p)->current_trajectory_frame_set_output_file_pos =
+        (*tng_data_p)->current_trajectory_frame_set_input_file_pos;
+        (*tng_data_p)->first_trajectory_frame_set_input_file_pos = -1;
+        (*tng_data_p)->last_trajectory_frame_set_input_file_pos = -1;
+        (*tng_data_p)->current_trajectory_frame_set_input_file_pos = -1;
+        if((*tng_data_p)->input_file)
+        {
+            fclose((*tng_data_p)->input_file);
+            (*tng_data_p)->input_file = 0;
+        }
+        if((*tng_data_p)->input_file_path)
+        {
+            free((*tng_data_p)->input_file_path);
+            (*tng_data_p)->input_file_path = 0;
+        }
+        tng_output_append_file_set(*tng_data_p, filename);
+
+        fseek((*tng_data_p)->output_file, 0, SEEK_END);
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_close
+                (tng_trajectory_t *tng_data_p)
+{
+    tng_trajectory_frame_set_t frame_set;
+
+    if(tng_data_p == 0)
+    {
+        fprintf(stderr, "TNG library: Empty pointer to trajectory when attempting to close. %s: %d\n",
+               __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    if(*tng_data_p == 0)
+    {
+        return(TNG_SUCCESS);
+    }
+
+    frame_set = &(*tng_data_p)->current_trajectory_frame_set;
+
+    if(frame_set->n_unwritten_frames > 0)
+    {
+        frame_set->n_frames = frame_set->n_unwritten_frames;
+        tng_frame_set_write(*tng_data_p, TNG_USE_HASH);
+    }
+
+    return(tng_trajectory_destroy(tng_data_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_time_of_frame_get
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 double *time)
+{
+    int64_t first_frame;
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(time, "TNG library: time must not be a NULL pointer");
+
+    stat = tng_frame_set_of_frame_find(tng_data, frame_nr);
+    if(stat != TNG_SUCCESS)
+    {
+        fprintf(stderr, "TNG library: Cannot find frame nr %"PRId64". %s: %d\n",
+               frame_nr, __FILE__, __LINE__);
+        return(stat);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+    first_frame = frame_set->first_frame;
+
+    if(tng_data->time_per_frame <= 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    *time = frame_set->first_frame_time + (tng_data->time_per_frame * (frame_nr - first_frame));
+
+    return(TNG_SUCCESS);
+}
+
+/*
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_molecules_get
+                (tng_trajectory_t tng_data,
+                 int64_t *n_mols,
+                 int64_t **molecule_cnt_list,
+                 tng_molecule_t *mols)
+{
+    tng_trajectory_frame_set_t frame_set;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_mols, "TNG library: n_mols must not be a NULL pointer.");
+
+    *n_mols = tng_data->n_molecules;
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+    if(tng_data->var_num_atoms_flag && frame_set && frame_set->molecule_cnt_list)
+    {
+        *molecule_cnt_list = frame_set->molecule_cnt_list;
+    }
+    else
+    {
+        *molecule_cnt_list = tng_data->molecule_cnt_list;
+    }
+
+    *mols = tng_data->molecules;
+
+    return(TNG_SUCCESS);
+}
+*/
+/*
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_molecule_add
+                (tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t cnt,
+                 tng_molecule_t *mol)
+{
+    tng_function_status stat;
+
+    TNG_ASSERT(name, "TNG library: name must not be a NULL pointer");
+    TNG_ASSERT(cnt>=0, "TNG library: cnt must be >= 0");
+
+    stat = tng_molecule_add(tng_data, name, mol);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+    stat = tng_molecule_cnt_set(tng_data, *mol, cnt);
+
+    return(stat);
+}
+*/
+tng_function_status DECLSPECDLLEXPORT tng_util_molecule_particles_get
+                (tng_trajectory_t tng_data,
+                 const tng_molecule_t mol,
+                 int64_t *n_particles,
+                 char ***names,
+                 char ***types,
+                 char ***res_names,
+                 int64_t **res_ids,
+                 char ***chain_names,
+                 int64_t **chain_ids)
+{
+    tng_atom_t atom;
+    tng_residue_t res;
+    tng_chain_t chain;
+    int64_t i;
+    (void)tng_data;
+
+    *n_particles = mol->n_atoms;
+
+    *names = malloc(sizeof(char *) * *n_particles);
+    *types = malloc(sizeof(char *) * *n_particles);
+    *res_names = malloc(sizeof(char *) * *n_particles);
+    *chain_names = malloc(sizeof(char *) * *n_particles);
+    *res_ids = malloc(sizeof(int64_t) * *n_particles);
+    *chain_ids = malloc(sizeof(int64_t) * *n_particles);
+
+    for(i = 0; i < *n_particles; i++)
+    {
+        atom = &mol->atoms[i];
+        res = atom->residue;
+        chain = res->chain;
+        (*names)[i] = malloc(strlen(atom->name));
+        strcpy(*names[i], atom->name);
+        (*types)[i] = malloc(strlen(atom->atom_type));
+        strcpy(*types[i], atom->atom_type);
+        (*res_names)[i] = malloc(strlen(res->name));
+        strcpy(*res_names[i], res->name);
+        (*chain_names)[i] = malloc(strlen(chain->name));
+        strcpy(*chain_names[i], chain->name);
+        (*res_ids)[i] = res->id;
+        (*chain_ids)[i] = chain->id;
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_molecule_particles_set
+                (tng_trajectory_t tng_data,
+                 tng_molecule_t mol,
+                 const int64_t n_particles,
+                 const char **names,
+                 const char **types,
+                 const char **res_names,
+                 const int64_t *res_ids,
+                 const char **chain_names,
+                 const int64_t *chain_ids)
+{
+    int64_t i;
+    tng_chain_t chain;
+    tng_residue_t residue;
+    tng_atom_t atom;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(names, "TNG library: names must not be a NULL pointer");
+    TNG_ASSERT(types, "TNG library: types must not be a NULL pointer");
+    TNG_ASSERT(res_names, "TNG library: res_names must not be a NULL pointer");
+    TNG_ASSERT(res_ids, "TNG library: res_ids must not be a NULL pointer");
+    TNG_ASSERT(chain_names, "TNG library: chain_names must not be a NULL pointer");
+    TNG_ASSERT(chain_ids, "TNG library: chain_ids must not be a NULL pointer");
+
+    for(i = 0; i < n_particles; i++)
+    {
+        if(tng_molecule_chain_find(tng_data, mol, chain_names[i], chain_ids[i],
+           &chain) == TNG_FAILURE)
+        {
+            stat = tng_molecule_chain_add(tng_data, mol, chain_names[i],
+                                          &chain);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+        }
+        if(tng_chain_residue_find(tng_data, chain, res_names[i], res_ids[i],
+           &residue) == TNG_FAILURE)
+        {
+            stat = tng_chain_residue_add(tng_data, chain, res_names[i],
+                                         &residue);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+        }
+        stat = tng_residue_atom_add(tng_data, residue, names[i], types[i], &atom);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read
+                (tng_trajectory_t tng_data,
+                 float **positions, int64_t *stride_length)
+{
+    int64_t n_frames, n_particles, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(positions, "TNG library: positions must not be a NULL pointer");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_num_frames_get(tng_data, &n_frames);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    stat = tng_particle_data_vector_interval_get(tng_data, TNG_TRAJ_POSITIONS,
+                                                 0, n_frames - 1, TNG_USE_HASH,
+                                                 (void **)positions,
+                                                 &n_particles,
+                                                 stride_length,
+                                                 &n_values_per_frame,
+                                                 &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read
+                (tng_trajectory_t tng_data,
+                 float **velocities, int64_t *stride_length)
+{
+    int64_t n_frames, n_particles, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(velocities, "TNG library: velocities must not be a NULL pointer");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_num_frames_get(tng_data, &n_frames);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    stat = tng_particle_data_vector_interval_get(tng_data, TNG_TRAJ_VELOCITIES,
+                                                 0, n_frames - 1, TNG_USE_HASH,
+                                                 (void **)velocities,
+                                                 &n_particles,
+                                                 stride_length,
+                                                 &n_values_per_frame,
+                                                 &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read
+                (tng_trajectory_t tng_data,
+                 float **forces, int64_t *stride_length)
+{
+    int64_t n_frames, n_particles, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(forces, "TNG library: forces must not be a NULL pointer");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_num_frames_get(tng_data, &n_frames);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    stat = tng_particle_data_vector_interval_get(tng_data, TNG_TRAJ_FORCES,
+                                                 0, n_frames - 1, TNG_USE_HASH,
+                                                 (void **)forces,
+                                                 &n_particles,
+                                                 stride_length,
+                                                 &n_values_per_frame,
+                                                 &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read
+                (tng_trajectory_t tng_data,
+                 float **box_shape,
+                 int64_t *stride_length)
+{
+    int64_t n_frames, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(box_shape, "TNG library: box_shape must not be a NULL pointer");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_num_frames_get(tng_data, &n_frames);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    stat = tng_data_vector_interval_get(tng_data, TNG_TRAJ_BOX_SHAPE,
+                                        0, n_frames - 1, TNG_USE_HASH,
+                                        (void **)box_shape,
+                                        stride_length,
+                                        &n_values_per_frame,
+                                        &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_particle_data_next_frame_read
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 char *data_type,
+                 int64_t *retrieved_frame_number,
+                 double *retrieved_time)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t data;
+    tng_function_status stat;
+    int size;
+    int64_t i, data_size, n_particles;
+    void *temp;
+    long file_pos;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(values, "TNG library: The pointer to the values array must not be a NULL pointer");
+    TNG_ASSERT(data_type, "TNG library: The pointer to the data type of the returned data must not be a NULL pointer");
+    TNG_ASSERT(retrieved_frame_number, "TNG library: The pointer to the frame number of the returned data must not be a NULL pointer");
+    TNG_ASSERT(retrieved_time, "TNG library: The pointer to the time of the returned data must not be a NULL pointer");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    stat = tng_particle_data_find(tng_data, block_id, &data);
+    if(stat != TNG_SUCCESS)
+    {
+        stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+        file_pos = ftell(tng_data->input_file);
+        while(stat != TNG_SUCCESS && file_pos < tng_data->input_file_len)
+        {
+            stat = tng_frame_set_read_next_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            file_pos = ftell(tng_data->input_file);
+        }
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_particle_data_find(tng_data, block_id, &data);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+    }
+    if(data->last_retrieved_frame < 0)
+    {
+        fseek(tng_data->input_file,
+              (long)tng_data->first_trajectory_frame_set_input_file_pos,
+              SEEK_SET);
+        stat = tng_frame_set_read(tng_data, TNG_USE_HASH);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+
+        i = data->first_frame_with_data;
+    }
+    else
+    {
+        i = data->last_retrieved_frame + data->stride_length;
+        if(i < frame_set->first_frame || i >= frame_set->first_frame + frame_set->n_frames)
+        {
+            stat = tng_frame_set_of_frame_find(tng_data, i);
+            if(stat != TNG_SUCCESS)
+            {
+                /* If the frame set search found the frame set after the starting
+                 * frame set there is a gap in the frame sets. So, even if the frame
+                 * was not found the next frame with data is still in the found
+                 * frame set. */
+                if(stat == TNG_CRITICAL)
+                {
+                    return(stat);
+                }
+                i = frame_set->first_frame;
+            }
+        }
+        if(data->last_retrieved_frame < frame_set->first_frame)
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+        }
+    }
+    data->last_retrieved_frame = i;
+    *retrieved_frame_number = i;
+    if(frame_set->first_frame_time >= 0 && tng_data->time_per_frame >= 0)
+    {
+        *retrieved_time = frame_set->first_frame_time +
+                        (i - frame_set->first_frame) *
+                        tng_data->time_per_frame;
+    }
+    else
+    {
+        *retrieved_time = 0;
+    }
+
+    if(data->stride_length > 1)
+    {
+        i = (i - data->first_frame_with_data) / data->stride_length;
+    }
+    else
+    {
+        i = (i - frame_set->first_frame);
+    }
+
+    tng_num_particles_get(tng_data, &n_particles);
+
+    *data_type = data->datatype;
+
+    switch(*data_type)
+    {
+    case TNG_CHAR_DATA:
+        return(TNG_FAILURE);
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    data_size = size * n_particles * data->n_values_per_frame;
+
+//     fprintf(stderr, "TNG library: TEMP: i = %"PRId64", data_size = %"PRId64", size = %d, n_particles = %"PRId64", n_values_per_frame = %"PRId64"\n",
+//            i, data_size, size, n_particles, data->n_values_per_frame);
+
+    temp = realloc(*values, data_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               data_size, __FILE__, __LINE__);
+        free(*values);
+        *values = 0;
+        return(TNG_CRITICAL);
+    }
+
+    *values = temp;
+
+    memcpy(*values, (char *)data->values + i * data_size, data_size);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_non_particle_data_next_frame_read
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 char *data_type,
+                 int64_t *retrieved_frame_number,
+                 double *retrieved_time)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_non_particle_data_t data;
+    tng_function_status stat;
+    int size;
+    int64_t i, data_size;
+    void *temp;
+    long file_pos;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(values, "TNG library: The pointer to the values array must not be a NULL pointer");
+    TNG_ASSERT(data_type, "TNG library: The pointer to the data type of the returned data must not be a NULL pointer");
+    TNG_ASSERT(retrieved_frame_number, "TNG library: The pointer to the frame number of the returned data must not be a NULL pointer");
+    TNG_ASSERT(retrieved_time, "TNG library: The pointer to the time of the returned data must not be a NULL pointer");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    stat = tng_data_find(tng_data, block_id, &data);
+    if(stat != TNG_SUCCESS)
+    {
+        stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+        file_pos = ftell(tng_data->input_file);
+        while(stat != TNG_SUCCESS && file_pos < tng_data->input_file_len)
+        {
+            stat = tng_frame_set_read_next_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            file_pos = ftell(tng_data->input_file);
+        }
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_data_find(tng_data, block_id, &data);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+    }
+    if(data->last_retrieved_frame < 0)
+    {
+        fseek(tng_data->input_file,
+                (long)tng_data->first_trajectory_frame_set_input_file_pos,
+                SEEK_SET);
+        stat = tng_frame_set_read(tng_data, TNG_USE_HASH);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+
+        i = data->first_frame_with_data;
+    }
+    else
+    {
+        i = data->last_retrieved_frame + data->stride_length;
+        if(i < frame_set->first_frame || i >= frame_set->first_frame + frame_set->n_frames)
+        {
+            stat = tng_frame_set_of_frame_find(tng_data, i);
+            if(stat != TNG_SUCCESS)
+            {
+                /* If the frame set search found the frame set after the starting
+                 * frame set there is a gap in the frame sets. So, even if the frame
+                 * was not found the next frame with data is still in the found
+                 * frame set. */
+                if(stat == TNG_CRITICAL)
+                {
+                    return(stat);
+                }
+                i = frame_set->first_frame;
+            }
+        }
+        if(data->last_retrieved_frame < frame_set->first_frame)
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+        }
+    }
+    data->last_retrieved_frame = i;
+    *retrieved_frame_number = i;
+    if(frame_set->first_frame_time >= 0 && tng_data->time_per_frame >= 0)
+    {
+        *retrieved_time = frame_set->first_frame_time +
+                        (i - frame_set->first_frame) *
+                        tng_data->time_per_frame;
+    }
+    else
+    {
+        *retrieved_time = 0;
+    }
+
+    if(data->stride_length > 1)
+    {
+        i = (i - data->first_frame_with_data) / data->stride_length;
+    }
+    else
+    {
+        i = (i - frame_set->first_frame);
+    }
+
+    *data_type = data->datatype;
+
+    switch(*data_type)
+    {
+    case TNG_CHAR_DATA:
+        return(TNG_FAILURE);
+    case TNG_INT_DATA:
+        size = sizeof(int64_t);
+        break;
+    case TNG_FLOAT_DATA:
+        size = sizeof(float);
+        break;
+    case TNG_DOUBLE_DATA:
+    default:
+        size = sizeof(double);
+    }
+
+    data_size = size * data->n_values_per_frame;
+
+    temp = realloc(*values, data_size);
+    if(!temp)
+    {
+        fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+               data_size, __FILE__, __LINE__);
+        free(*values);
+        *values = 0;
+        return(TNG_CRITICAL);
+    }
+
+    *values = temp;
+
+    memcpy(*values, (char *)data->values + i * data_size, data_size);
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **positions,
+                 int64_t *stride_length)
+{
+    int64_t n_particles, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(positions, "TNG library: positions must not be a NULL pointer");
+    TNG_ASSERT(first_frame <= last_frame, "TNG library: first_frame must be lower or equal to last_frame.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_particle_data_vector_interval_get(tng_data, TNG_TRAJ_POSITIONS,
+                                                 first_frame, last_frame,
+                                                 TNG_USE_HASH,
+                                                 (void **)positions,
+                                                 &n_particles,
+                                                 stride_length,
+                                                 &n_values_per_frame,
+                                                 &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **velocities,
+                 int64_t *stride_length)
+{
+    int64_t n_particles, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(velocities, "TNG library: velocities must not be a NULL pointer");
+    TNG_ASSERT(first_frame <= last_frame, "TNG library: first_frame must be lower or equal to last_frame.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_particle_data_vector_interval_get(tng_data, TNG_TRAJ_VELOCITIES,
+                                                 first_frame, last_frame,
+                                                 TNG_USE_HASH,
+                                                 (void **)velocities,
+                                                 &n_particles,
+                                                 stride_length,
+                                                 &n_values_per_frame,
+                                                 &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **forces,
+                 int64_t *stride_length)
+{
+    int64_t n_particles, n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(forces, "TNG library: forces must not be a NULL pointer");
+    TNG_ASSERT(first_frame <= last_frame, "TNG library: first_frame must be lower or equal to last_frame.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_particle_data_vector_interval_get(tng_data, TNG_TRAJ_FORCES,
+                                                 first_frame, last_frame,
+                                                 TNG_USE_HASH,
+                                                 (void **)forces,
+                                                 &n_particles,
+                                                 stride_length,
+                                                 &n_values_per_frame,
+                                                 &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read_range
+                (tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **box_shape,
+                 int64_t *stride_length)
+{
+    int64_t n_values_per_frame;
+    char type;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(box_shape, "TNG library: box_shape must not be a NULL pointer");
+    TNG_ASSERT(first_frame <= last_frame, "TNG library: first_frame must be lower or equal to last_frame.");
+    TNG_ASSERT(stride_length, "TNG library: stride_length must not be a NULL pointer");
+
+    stat = tng_data_vector_interval_get(tng_data, TNG_TRAJ_BOX_SHAPE,
+                                        first_frame, last_frame,
+                                        TNG_USE_HASH,
+                                        (void **)box_shape,
+                                        stride_length,
+                                        &n_values_per_frame,
+                                        &type);
+
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    int64_t n_particles, n_frames;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i >= 0, "TNG library: i (writing interval) must be >= 0.");
+
+    if(i <= 0)
+    {
+        fprintf(stderr, "TNG library: Cannot set writing frequency to %"PRId64". %s: %d\n",
+               i, __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(!frame_set || tng_data->n_trajectory_frame_sets <= 0)
+    {
+        n_frames = tng_data->frame_set_n_frames;
+
+        stat = tng_frame_set_new(tng_data, 0, n_frames);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create frame set.  %s: %d\n", __FILE__,
+                __LINE__);
+            return(stat);
+        }
+    }
+    else
+    {
+        n_frames = frame_set->n_frames;
+    }
+
+    if(particle_dependency == TNG_PARTICLE_BLOCK_DATA)
+    {
+        tng_num_particles_get(tng_data, &n_particles);
+        if(n_particles <= 0)
+        {
+            return(TNG_FAILURE);
+        }
+
+        if(tng_particle_data_find(tng_data, block_id, &p_data)
+        != TNG_SUCCESS)
+        {
+            stat = tng_particle_data_block_add(tng_data, block_id,
+                                               block_name,
+                                               TNG_FLOAT_DATA,
+                                               TNG_TRAJECTORY_BLOCK,
+                                               n_frames, n_values_per_frame, i,
+                                               0, n_particles,
+                                               compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            p_data = &frame_set->tr_particle_data[frame_set->
+                                                  n_particle_data_blocks - 1];
+            stat = tng_allocate_particle_data_mem(tng_data, p_data, n_frames,
+                                                  i, n_particles,
+                                                  n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+        else
+        {
+            if(p_data->stride_length != i)
+            {
+                p_data->stride_length = i;
+                stat = tng_allocate_particle_data_mem(tng_data, p_data, n_frames,
+                                                      i, n_particles,
+                                                      n_values_per_frame);
+                if(stat != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(stat);
+                }
+            }
+        }
+    }
+    else
+    {
+        if(tng_data_find(tng_data, block_id, &np_data) != TNG_SUCCESS)
+        {
+            stat = tng_data_block_add(tng_data, block_id, block_name,
+                                      TNG_FLOAT_DATA, TNG_TRAJECTORY_BLOCK,
+                                      n_frames, n_values_per_frame,
+                                      i, compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            np_data = &frame_set->tr_data[frame_set->
+                                          n_data_blocks - 1];
+            stat = tng_allocate_data_mem(tng_data, np_data, n_frames,
+                                         i, n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+        else
+        {
+            if(np_data->stride_length != i)
+            {
+                np_data->stride_length = i;
+                stat = tng_allocate_data_mem(tng_data, np_data, n_frames,
+                                             i, n_values_per_frame);
+                if(stat != TNG_SUCCESS)
+                {
+                    fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                           __FILE__, __LINE__);
+                    return(stat);
+                }
+            }
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    int64_t n_particles, n_frames;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i >= 0, "TNG library: i (writing interval) must be >= 0.");
+
+    if(i <= 0)
+    {
+        fprintf(stderr, "TNG library: Cannot set writing frequency to %"PRId64". %s: %d\n",
+               i, __FILE__, __LINE__);
+        return(TNG_FAILURE);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(!frame_set || tng_data->n_trajectory_frame_sets <= 0)
+    {
+        n_frames = tng_data->frame_set_n_frames;
+
+        stat = tng_frame_set_new(tng_data, 0, n_frames);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot create frame set.  %s: %d\n", __FILE__,
+                __LINE__);
+            return(stat);
+        }
+    }
+    else
+    {
+        n_frames = frame_set->n_frames;
+    }
+
+    if(particle_dependency == TNG_PARTICLE_BLOCK_DATA)
+    {
+        tng_num_particles_get(tng_data, &n_particles);
+
+        if(n_particles <= 0)
+        {
+            return(TNG_FAILURE);
+        }
+
+        if(tng_particle_data_find(tng_data, block_id, &p_data)
+        != TNG_SUCCESS)
+        {
+            stat = tng_particle_data_block_add(tng_data, block_id,
+                                            block_name,
+                                            TNG_DOUBLE_DATA,
+                                            TNG_TRAJECTORY_BLOCK,
+                                            n_frames, n_values_per_frame, i,
+                                            0, n_particles,
+                                            compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            p_data = &frame_set->tr_particle_data[frame_set->
+                                                  n_particle_data_blocks - 1];
+            stat = tng_allocate_particle_data_mem(tng_data, p_data, n_frames,
+                                                  i, n_particles,
+                                                  n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+        else
+        {
+            p_data->stride_length = i;
+        }
+    }
+    else
+    {
+        if(tng_data_find(tng_data, block_id, &np_data) != TNG_SUCCESS)
+        {
+            stat = tng_data_block_add(tng_data, block_id, block_name,
+                                      TNG_DOUBLE_DATA, TNG_TRAJECTORY_BLOCK,
+                                      n_frames, n_values_per_frame,
+                                      i, compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            np_data = &frame_set->tr_data[frame_set->
+                                          n_data_blocks - 1];
+            stat = tng_allocate_data_mem(tng_data, np_data, n_frames,
+                                         i, n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+        else
+        {
+            np_data->stride_length = i;
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    fprintf(stderr, "TNG library: Using obsolete function tng_util_generic_write_frequency_set(). "
+           "See documentation. %s: %d", __FILE__, __LINE__);
+    return(tng_util_generic_write_interval_set(tng_data, i, n_values_per_frame,
+                                               block_id, block_name,
+                                               particle_dependency,
+                                               compression));
+}
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_set(tng_data, i, 3,
+                                               TNG_TRAJ_POSITIONS,
+                                               "POSITIONS",
+                                               TNG_PARTICLE_BLOCK_DATA,
+                                               TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_double_set(tng_data, i, 3,
+                                                      TNG_TRAJ_POSITIONS,
+                                                      "POSITIONS",
+                                                      TNG_PARTICLE_BLOCK_DATA,
+                                                      TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    fprintf(stderr, "TNG library: Using obsolete function tng_util_pos_write_frequency_set(). "
+           "See documentation. %s: %d", __FILE__, __LINE__);
+    return(tng_util_pos_write_interval_set(tng_data, i));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_set(tng_data, i, 3,
+                                               TNG_TRAJ_VELOCITIES,
+                                               "VELOCITIES",
+                                               TNG_PARTICLE_BLOCK_DATA,
+                                               TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_double_set(tng_data, i, 3,
+                                                      TNG_TRAJ_VELOCITIES,
+                                                      "VELOCITIES",
+                                                      TNG_PARTICLE_BLOCK_DATA,
+                                                      TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    fprintf(stderr, "TNG library: Using obsolete function tng_util_vel_write_frequency_set(). "
+           "See documentation. %s: %d", __FILE__, __LINE__);
+    return(tng_util_vel_write_interval_set(tng_data, i));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_set(tng_data, i, 3,
+                                               TNG_TRAJ_FORCES,
+                                               "FORCES",
+                                               TNG_PARTICLE_BLOCK_DATA,
+                                               TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_double_set(tng_data, i, 3,
+                                                      TNG_TRAJ_FORCES,
+                                                      "FORCES",
+                                                      TNG_PARTICLE_BLOCK_DATA,
+                                                      TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    fprintf(stderr, "TNG library: Using obsolete function tng_util_force_write_frequency_set(). "
+           "See documentation. %s: %d", __FILE__, __LINE__);
+    return(tng_util_force_write_interval_set(tng_data, i));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_interval_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_set(tng_data, i, 9,
+                                               TNG_TRAJ_BOX_SHAPE,
+                                               "BOX SHAPE",
+                                               TNG_NON_PARTICLE_BLOCK_DATA,
+                                               TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_interval_double_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(i > 0, "TNG library: i (writing interval) must be >= 0.");
+
+    return(tng_util_generic_write_interval_double_set(tng_data, i, 9,
+                                                      TNG_TRAJ_BOX_SHAPE,
+                                                      "BOX SHAPE",
+                                                      TNG_NON_PARTICLE_BLOCK_DATA,
+                                                      TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_frequency_set
+                (tng_trajectory_t tng_data,
+                 const int64_t i)
+{
+    fprintf(stderr, "TNG library: Using obsolete function tng_util_box_shape_write_frequency_set(). "
+           "See documentation. %s: %d", __FILE__, __LINE__);
+    return(tng_util_box_shape_write_interval_set(tng_data, i));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    int64_t n_particles, n_frames, stride_length = 100, frame_pos;
+    int64_t last_frame;
+    int is_first_frame_flag = 0;
+    char block_type_flag;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(values, "TNG library: values must not be a NULL pointer");
+
+    if(particle_dependency == TNG_PARTICLE_BLOCK_DATA)
+    {
+        tng_num_particles_get(tng_data, &n_particles);
+        TNG_ASSERT(n_particles > 0, "TNG library: There must be particles in the system to write particle data.");
+    }
+
+    if(values == 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(frame_nr < 0)
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+        n_frames = stride_length = 1;
+    }
+    else
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+
+        if(!frame_set || tng_data->n_trajectory_frame_sets <= 0)
+        {
+            stat = tng_frame_set_new(tng_data, 0, tng_data->frame_set_n_frames);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot create frame set.  %s: %d\n", __FILE__,
+                    __LINE__);
+                return(stat);
+            }
+        }
+        last_frame = frame_set->first_frame +
+                     frame_set->n_frames - 1;
+        if(frame_nr > last_frame)
+        {
+            stat = tng_frame_set_write(tng_data, TNG_USE_HASH);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot write frame set.  %s: %d\n", __FILE__,
+                    __LINE__);
+                return(stat);
+            }
+            if(last_frame + tng_data->frame_set_n_frames < frame_nr)
+            {
+                last_frame = frame_nr - 1;
+            }
+            stat = tng_frame_set_new(tng_data, last_frame + 1,
+                                     tng_data->frame_set_n_frames);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot create frame set.  %s: %d\n", __FILE__,
+                    __LINE__);
+                return(stat);
+            }
+        }
+        if(frame_set->n_unwritten_frames == 0)
+        {
+            is_first_frame_flag = 1;
+        }
+        frame_set->n_unwritten_frames = frame_nr -
+                                        frame_set->first_frame + 1;
+
+        n_frames = frame_set->n_frames;
+    }
+
+    if(particle_dependency == TNG_PARTICLE_BLOCK_DATA)
+    {
+        if(tng_particle_data_find(tng_data, block_id, &p_data)
+        != TNG_SUCCESS)
+        {
+            stat = tng_particle_data_block_add(tng_data, block_id,
+                                               block_name,
+                                               TNG_FLOAT_DATA,
+                                               block_type_flag,
+                                               n_frames, n_values_per_frame,
+                                               stride_length,
+                                               0, n_particles,
+                                               compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+            {
+                p_data = &frame_set->tr_particle_data[frame_set->
+                                                    n_particle_data_blocks - 1];
+            }
+            else
+            {
+                p_data = &tng_data->non_tr_particle_data[tng_data->
+                                                    n_particle_data_blocks - 1];
+            }
+            stat = tng_allocate_particle_data_mem(tng_data, p_data, n_frames,
+                                                  stride_length, n_particles,
+                                                  n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            stride_length = p_data->stride_length;
+
+            if(is_first_frame_flag || p_data->first_frame_with_data < frame_set->first_frame)
+            {
+                p_data->first_frame_with_data = frame_nr;
+                frame_pos = 0;
+            }
+            else
+            {
+                frame_pos = (frame_nr - frame_set->first_frame) / stride_length;
+            }
+
+            memcpy((char *)p_data->values + sizeof(float) * frame_pos * n_particles *
+                   n_values_per_frame, values, sizeof(float) *
+                   n_particles * n_values_per_frame);
+        }
+        else
+        {
+            memcpy(p_data->values, values, sizeof(float) * n_particles *
+                   n_values_per_frame);
+        }
+    }
+    else
+    {
+        if(tng_data_find(tng_data, block_id, &np_data) != TNG_SUCCESS)
+        {
+            stat = tng_data_block_add(tng_data, block_id, block_name,
+                                      TNG_FLOAT_DATA, block_type_flag,
+                                      n_frames, n_values_per_frame,
+                                      stride_length, compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+            {
+                np_data = &frame_set->tr_data[frame_set->
+                                              n_data_blocks - 1];
+            }
+            else
+            {
+                np_data = &tng_data->non_tr_data[tng_data->
+                                                 n_data_blocks - 1];
+            }
+            stat = tng_allocate_data_mem(tng_data, np_data, n_frames,
+                                         stride_length, n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            stride_length = np_data->stride_length;
+
+            if(is_first_frame_flag || np_data->first_frame_with_data < frame_set->first_frame)
+            {
+                np_data->first_frame_with_data = frame_nr;
+                frame_pos = 0;
+            }
+            else
+            {
+                frame_pos = (frame_nr - frame_set->first_frame) / stride_length;
+            }
+
+            memcpy((char *)np_data->values + sizeof(float) * frame_pos *
+                   n_values_per_frame, values, sizeof(float) *
+                   n_values_per_frame);
+        }
+        else
+        {
+            memcpy(np_data->values, values, sizeof(float) * n_values_per_frame);
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    int64_t n_particles, n_frames, stride_length = 100, frame_pos;
+    int64_t last_frame;
+    int is_first_frame_flag = 0;
+    char block_type_flag;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(values, "TNG library: values must not be a NULL pointer");
+
+    if(particle_dependency == TNG_PARTICLE_BLOCK_DATA)
+    {
+        tng_num_particles_get(tng_data, &n_particles);
+        TNG_ASSERT(n_particles > 0, "TNG library: There must be particles in the system to write particle data.");
+    }
+
+    if(values == 0)
+    {
+        return(TNG_FAILURE);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    if(frame_nr < 0)
+    {
+        block_type_flag = TNG_NON_TRAJECTORY_BLOCK;
+        n_frames = stride_length = 1;
+    }
+    else
+    {
+        block_type_flag = TNG_TRAJECTORY_BLOCK;
+
+        n_frames = tng_data->frame_set_n_frames;
+
+        if(!frame_set || tng_data->n_trajectory_frame_sets <= 0)
+        {
+            stat = tng_frame_set_new(tng_data, 0, n_frames);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot create frame set.  %s: %d\n", __FILE__,
+                    __LINE__);
+                return(stat);
+            }
+        }
+        else
+        {
+            n_frames = frame_set->n_frames;
+        }
+        last_frame = frame_set->first_frame +
+                     frame_set->n_frames - 1;
+        if(frame_nr > last_frame)
+        {
+            stat = tng_frame_set_write(tng_data, TNG_USE_HASH);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot write frame set.  %s: %d\n", __FILE__,
+                    __LINE__);
+                return(stat);
+            }
+            if(last_frame + tng_data->frame_set_n_frames < frame_nr)
+            {
+                last_frame = frame_nr - 1;
+            }
+            stat = tng_frame_set_new(tng_data, last_frame + 1, n_frames);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Cannot create frame set.  %s: %d\n", __FILE__,
+                    __LINE__);
+                return(stat);
+            }
+        }
+        if(frame_set->n_unwritten_frames == 0)
+        {
+            is_first_frame_flag = 1;
+        }
+        frame_set->n_unwritten_frames = frame_nr -
+                                        frame_set->first_frame + 1;
+    }
+
+    if(particle_dependency == TNG_PARTICLE_BLOCK_DATA)
+    {
+        if(tng_particle_data_find(tng_data, block_id, &p_data)
+        != TNG_SUCCESS)
+        {
+            stat = tng_particle_data_block_add(tng_data, block_id,
+                                            block_name,
+                                            TNG_DOUBLE_DATA,
+                                            block_type_flag,
+                                            n_frames, n_values_per_frame,
+                                            stride_length,
+                                            0, n_particles,
+                                            compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+            {
+                p_data = &frame_set->tr_particle_data[frame_set->
+                                                    n_particle_data_blocks - 1];
+            }
+            else
+            {
+                p_data = &tng_data->non_tr_particle_data[tng_data->
+                                                    n_particle_data_blocks - 1];
+            }
+            stat = tng_allocate_particle_data_mem(tng_data, p_data, n_frames,
+                                                  stride_length, n_particles,
+                                                  n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            stride_length = p_data->stride_length;
+
+            if(is_first_frame_flag)
+            {
+                p_data->first_frame_with_data = frame_nr;
+                frame_pos = 0;
+            }
+            else
+            {
+                frame_pos = (frame_nr - frame_set->first_frame) / stride_length;
+            }
+
+            memcpy((char *)p_data->values + sizeof(double) * frame_pos * n_particles *
+                   n_values_per_frame, values, sizeof(double) *
+                   n_particles * n_values_per_frame);
+        }
+        else
+        {
+            memcpy(p_data->values, values, sizeof(double) * n_particles *
+                   n_values_per_frame);
+        }
+    }
+    else
+    {
+        if(tng_data_find(tng_data, block_id, &np_data) != TNG_SUCCESS)
+        {
+            stat = tng_data_block_add(tng_data, block_id, block_name,
+                                      TNG_DOUBLE_DATA, block_type_flag,
+                                      n_frames, n_values_per_frame,
+                                      stride_length, compression, 0);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error %s adding data block. %s: %d\n", block_name,
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+            if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+            {
+                np_data = &frame_set->tr_data[frame_set->
+                                              n_data_blocks - 1];
+            }
+            else
+            {
+                np_data = &tng_data->non_tr_data[tng_data->
+                                                 n_data_blocks - 1];
+            }
+            stat = tng_allocate_data_mem(tng_data, np_data, n_frames,
+                                         stride_length, n_values_per_frame);
+            if(stat != TNG_SUCCESS)
+            {
+                fprintf(stderr, "TNG library: Error allocating particle data memory. %s: %d\n",
+                       __FILE__, __LINE__);
+                return(stat);
+            }
+        }
+
+        if(block_type_flag == TNG_TRAJECTORY_BLOCK)
+        {
+            stride_length = np_data->stride_length;
+
+            if(is_first_frame_flag)
+            {
+                np_data->first_frame_with_data = frame_nr;
+                frame_pos = 0;
+            }
+            else
+            {
+                frame_pos = (frame_nr - frame_set->first_frame) / stride_length;
+            }
+
+            memcpy((char *)np_data->values + sizeof(double) * frame_pos *
+                   n_values_per_frame, values, sizeof(double) *
+                   n_values_per_frame);
+        }
+        else
+        {
+            memcpy(np_data->values, values, sizeof(double) * n_values_per_frame);
+        }
+    }
+
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *positions)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(positions, "TNG library: positions must not be a NULL pointer");
+
+    return(tng_util_generic_write(tng_data, frame_nr, positions, 3,
+                                  TNG_TRAJ_POSITIONS, "POSITIONS",
+                                  TNG_PARTICLE_BLOCK_DATA,
+                                  TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *positions)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(positions, "TNG library: positions must not be a NULL pointer");
+
+    return(tng_util_generic_double_write(tng_data, frame_nr, positions, 3,
+                                         TNG_TRAJ_POSITIONS, "POSITIONS",
+                                         TNG_PARTICLE_BLOCK_DATA,
+                                         TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *velocities)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(velocities, "TNG library: velocities must not be a NULL pointer");
+
+    return(tng_util_generic_write(tng_data, frame_nr, velocities, 3,
+                                  TNG_TRAJ_VELOCITIES, "VELOCITIES",
+                                  TNG_PARTICLE_BLOCK_DATA,
+                                  TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *velocities)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(velocities, "TNG library: velocities must not be a NULL pointer");
+
+    return(tng_util_generic_double_write(tng_data, frame_nr, velocities, 3,
+                                         TNG_TRAJ_VELOCITIES, "VELOCITIES",
+                                         TNG_PARTICLE_BLOCK_DATA,
+                                         TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *forces)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(forces, "TNG library: forces must not be a NULL pointer");
+
+    return(tng_util_generic_write(tng_data, frame_nr, forces, 3,
+                                  TNG_TRAJ_FORCES, "FORCES",
+                                  TNG_PARTICLE_BLOCK_DATA,
+                                  TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *forces)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(forces, "TNG library: forces must not be a NULL pointer");
+
+    return(tng_util_generic_double_write(tng_data, frame_nr, forces, 3,
+                                         TNG_TRAJ_FORCES, "FORCES",
+                                         TNG_PARTICLE_BLOCK_DATA,
+                                         TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *box_shape)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(box_shape, "TNG library: box_shape must not be a NULL pointer");
+
+    return(tng_util_generic_write(tng_data, frame_nr, box_shape, 9,
+                                  TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
+                                  TNG_NON_PARTICLE_BLOCK_DATA,
+                                  TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *box_shape)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(box_shape, "TNG library: box_shape must not be a NULL pointer");
+
+    return(tng_util_generic_double_write(tng_data, frame_nr, box_shape, 9,
+                                         TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
+                                         TNG_NON_PARTICLE_BLOCK_DATA,
+                                         TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(values, "TNG library: values must not be a NULL pointer");
+
+    stat = tng_util_generic_write(tng_data, frame_nr, values, n_values_per_frame,
+                                  block_id, block_name,
+                                  particle_dependency,
+                                  compression);
+
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    /* first_frame_time is -1 when it is not yet set. */
+    if(frame_set->first_frame_time < -0.1)
+    {
+        if(frame_nr > frame_set->first_frame)
+        {
+            stat = tng_frame_set_first_frame_time_set(tng_data,
+                                                      time -
+                                                      (frame_nr -
+                                                       frame_set->first_frame) *
+                                                      tng_data->time_per_frame);
+        }
+        else
+        {
+            stat = tng_frame_set_first_frame_time_set(tng_data, time);
+        }
+    }
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(values, "TNG library: values must not be a NULL pointer");
+
+    stat = tng_util_generic_double_write(tng_data, frame_nr, values, n_values_per_frame,
+                                         block_id, block_name,
+                                         particle_dependency,
+                                         compression);
+
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    /* first_frame_time is -1 when it is not yet set. */
+    if(frame_set->first_frame_time < -0.1)
+    {
+        if(frame_nr > frame_set->first_frame)
+        {
+            stat = tng_frame_set_first_frame_time_set(tng_data,
+                                                      time -
+                                                      (frame_nr -
+                                                       frame_set->first_frame) *
+                                                      tng_data->time_per_frame);
+        }
+        else
+        {
+            stat = tng_frame_set_first_frame_time_set(tng_data, time);
+        }
+    }
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *positions)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(positions, "TNG library: positions must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_write(tng_data, frame_nr, time, positions,
+                                            3, TNG_TRAJ_POSITIONS, "POSITIONS",
+                                            TNG_PARTICLE_BLOCK_DATA,
+                                            TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *positions)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(positions, "TNG library: positions must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_double_write(tng_data, frame_nr, time,
+                                                   positions, 3,
+                                                   TNG_TRAJ_POSITIONS,
+                                                   "POSITIONS",
+                                                   TNG_PARTICLE_BLOCK_DATA,
+                                                   TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *velocities)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(velocities, "TNG library: velocities must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_write(tng_data, frame_nr, time,
+                                            velocities, 3,
+                                            TNG_TRAJ_VELOCITIES,
+                                            "VELOCITIES",
+                                            TNG_PARTICLE_BLOCK_DATA,
+                                            TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *velocities)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(velocities, "TNG library: velocities must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_double_write(tng_data, frame_nr, time,
+                                                   velocities, 3,
+                                                   TNG_TRAJ_VELOCITIES,
+                                                   "VELOCITIES",
+                                                   TNG_PARTICLE_BLOCK_DATA,
+                                                   TNG_TNG_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *forces)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(forces, "TNG library: forces must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_write(tng_data, frame_nr, time, forces,
+                                            3, TNG_TRAJ_FORCES, "FORCES",
+                                            TNG_PARTICLE_BLOCK_DATA,
+                                            TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *forces)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(forces, "TNG library: forces must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_double_write(tng_data, frame_nr, time,
+                                                   forces, 3,
+                                                   TNG_TRAJ_FORCES, "FORCES",
+                                                   TNG_PARTICLE_BLOCK_DATA,
+                                                   TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *box_shape)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(box_shape, "TNG library: box_shape must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_write(tng_data, frame_nr, time, box_shape,
+                                            9, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
+                                            TNG_NON_PARTICLE_BLOCK_DATA,
+                                            TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_double_write
+                (tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *box_shape)
+{
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(frame_nr >= 0, "TNG library: frame_nr must be >= 0.");
+    TNG_ASSERT(time >= 0, "TNG library: time must be >= 0.");
+    TNG_ASSERT(box_shape, "TNG library: box_shape must not be a NULL pointer");
+
+    return(tng_util_generic_with_time_double_write(tng_data, frame_nr,
+                                                   time, box_shape, 9,
+                                                   TNG_TRAJ_BOX_SHAPE,
+                                                   "BOX SHAPE",
+                                                   TNG_NON_PARTICLE_BLOCK_DATA,
+                                                   TNG_GZIP_COMPRESSION));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_frame_current_compression_get
+                (tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 char *codec_id,
+                 float *factor)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    tng_function_status stat;
+    int64_t i;
+    int block_type = -1;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(codec_id, "TNG library: The pointer to the returned codec id must not be a NULL pointer.");
+    TNG_ASSERT(factor, "TNG library: The pointer to the returned multiplication factor must not be a NULL pointer.");
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    stat = tng_particle_data_find(tng_data, block_id, &p_data);
+    if(stat == TNG_SUCCESS)
+    {
+        block_type = TNG_PARTICLE_BLOCK_DATA;
+    }
+    else
+    {
+        stat = tng_data_find(tng_data, block_id, &np_data);
+        if(stat == TNG_SUCCESS)
+        {
+            block_type = TNG_NON_PARTICLE_BLOCK_DATA;
+        }
+        else
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+            if(stat != TNG_SUCCESS)
+            {
+                return(stat);
+            }
+            stat = tng_particle_data_find(tng_data, block_id, &p_data);
+            if(stat == TNG_SUCCESS)
+            {
+                block_type = TNG_PARTICLE_BLOCK_DATA;
+            }
+            else
+            {
+                stat = tng_data_find(tng_data, block_id, &np_data);
+                if(stat == TNG_SUCCESS)
+                {
+                    block_type = TNG_NON_PARTICLE_BLOCK_DATA;
+                }
+                else
+                {
+                    return(stat);
+                }
+            }
+        }
+    }
+    if(block_type == TNG_PARTICLE_BLOCK_DATA)
+    {
+        if(p_data->last_retrieved_frame < 0)
+        {
+            i = p_data->first_frame_with_data;
+        }
+        else
+        {
+            i = p_data->last_retrieved_frame;
+        }
+    }
+    else if(block_type == TNG_NON_PARTICLE_BLOCK_DATA)
+    {
+        if(np_data->last_retrieved_frame < 0)
+        {
+            i = np_data->first_frame_with_data;
+        }
+        else
+        {
+            i = np_data->last_retrieved_frame;
+        }
+    }
+    else
+    {
+        return(TNG_FAILURE);
+    }
+    if(i < frame_set->first_frame || i >= frame_set->first_frame + frame_set->n_frames)
+    {
+        stat = tng_frame_set_of_frame_find(tng_data, i);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_frame_set_read_current_only_data_from_block_id(tng_data, TNG_USE_HASH, block_id);
+        if(stat != TNG_SUCCESS)
+        {
+            fprintf(stderr, "TNG library: Cannot read data block of frame set. %s: %d\n",
+                __FILE__, __LINE__);
+            return(stat);
+        }
+    }
+    if(block_type == TNG_PARTICLE_BLOCK_DATA)
+    {
+        *codec_id = p_data->codec_id;
+        *factor   = p_data->compression_multiplier;
+    }
+    else if(block_type == TNG_NON_PARTICLE_BLOCK_DATA)
+    {
+        *codec_id = np_data->codec_id;
+        *factor   = np_data->compression_multiplier;
+    }
+    return(TNG_SUCCESS);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_next_frame_present_data_blocks_find
+                (tng_trajectory_t tng_data,
+                 int64_t current_frame,
+                 const int64_t n_requested_data_block_ids,
+                 const int64_t *requested_data_block_ids,
+                 int64_t *next_frame,
+                 int64_t *n_data_blocks_in_next_frame,
+                 int64_t **data_block_ids_in_next_frame)
+{
+    tng_trajectory_frame_set_t frame_set;
+    tng_function_status stat;
+    tng_particle_data_t p_data;
+    tng_non_particle_data_t np_data;
+    tng_gen_block_t block;
+    int64_t i, j, block_id, *temp;
+    int64_t data_frame, frame_diff, min_diff;
+    int64_t size, frame_set_file_pos;
+    int found, read_all = 0;
+    long file_pos;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(next_frame, "TNG library: The pointer to the next frame must not be NULL.");
+    TNG_ASSERT(n_data_blocks_in_next_frame, "TNG library: The pointer to n_data_blocks_in_next_frame must not be NULL.");
+    TNG_ASSERT(data_block_ids_in_next_frame, "TNG library: The pointer to the list of data block IDs must not be NULL.");
+
+    if(n_requested_data_block_ids)
+    {
+        TNG_ASSERT(requested_data_block_ids, "TNG library: If the number of requested data blocks is > 0 then the array of data block IDs must not be NULL.");
+        size = sizeof(int64_t) * n_requested_data_block_ids;
+        temp = realloc(*data_block_ids_in_next_frame, size);
+        if(!temp)
+        {
+            fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                    sizeof(int64_t) * (*n_data_blocks_in_next_frame),
+                    __FILE__, __LINE__);
+            free(*data_block_ids_in_next_frame);
+            *data_block_ids_in_next_frame = 0;
+            return(TNG_CRITICAL);
+        }
+        *data_block_ids_in_next_frame = temp;
+    }
+
+    frame_set = &tng_data->current_trajectory_frame_set;
+
+    current_frame += 1;
+
+    if(current_frame < frame_set->first_frame ||
+       current_frame >= frame_set->first_frame + frame_set->n_frames)
+    {
+        frame_set_file_pos = tng_data->current_trajectory_frame_set_input_file_pos;
+        stat = tng_frame_set_of_frame_find(tng_data, current_frame);
+        if(stat != TNG_SUCCESS)
+        {
+            /* If the frame set search found the frame set after the starting
+             * frame set there is a gap in the frame sets. So, even if the frame
+             * was not found the next frame with data is still in the found
+             * frame set. */
+            if(stat == TNG_CRITICAL || frame_set->prev_frame_set_file_pos !=
+               frame_set_file_pos)
+            {
+                return(stat);
+            }
+            current_frame = frame_set->first_frame;
+        }
+    }
+
+    if(frame_set->n_particle_data_blocks <= 0 || frame_set->n_data_blocks <= 0)
+    {
+        tng_block_init(&block);
+        file_pos = ftell(tng_data->input_file);
+        /* Read all blocks until next frame set block */
+        stat = tng_block_header_read(tng_data, block);
+        while(file_pos < tng_data->input_file_len &&
+            stat != TNG_CRITICAL &&
+            block->id != TNG_TRAJECTORY_FRAME_SET)
+        {
+            stat = tng_block_read_next(tng_data, block,
+                                       TNG_USE_HASH);
+            if(stat != TNG_CRITICAL)
+            {
+                file_pos = ftell(tng_data->input_file);
+                if(file_pos < tng_data->input_file_len)
+                {
+                    stat = tng_block_header_read(tng_data, block);
+                }
+            }
+        }
+        tng_block_destroy(&block);
+        if(stat == TNG_CRITICAL)
+        {
+            fprintf(stderr, "TNG library: Cannot read block header at pos %ld. %s: %d\n",
+                    file_pos, __FILE__, __LINE__);
+            return(stat);
+        }
+        read_all = 1;
+    }
+
+    min_diff = -1;
+
+    *n_data_blocks_in_next_frame = 0;
+
+    for(i = 0; i < frame_set->n_particle_data_blocks; i++)
+    {
+        p_data = &frame_set->tr_particle_data[i];
+        block_id = p_data->block_id;
+
+        if(n_requested_data_block_ids > 0)
+        {
+            found = 0;
+            for(j = 0; j < n_requested_data_block_ids; j++)
+            {
+                if(block_id == requested_data_block_ids[j])
+                {
+                    found = 1;
+                    break;
+                }
+            }
+            if(!found)
+            {
+                continue;
+            }
+        }
+
+        if(!read_all && (p_data->last_retrieved_frame < frame_set->first_frame ||
+           p_data->last_retrieved_frame >=
+           frame_set->first_frame + frame_set->n_frames))
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data,
+                                                                      TNG_USE_HASH, block_id);
+            if(stat == TNG_CRITICAL)
+            {
+                fprintf(stderr, "TNG library: Cannot read data block of frame set. %s: %d\n",
+                    __FILE__, __LINE__);
+                return(stat);
+            }
+            if(stat == TNG_FAILURE)
+            {
+                continue;
+            }
+        }
+        if(frame_set->first_frame != current_frame &&
+           p_data->last_retrieved_frame >= 0)
+        {
+            data_frame = p_data->last_retrieved_frame + p_data->stride_length;
+        }
+        else
+        {
+            data_frame = p_data->first_frame_with_data;
+        }
+        frame_diff = data_frame - current_frame;
+        if(frame_diff < 0)
+        {
+            continue;
+        }
+        if(min_diff == -1 || frame_diff <= min_diff)
+        {
+            if(frame_diff < min_diff)
+            {
+                *n_data_blocks_in_next_frame = 1;
+            }
+            else
+            {
+                *n_data_blocks_in_next_frame += 1;
+            }
+            if(n_requested_data_block_ids <= 0)
+            {
+                size = sizeof(int64_t) * (*n_data_blocks_in_next_frame);
+                temp = realloc(*data_block_ids_in_next_frame, size);
+                if(!temp)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                           sizeof(int64_t) * (*n_data_blocks_in_next_frame),
+                           __FILE__, __LINE__);
+                    free(*data_block_ids_in_next_frame);
+                    *data_block_ids_in_next_frame = 0;
+                    return(TNG_CRITICAL);
+                }
+                *data_block_ids_in_next_frame = temp;
+            }
+            else
+            {
+                TNG_ASSERT(*n_data_blocks_in_next_frame <= n_requested_data_block_ids, "TNG library: Array of data block IDs out of bounds");
+            }
+            (*data_block_ids_in_next_frame)[(*n_data_blocks_in_next_frame) - 1] = block_id;
+
+            min_diff = frame_diff;
+        }
+    }
+    for(i = 0; i < frame_set->n_data_blocks; i++)
+    {
+        np_data = &frame_set->tr_data[i];
+        block_id = np_data->block_id;
+
+        if(n_requested_data_block_ids > 0)
+        {
+            found = 0;
+            for(j = 0; j < n_requested_data_block_ids; j++)
+            {
+                if(block_id == requested_data_block_ids[j])
+                {
+                    found = 1;
+                    break;
+                }
+            }
+            if(!found)
+            {
+                continue;
+            }
+        }
+
+        if(!read_all && (np_data->last_retrieved_frame < frame_set->first_frame ||
+           np_data->last_retrieved_frame >=
+           frame_set->first_frame + frame_set->n_frames))
+        {
+            stat = tng_frame_set_read_current_only_data_from_block_id(tng_data,
+                                                                      TNG_USE_HASH, block_id);
+            if(stat == TNG_CRITICAL)
+            {
+                fprintf(stderr, "TNG library: Cannot read data block of frame set. %s: %d\n",
+                    __FILE__, __LINE__);
+                return(stat);
+            }
+            if(stat == TNG_FAILURE)
+            {
+                continue;
+            }
+        }
+        if(frame_set->first_frame != current_frame &&
+           np_data->last_retrieved_frame >= 0)
+        {
+            data_frame = np_data->last_retrieved_frame + np_data->stride_length;
+        }
+        else
+        {
+            data_frame = np_data->first_frame_with_data;
+        }
+        frame_diff = data_frame - current_frame;
+        if(frame_diff < 0)
+        {
+            continue;
+        }
+        if(min_diff == -1 || frame_diff <= min_diff)
+        {
+            if(frame_diff < min_diff)
+            {
+                *n_data_blocks_in_next_frame = 1;
+            }
+            else
+            {
+                *n_data_blocks_in_next_frame += 1;
+            }
+            if(n_requested_data_block_ids <= 0)
+            {
+                size = sizeof(int64_t) * (*n_data_blocks_in_next_frame);
+                temp = realloc(*data_block_ids_in_next_frame, size);
+                if(!temp)
+                {
+                    fprintf(stderr, "TNG library: Cannot allocate memory (%"PRId64" bytes). %s: %d\n",
+                           sizeof(int64_t) * (*n_data_blocks_in_next_frame),
+                           __FILE__, __LINE__);
+                    free(*data_block_ids_in_next_frame);
+                    *data_block_ids_in_next_frame = 0;
+                    return(TNG_CRITICAL);
+                }
+                *data_block_ids_in_next_frame = temp;
+            }
+            else
+            {
+                TNG_ASSERT(*n_data_blocks_in_next_frame <= n_requested_data_block_ids, "TNG library: Array of data block IDs out of bounds");
+            }
+            (*data_block_ids_in_next_frame)[(*n_data_blocks_in_next_frame) - 1] = block_id;
+
+            min_diff = frame_diff;
+        }
+    }
+    if(min_diff < 0)
+    {
+        return(TNG_FAILURE);
+    }
+    *next_frame = current_frame + min_diff;
+
+    return(TNG_SUCCESS);
+}
+
+/*
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_all_data_block_types_get
+                (tng_trajectory_t tng_data,
+                 int64_t *n_data_blocks,
+                 int64_t **data_block_ids,
+                 char ***data_block_names,
+                 int64_t **stride_lengths,
+                 int64_t **n_values_per_frame,
+                 char **block_types,
+                 char **dependencies,
+                 char **compressions)
+{
+    tng_gen_block_t block;
+    long orig_file_pos, file_pos;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(n_data_blocks, "TNG library: The pointer to n_data_blocks must not be NULL.");
+    TNG_ASSERT(data_block_ids, "TNG library: The pointer to the list of data block IDs must not be NULL.");
+    TNG_ASSERT(data_block_names, "TNG library: The pointer to the list of data block names must not be NULL.");
+    TNG_ASSERT(stride_lengths, "TNG library: The pointer to the list of stride lengths must not be NULL.");
+
+    orig_file_pos = ftell(tng_data->input_file);
+
+    if(!tng_data->input_file_len)
+    {
+        fseek(tng_data->input_file, 0, SEEK_END);
+        tng_data->input_file_len = ftell(tng_data->input_file);
+    }
+
+    fseek(tng_data->input_file, 0, SEEK_SET);
+    file_pos = 0;
+
+    *n_data_blocks = 0;
+
+    tng_block_init(&block);
+
+    while(file_pos < tng_data->input_file_len &&
+          tng_block_header_read(tng_data, block) != TNG_CRITICAL)
+    {
+        if(block->id > TNG_TRAJECTORY_FRAME_SET)
+        {
+
+        }
+        file_pos += (long)(block->block_contents_size + block->header_contents_size);
+        fseek(tng_data->input_file, (long)block->block_contents_size, SEEK_CUR);
+    }
+
+    fseek(tng_data->input_file, orig_file_pos, SEEK_SET);
+
+    return(TNG_SUCCESS);
+}
+*/
+tng_function_status DECLSPECDLLEXPORT tng_util_prepare_append_after_frame
+                (tng_trajectory_t tng_data,
+                 const int64_t prev_frame)
+{
+    tng_function_status stat;
+    FILE *temp = tng_data->input_file;
+
+    TNG_ASSERT(tng_data, "TNG library: Trajectory container not properly setup.");
+    TNG_ASSERT(prev_frame >= 0, "TNG library: The previous frame must not be negative.");
+
+    tng_data->input_file = tng_data->output_file;
+
+    stat = tng_frame_set_of_frame_find(tng_data, prev_frame);
+    if(stat != TNG_SUCCESS)
+    {
+        return(stat);
+    }
+
+    tng_data->current_trajectory_frame_set_output_file_pos =
+    tng_data->current_trajectory_frame_set_input_file_pos;
+
+    tng_data->input_file = temp;
+
+    return(TNG_SUCCESS);
+}
diff --git a/src/external/tng_io/src/lib/tng_io_fortran.c b/src/external/tng_io/src/lib/tng_io_fortran.c
new file mode 100644 (file)
index 0000000..f6101be
--- /dev/null
@@ -0,0 +1,1129 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.4
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#include "../../include/tng_io.h"
+
+/* The following is for calling the library from fortran */
+
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init_(tng_trajectory_t *tng_data_p)
+{
+    return(tng_trajectory_init(tng_data_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_destroy_(tng_trajectory_t *tng_data_p)
+{
+    return(tng_trajectory_destroy(tng_data_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init_from_src_(tng_trajectory_t src,
+                                                   tng_trajectory_t *dest_p)
+{
+    return(tng_trajectory_init_from_src(src, dest_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_input_file_get_(const tng_trajectory_t tng_data,
+                                        char *file_name, const int max_len)
+{
+    return(tng_input_file_get(tng_data, file_name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_input_file_set_(tng_trajectory_t tng_data,
+                                        const char *file_name, int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, file_name, name_len);
+    name[name_len] = 0;
+    stat = tng_input_file_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_get_(const tng_trajectory_t tng_data,
+                                         char *file_name, const int max_len)
+{
+    return(tng_output_file_get(tng_data, file_name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_set_(tng_trajectory_t tng_data,
+                                         const char *file_name, int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, file_name, name_len);
+    name[name_len] = 0;
+    stat = tng_output_file_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_get_(const tng_trajectory_t tng_data,
+                                                char *name, const int max_len)
+{
+    return(tng_first_program_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_get_
+                (tng_trajectory_t tng_data, tng_file_endianness *endianness)
+{
+    return(tng_output_file_endianness_get(tng_data, endianness));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_set_
+                (tng_trajectory_t tng_data, const tng_file_endianness *endianness)
+{
+    return(tng_output_file_endianness_set(tng_data, *endianness));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_set_(tng_trajectory_t tng_data,
+                                                const char *new_name,
+                                                int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_first_program_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_get_(const tng_trajectory_t tng_data,
+                                                char *name, const int max_len)
+{
+    return(tng_last_program_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_set_(tng_trajectory_t tng_data,
+                                               const char *new_name,
+                                               int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_last_program_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_get_(const tng_trajectory_t tng_data,
+                                             char *name, const int max_len)
+{
+    return(tng_first_user_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_set_(tng_trajectory_t tng_data,
+                                             const char *new_name,
+                                             int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_first_user_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_get_(const tng_trajectory_t tng_data,
+                                            char *name, const int max_len)
+{
+    return(tng_last_user_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_set_(tng_trajectory_t tng_data,
+                                            const char *new_name,
+                                            int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_last_user_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_get_(const tng_trajectory_t tng_data,
+                                                 char *name, const int max_len)
+{
+    return(tng_first_computer_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_set_(tng_trajectory_t tng_data,
+                                                 const char *new_name,
+                                                 int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_first_computer_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_get_(const tng_trajectory_t tng_data,
+                                                 char *name, const int max_len)
+{
+    return(tng_last_computer_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_set_(tng_trajectory_t tng_data,
+                                                const char *new_name,
+                                                int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_last_computer_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_get_
+                (const tng_trajectory_t tng_data,
+                 char *signature, const int max_len)
+{
+    return(tng_first_signature_get(tng_data, signature, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_set_(tng_trajectory_t tng_data,
+                                             const char *signature,
+                                             int sign_len)
+{
+    char *sign = malloc(sign_len + 1);
+    tng_function_status stat;
+
+    strncpy(sign, signature, sign_len);
+    sign[sign_len] = 0;
+    stat = tng_first_signature_set(tng_data, sign);
+    free(sign);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_get_
+                (const tng_trajectory_t tng_data,
+                 char *signature, const int max_len)
+{
+    return(tng_last_signature_get(tng_data, signature, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_set_
+                (tng_trajectory_t tng_data,
+                 const char *signature,
+                 int sign_len)
+{
+    char *sign = malloc(sign_len + 1);
+    tng_function_status stat;
+
+    strncpy(sign, signature, sign_len);
+    sign[sign_len] = 0;
+    stat = tng_last_signature_set(tng_data, sign);
+    free(sign);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_get_
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len)
+{
+    return(tng_forcefield_name_get(tng_data, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_set_
+                (tng_trajectory_t tng_data,
+                 const char *new_name,
+                 int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_forcefield_name_set(tng_data, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *len)
+{
+    return(tng_medium_stride_length_get(tng_data, len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *len)
+{
+    return(tng_medium_stride_length_set(tng_data, *len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *len)
+{
+    return(tng_long_stride_length_get(tng_data, len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *len)
+{
+    return(tng_long_stride_length_set(tng_data, *len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_get_
+                (const tng_trajectory_t tng_data,
+                 double *time)
+{
+    return(tng_time_per_frame_get(tng_data, time));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_set_
+                (tng_trajectory_t tng_data,
+                 const double *time)
+{
+    return(tng_time_per_frame_set(tng_data, *time));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_input_file_len_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *len)
+{
+    return(tng_input_file_len_get(tng_data, len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    return(tng_num_frames_get(tng_data, n));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    return(tng_num_particles_get(tng_data, n));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_molecules_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    return(tng_num_molecules_get(tng_data, n));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *exp)
+{
+    return(tng_distance_unit_exponential_get(tng_data, exp));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_set_
+                (const tng_trajectory_t tng_data,
+                 const int64_t *exp)
+{
+    return(tng_distance_unit_exponential_set(tng_data, *exp));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    return(tng_num_frames_per_frame_set_get(tng_data, n));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_set_
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    return(tng_num_frames_per_frame_set_set(tng_data, *n));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_num_frame_sets_get_
+                (const tng_trajectory_t tng_data,
+                 int64_t *n)
+{
+    return(tng_num_frame_sets_get(tng_data, n));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_current_frame_set_get_
+                (tng_trajectory_t tng_data,
+                 tng_trajectory_frame_set_t *frame_set_p)
+{
+    return(tng_current_frame_set_get(tng_data, frame_set_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_nr_find_(tng_trajectory_t tng_data,
+                                                             const int64_t *nr)
+{
+    return(tng_frame_set_nr_find(tng_data, *nr));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_of_frame_find_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame)
+{
+    return(tng_frame_set_of_frame_find(tng_data, *frame));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_next_frame_set_file_pos_get_
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos)
+{
+    return(tng_frame_set_next_frame_set_file_pos_get(tng_data, frame_set, pos));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_prev_frame_set_file_pos_get_
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos)
+{
+    return(tng_frame_set_prev_frame_set_file_pos_get(tng_data, frame_set, pos));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_frame_range_get_
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *first_frame,
+                 int64_t *last_frame)
+{
+    return(tng_frame_set_frame_range_get(tng_data, frame_set, first_frame,
+                                         last_frame));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_init_(const tng_trajectory_t tng_data,
+                                       tng_molecule_t molecule)
+{
+    return(tng_molecule_init(tng_data, molecule));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_destroy_
+                (const tng_trajectory_t tng_data,
+                 tng_molecule_t molecule)
+{
+    return(tng_molecule_destroy(tng_data, molecule));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_add_(tng_trajectory_t tng_data,
+                                                        const char *name,
+                                                        tng_molecule_t *molecule,
+                                                        int name_len)
+{
+    char *n = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(n, name, name_len);
+    n[name_len] = 0;
+    stat = tng_molecule_add(tng_data, n, molecule);
+    free(n);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_set_(tng_trajectory_t tng_data,
+                                                             tng_molecule_t molecule,
+                                                             const char *new_name,
+                                                             int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_molecule_name_set(tng_data, molecule, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_get_(tng_trajectory_t tng_data,
+                                                            tng_molecule_t molecule,
+                                                            int64_t *cnt)
+{
+    return(tng_molecule_cnt_get(tng_data, molecule, cnt));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_set_(tng_trajectory_t tng_data,
+                                                            tng_molecule_t molecule,
+                                                            int64_t *cnt)
+{
+    return(tng_molecule_cnt_set(tng_data, molecule, *cnt));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_find_(tng_trajectory_t tng_data,
+                                                         const char *name,
+                                                         int64_t nr,
+                                                         tng_molecule_t *molecule,
+                                                         int name_len)
+{
+    char *n = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(n, name, name_len);
+    n[name_len] = 0;
+    stat = tng_molecule_find(tng_data, n, nr, molecule);
+    free(n);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_find_(tng_trajectory_t tng_data,
+                                                               tng_molecule_t molecule,
+                                                               const char *name,
+                                                               int64_t id,
+                                                               tng_chain_t *chain,
+                                                               int name_len)
+{
+    char *n = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(n, name, name_len);
+    n[name_len] = 0;
+    stat = tng_molecule_chain_find(tng_data, molecule, n, id, chain);
+    free(n);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_add_(tng_trajectory_t tng_data,
+                                                              tng_molecule_t molecule,
+                                                              const char *name,
+                                                              tng_chain_t *chain,
+                                                              int name_len)
+{
+    char *n = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(n, name, name_len);
+    n[name_len] = 0;
+    stat = tng_molecule_chain_add(tng_data, molecule, n, chain);
+    free(n);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_set_(tng_trajectory_t tng_data,
+                                                          tng_chain_t chain,
+                                                          const char *new_name,
+                                                          int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_chain_name_set(tng_data, chain, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_add_(tng_trajectory_t tng_data,
+                                                             tng_chain_t chain,
+                                                             const char *name,
+                                                             tng_residue_t *residue,
+                                                             int name_len)
+{
+    char *n = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(n, name, name_len);
+    n[name_len] = 0;
+    stat = tng_chain_residue_add(tng_data, chain, n, residue);
+    free(n);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_set_(tng_trajectory_t tng_data,
+                                                            tng_residue_t residue,
+                                                            const char *new_name,
+                                                            int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_residue_name_set(tng_data, residue, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_add_(tng_trajectory_t tng_data,
+                                                            tng_residue_t residue,
+                                                            const char *atom_name,
+                                                            const char *atom_type,
+                                                            tng_atom_t *atom,
+                                                            int name_len,
+                                                            int type_len)
+{
+    char *name = malloc(name_len + 1);
+    char *type = malloc(type_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, atom_name, name_len);
+    strncpy(type, atom_type, type_len);
+    name[name_len] = 0;
+    type[type_len] = 0;
+    stat = tng_residue_atom_add(tng_data, residue, name, type, atom);
+    free(name);
+    free(type);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_set_(tng_trajectory_t tng_data,
+                                                         tng_atom_t atom,
+                                                         const char *new_name,
+                                                         int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, new_name, name_len);
+    name[name_len] = 0;
+    stat = tng_atom_name_set(tng_data, atom, name);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_set_(tng_trajectory_t tng_data,
+                                                         tng_atom_t atom,
+                                                         const char *new_type,
+                                                         int type_len)
+{
+    char *type = malloc(type_len + 1);
+    tng_function_status stat;
+
+    strncpy(type, new_type, type_len);
+    type[type_len] = 0;
+    stat = tng_atom_type_set(tng_data, atom, type);
+    free(type);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_of_particle_nr_get_
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    return(tng_molecule_name_of_particle_nr_get(tng_data, nr, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_of_particle_nr_get_
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    return(tng_chain_name_of_particle_nr_get(tng_data, nr, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_of_particle_nr_get_
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    return(tng_residue_name_of_particle_nr_get(tng_data, nr, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_residue_id_of_particle_nr_get_
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id)
+{
+    return(tng_residue_id_of_particle_nr_get(tng_data, nr, id));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_of_particle_nr_get_
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 int max_len)
+{
+    return(tng_atom_name_of_particle_nr_get(tng_data, nr, name, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_of_particle_nr_get_
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *type,
+                 int max_len)
+{
+    return(tng_atom_type_of_particle_nr_get(tng_data, nr, type, max_len));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_mapping_add_
+                (tng_trajectory_t tng_data,
+                 const int64_t *first_particle_number,
+                 const int64_t *n_particles,
+                 const int64_t *mapping_table)
+{
+    return(tng_particle_mapping_add(tng_data, *first_particle_number,
+                                    *n_particles, mapping_table));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_read_(tng_trajectory_t tng_data,
+                                                             const char *hash_mode)
+{
+    return(tng_file_headers_read(tng_data, *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_write_
+                (tng_trajectory_t tng_data,
+                 const char *hash_mode)
+{
+    return(tng_file_headers_write(tng_data, *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_block_read_next_
+                (tng_trajectory_t tng_data,
+                 tng_gen_block_t block_data,
+                 const char *hash_mode)
+{
+    return(tng_block_read_next(tng_data, block_data, *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next_
+                (tng_trajectory_t tng_data,
+                 const char *hash_mode)
+{
+    return(tng_frame_set_read_next(tng_data, *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_write_(tng_trajectory_t tng_data,
+                                                           const char *hash_mode)
+{
+    return(tng_frame_set_write(tng_data, *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_new_(tng_trajectory_t tng_data,
+                                                         const int64_t *first_frame,
+                                                         const int64_t *n_frames)
+{
+    return(tng_frame_set_new(tng_data, *first_frame, *n_frames));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_with_time_new_
+                (tng_trajectory_t tng_data,
+                 const int64_t *first_frame,
+                 const int64_t *n_frames,
+                 const double *first_frame_time)
+{
+    return(tng_frame_set_with_time_new(tng_data, *first_frame, *n_frames,
+                             *first_frame_time));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_first_frame_time_set_
+                (tng_trajectory_t tng_data,
+                 const double *first_frame_time)
+{
+    return(tng_frame_set_first_frame_time_set(tng_data, *first_frame_time));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_block_add_
+                (tng_trajectory_t tng_data,
+                 const int64_t *id,
+                 const char *block_name,
+                 const char *datatype,
+                 const char *block_type_flag,
+                 int64_t *n_frames,
+                 const int64_t *n_values_per_frame,
+                 const int64_t *stride_length,
+                 const int64_t *codec_id,
+                 void *new_data,
+                 int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, block_name, name_len);
+    name[name_len] = 0;
+    stat = tng_data_block_add(tng_data, *id, name, *datatype, *block_type_flag,
+                              *n_frames, *n_values_per_frame, *stride_length,
+                              *codec_id, new_data);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_block_add_
+                (tng_trajectory_t tng_data,
+                 const int64_t *id,
+                 const char *block_name,
+                 const char *datatype,
+                 const char *block_type_flag,
+                 int64_t *n_frames,
+                 const int64_t *n_values_per_frame,
+                 const int64_t *stride_length,
+                 const int64_t *first_particle_number,
+                 const int64_t *n_particles,
+                 const int64_t *codec_id,
+                 void *new_data,
+                 int name_len)
+{
+    char *name = malloc(name_len + 1);
+    tng_function_status stat;
+
+    strncpy(name, block_name, name_len);
+    name[name_len] = 0;
+    stat = tng_particle_data_block_add(tng_data, *id, name, *datatype,
+                                       *block_type_flag, *n_frames,
+                                       *n_values_per_frame, *stride_length,
+                                       *first_particle_number, *n_particles,
+                                       *codec_id, new_data);
+    free(name);
+    return(stat);
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_data_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const int64_t *block_id,
+                 const void *data,
+                 const char *hash_mode)
+{
+    return(tng_frame_data_write(tng_data, *frame_nr, *block_id, data,
+                                *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_frame_particle_data_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const int64_t *block_id,
+                 const int64_t *val_first_particle,
+                 const int64_t *val_n_particles,
+                 const void *data,
+                 const char *hash_mode)
+{
+    return(tng_frame_particle_data_write(tng_data, *frame_nr, *block_id,
+                                         *val_first_particle, *val_n_particles,
+                                         data, *hash_mode));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_values_free_
+                (const tng_trajectory_t tng_data,
+                 union data_values **values,
+                 const int64_t *n_frames,
+                 const int64_t *n_values_per_frame,
+                 const char *type)
+{
+    return(tng_data_values_free(tng_data, values, *n_frames,
+                                *n_values_per_frame, *type));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_values_free_
+                (const tng_trajectory_t tng_data,
+                 union data_values ***values,
+                 const int64_t *n_frames,
+                 const int64_t *n_particles,
+                 const int64_t *n_values_per_frame,
+                 const char *type)
+{
+    return(tng_particle_data_values_free(tng_data, values, *n_frames, *n_particles,
+                                         *n_values_per_frame, *type));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_get_
+                (tng_trajectory_t tng_data,
+                 const int64_t *block_id,
+                 union data_values ***values,
+                 int64_t *n_frames,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    return(tng_data_get(tng_data, *block_id, values, n_frames,
+                        n_values_per_frame, type));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_data_interval_get_
+                (tng_trajectory_t tng_data,
+                 const int64_t *block_id,
+                 const int64_t *start_frame_nr,
+                 const int64_t *end_frame_nr,
+                 const char *hash_mode,
+                 union data_values ***values,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    return(tng_data_interval_get(tng_data, *block_id, *start_frame_nr,
+                                 *end_frame_nr, *hash_mode, values,
+                                 n_values_per_frame, type));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_get_
+                (tng_trajectory_t tng_data,
+                 const int64_t *block_id,
+                 union data_values ****values,
+                 int64_t *n_frames,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    return(tng_particle_data_get(tng_data, *block_id, values, n_frames,
+                                 n_particles, n_values_per_frame, type));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_interval_get_
+                (tng_trajectory_t tng_data,
+                 const int64_t *block_id,
+                 const int64_t *start_frame_nr,
+                 const int64_t *end_frame_nr,
+                 const char *hash_mode,
+                 union data_values ****values,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+{
+    return(tng_particle_data_interval_get(tng_data, *block_id, *start_frame_nr,
+                                          *end_frame_nr, *hash_mode, values,
+                                          n_particles, n_values_per_frame,
+                                          type));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_time_get_str_
+                (const tng_trajectory_t tng_data,
+                 char *time, int64_t str_len)
+{
+    return(tng_time_get_str(tng_data, time));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_open_
+                (const char *filename, const char *mode,
+                 tng_trajectory_t *tng_data_p)
+{
+    return(tng_util_trajectory_open(filename, *mode, tng_data_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_close_
+                (tng_trajectory_t *tng_data_p)
+{
+    return(tng_util_trajectory_close(tng_data_p));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read_
+                (tng_trajectory_t tng_data,
+                 float **positions,
+                 int64_t *stride_length)
+{
+    return(tng_util_pos_read(tng_data, positions, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read_
+                (tng_trajectory_t tng_data,
+                 float **velocities,
+                 int64_t *stride_length)
+{
+    return(tng_util_vel_read(tng_data, velocities, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read_
+                (tng_trajectory_t tng_data,
+                 float **forces,
+                 int64_t *stride_length)
+{
+    return(tng_util_force_read(tng_data, forces, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read_
+                (tng_trajectory_t tng_data,
+                 float **box_shape,
+                 int64_t *stride_length)
+{
+    return(tng_util_box_shape_read(tng_data, box_shape, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read_range_
+                (tng_trajectory_t tng_data,
+                 const int64_t *first_frame,
+                 const int64_t *last_frame,
+                 float **positions,
+                 int64_t *stride_length)
+{
+    return(tng_util_pos_read_range(tng_data, *first_frame, *last_frame,
+                                         positions, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read_range_
+                (tng_trajectory_t tng_data,
+                 const int64_t *first_frame,
+                 const int64_t *last_frame,
+                 float **velocities,
+                 int64_t *stride_length)
+{
+    return(tng_util_vel_read_range(tng_data, *first_frame, *last_frame,
+                                         velocities, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read_range_
+                (tng_trajectory_t tng_data,
+                 const int64_t *first_frame,
+                 const int64_t *last_frame,
+                 float **forces,
+                 int64_t *stride_length)
+{
+    return(tng_util_force_read_range(tng_data, *first_frame, *last_frame,
+                                         forces, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read_range_
+                (tng_trajectory_t tng_data,
+                 const int64_t *first_frame,
+                 const int64_t *last_frame,
+                 float **box_shape,
+                 int64_t *stride_length)
+{
+    return(tng_util_box_shape_read_range(tng_data, *first_frame, *last_frame,
+                                         box_shape, stride_length));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_frequency_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *f,
+                 const int64_t *n_values_per_frame,
+                 const int64_t *block_id,
+                 const char *block_name,
+                 const char *particle_dependency,
+                 const char *compression)
+{
+    return(tng_util_generic_write_frequency_set(tng_data, *f,
+                                                *n_values_per_frame, *block_id,
+                                                block_name,
+                                                *particle_dependency,
+                                                *compression));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_frequency_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *f)
+{
+    return(tng_util_pos_write_frequency_set(tng_data, *f));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_frequency_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *f)
+{
+    return(tng_util_vel_write_frequency_set(tng_data, *f));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_frequency_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *f)
+{
+    return(tng_util_force_write_frequency_set(tng_data, *f));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_frequency_set_
+                (tng_trajectory_t tng_data,
+                 const int64_t *f)
+{
+    return(tng_util_box_shape_write_frequency_set(tng_data, *f));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const float *values,
+                 const int64_t *n_values_per_frame,
+                 const int64_t *block_id,
+                 const char *block_name,
+                 const char *particle_dependency,
+                 const char *compression)
+{
+    return(tng_util_generic_write(tng_data, *frame_nr, values,
+                                  *n_values_per_frame, *block_id, block_name,
+                                  *particle_dependency, *compression));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const float *positions)
+{
+    return(tng_util_vel_write(tng_data, *frame_nr, positions));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const float *velocities)
+{
+    return(tng_util_vel_write(tng_data, *frame_nr, velocities));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const float *forces)
+{
+    return(tng_util_force_write(tng_data, *frame_nr, forces));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const float *box_shape)
+{
+    return(tng_util_box_shape_write(tng_data, *frame_nr, box_shape));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const int64_t *time,
+                 const float *positions)
+{
+    return(tng_util_pos_with_time_write(tng_data, *frame_nr, *time,
+                                        positions));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const int64_t *time,
+                 const float *velocities)
+{
+    return(tng_util_vel_with_time_write(tng_data, *frame_nr, *time,
+                                        velocities));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const int64_t *time,
+                 const float *forces)
+{
+    return(tng_util_force_with_time_write(tng_data, *frame_nr, *time,
+                                          forces));
+}
+
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_write_
+                (tng_trajectory_t tng_data,
+                 const int64_t *frame_nr,
+                 const int64_t *time,
+                 const float *box_shape)
+{
+    return(tng_util_box_shape_with_time_write(tng_data, *frame_nr, *time,
+                                              box_shape));
+}
diff --git a/src/external/tng_io/src/tests/CMakeLists.txt b/src/external/tng_io/src/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a0cfc46
--- /dev/null
@@ -0,0 +1,89 @@
+if(TNG_BUILD_COMPRESSION_TESTS)
+  add_subdirectory(compression)
+endif()
+
+link_directories(${TNG_IO_BINARY_DIR}/src/lib)
+
+add_definitions(-DTNG_EXAMPLE_FILES_DIR="${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/tests/example_files/") # Directory where to find input test files and save output files.
+
+if(TNG_BUILD_TEST)
+    add_executable(tng_testing tng_io_testing.c)
+    target_link_libraries(tng_testing tng_io)
+    if(UNIX)
+        target_link_libraries(tng_testing m)
+    endif()
+
+    if(HAVE_INTTYPES_H)
+      set_property(TARGET tng_testing APPEND PROPERTY COMPILE_DEFINITIONS USE_STD_INTTYPES_H=1)
+    endif()
+
+    file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../../example_files DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/tests/)
+
+    set_property(TARGET tng_testing PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/tests)
+endif()
+
+if(TNG_BUILD_EXAMPLES)
+    if(TNG_USE_OPENMP AND OPENMP_FOUND)
+
+        add_executable(md_openmp md_openmp.c)
+        target_link_libraries(md_openmp tng_io ${OpenMP_LIBS})
+        if(UNIX)
+            target_link_libraries(md_openmp m)
+        endif()
+        set_property(TARGET md_openmp PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples)
+        set_property(TARGET md_openmp APPEND PROPERTY COMPILE_DEFINITIONS TNG_BUILD_OPENMP_EXAMPLES)
+
+
+        add_executable(md_openmp_util md_openmp_util.c)
+        target_link_libraries(md_openmp_util tng_io ${OpenMP_LIBS})
+        if(UNIX)
+            target_link_libraries(md_openmp_util m)
+        endif()
+        set_property(TARGET md_openmp_util PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples)
+        set_property(TARGET md_openmp_util APPEND PROPERTY COMPILE_DEFINITIONS TNG_BUILD_OPENMP_EXAMPLES)
+
+        add_executable(tng_parallel_read tng_parallel_read.c)
+        target_link_libraries(tng_parallel_read tng_io)
+        if(UNIX)
+            target_link_libraries(tng_parallel_read m)
+        endif()
+        set_property(TARGET tng_parallel_read PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples)
+        set_property(TARGET tng_parallel_read APPEND PROPERTY COMPILE_DEFINITIONS TNG_BUILD_OPENMP_EXAMPLES)
+    endif()
+
+    add_executable(tng_io_read_pos tng_io_read_pos.c)
+    target_link_libraries(tng_io_read_pos tng_io)
+    if(UNIX)
+        target_link_libraries(tng_io_read_pos m)
+    endif()
+    if(HAVE_INTTYPES_H)
+      set_property(TARGET tng_io_read_pos APPEND PROPERTY COMPILE_DEFINITIONS USE_STD_INTTYPES_H=1)
+    endif()
+    set_property(TARGET tng_io_read_pos PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples)
+
+    add_executable(tng_io_read_pos_util tng_io_read_pos_util.c)
+    target_link_libraries(tng_io_read_pos_util tng_io)
+    if(UNIX)
+        target_link_libraries(tng_io_read_pos_util m)
+    endif()
+    if(HAVE_INTTYPES_H)
+      set_property(TARGET tng_io_read_pos_util APPEND PROPERTY COMPILE_DEFINITIONS USE_STD_INTTYPES_H=1)
+    endif()
+    set_property(TARGET tng_io_read_pos_util PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples)
+
+    if(TNG_BUILD_FORTRAN)
+        # This does not work due to a bug in CMake. Remove lines below if no fortran compiler is found.
+        enable_language(Fortran OPTIONAL)
+        if(${CMAKE_Fortran_COMPILER_WORKS})
+            get_filename_component (Fortran_COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME)
+            if (Fortran_COMPILER_NAME STREQUAL "gfortran")
+                set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fcray-pointer ${OpenMP_C_FLAGS} -std=legacy")
+            endif()
+            if(OPENMP_FOUND)
+                add_executable(md_openmp_f md_openmp.f)
+                target_link_libraries(md_openmp_f tng_io ${OpenMP_LIBS})
+                set_property(TARGET md_openmp_f PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples)
+            endif()
+        endif()
+    endif()
+endif()
diff --git a/src/external/tng_io/src/tests/compression/CMakeLists.txt b/src/external/tng_io/src/tests/compression/CMakeLists.txt
new file mode 100644 (file)
index 0000000..075b06b
--- /dev/null
@@ -0,0 +1,46 @@
+link_directories(${TNG_IO_BINARY_DIR}/src/lib)
+
+add_definitions(-DTNG_COMPRESS_FILES_DIR="${CMAKE_BINARY_DIR}/test_tng_compress_files/") # Directory where to write tng_compress test files.
+
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_tng_compress_files)
+
+include_directories(${CMAKE_BINARY_DIR}/include/)
+
+set(number 0)
+set(numtests 78)
+
+while( number LESS ${numtests})
+
+math( EXPR number "${number} + 1" )
+
+add_executable(test_tng_compress_gen${number} testsuite.c)
+target_link_libraries(test_tng_compress_gen${number} tng_compress)
+if(UNIX)
+target_link_libraries(test_tng_compress_gen${number} m)
+endif()
+list(APPEND gen${number}_build_definitions GEN)
+list(APPEND gen${number}_build_definitions TESTPARAM="test${number}.h")
+set_target_properties(test_tng_compress_gen${number} PROPERTIES COMPILE_DEFINITIONS "${gen${number}_build_definitions}")
+add_dependencies(test_tng_compress_gen${number} test${number}.h)
+set_property(TARGET test_tng_compress_gen${number} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/compression_tests)
+
+add_executable(test_tng_compress_read${number} testsuite.c)
+target_link_libraries(test_tng_compress_read${number} tng_compress)
+if(UNIX)
+target_link_libraries(test_tng_compress_read${number} m)
+endif()
+list(APPEND read${number}_build_definitions TESTPARAM="test${number}.h")
+set_target_properties(test_tng_compress_read${number} PROPERTIES COMPILE_DEFINITIONS "${read${number}_build_definitions}")
+add_dependencies(test_tng_compress_read${number} test${number}.h)
+set_property(TARGET test_tng_compress_read${number} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/compression_tests)
+
+endwhile()
+
+if(UNIX)
+file(COPY test_tng_compress_write.sh DESTINATION ${CMAKE_BINARY_DIR}/bin/compression_tests)
+file(COPY test_tng_compress_read.sh DESTINATION ${CMAKE_BINARY_DIR}/bin/compression_tests)
+endif()
+if(WIN32)
+file(COPY test_tng_compress_write.bat DESTINATION ${CMAKE_BINARY_DIR}/bin/compression_tests)
+file(COPY test_tng_compress_read.bat DESTINATION ${CMAKE_BINARY_DIR}/bin/compression_tests)
+endif()
diff --git a/src/external/tng_io/src/tests/compression/getfilesize.sh b/src/external/tng_io/src/tests/compression/getfilesize.sh
new file mode 100755 (executable)
index 0000000..41651a1
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+if [ -z "$3" ]; then
+    echo $0 STARTTEST ENDTEST TNGFILEDIR
+else
+    STARTTEST="$1"
+    ENDTEST="$2"
+    TNGFILEDIR="$3"
+    for testnum in $(seq $STARTTEST $ENDTEST); do
+       if [ -r $TNGFILEDIR/test$testnum.tng_compress ]; then
+           grep -v "EXPECTED_FILESIZE" test$testnum.h >tmp$$.h
+           echo "#define EXPECTED_FILESIZE" $(ls -l $TNGFILEDIR/test$testnum.tng_compress |awk '{print $5}'). >>tmp$$.h
+           mv tmp$$.h test$testnum.h
+       fi
+    done
+fi
\ No newline at end of file
diff --git a/src/external/tng_io/src/tests/compression/test1.h b/src/external/tng_io/src/tests/compression/test1.h
new file mode 100644 (file)
index 0000000..b45abc4
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame triple algorithm. Cubic cell."
+#define FILENAME "test1.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2776230.
diff --git a/src/external/tng_io/src/tests/compression/test10.h b/src/external/tng_io/src/tests/compression/test10.h
new file mode 100644 (file)
index 0000000..d5135eb
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Triple intraframe algorithm. Cubic cell."
+#define FILENAME "test10.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 3
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2728492.
diff --git a/src/external/tng_io/src/tests/compression/test11.h b/src/external/tng_io/src/tests/compression/test11.h
new file mode 100644 (file)
index 0000000..596d63c
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Triple one-to-one algorithm. Cubic cell."
+#define FILENAME "test11.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 7
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 4293415.
diff --git a/src/external/tng_io/src/tests/compression/test12.h b/src/external/tng_io/src/tests/compression/test12.h
new file mode 100644 (file)
index 0000000..43eb15d
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. BWLZH interframe algorithm. Cubic cell."
+#define FILENAME "test12.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 8
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 894421.
diff --git a/src/external/tng_io/src/tests/compression/test13.h b/src/external/tng_io/src/tests/compression/test13.h
new file mode 100644 (file)
index 0000000..72aa99e
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. BWLZH intraframe algorithm. Cubic cell."
+#define FILENAME "test13.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 9
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 840246.
diff --git a/src/external/tng_io/src/tests/compression/test14.h b/src/external/tng_io/src/tests/compression/test14.h
new file mode 100644 (file)
index 0000000..382c70e
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. XTC3 algorithm. Cubic cell."
+#define FILENAME "test14.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 10 
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 1401016.
diff --git a/src/external/tng_io/src/tests/compression/test15.h b/src/external/tng_io/src/tests/compression/test15.h
new file mode 100644 (file)
index 0000000..c593bf5
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Automatic selection of algorithms. Cubic cell."
+#define FILENAME "test15.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2776230.
diff --git a/src/external/tng_io/src/tests/compression/test16.h b/src/external/tng_io/src/tests/compression/test16.h
new file mode 100644 (file)
index 0000000..a29c5e5
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Coding. Automatic selection of algorithms. Cubic cell."
+#define FILENAME "test16.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING -1
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define SPEED 6
+#define EXPECTED_FILESIZE 838168.
diff --git a/src/external/tng_io/src/tests/compression/test17.h b/src/external/tng_io/src/tests/compression/test17.h
new file mode 100644 (file)
index 0000000..9787976
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Initial coding of velocities. Stopbits one-to-one . Cubic cell."
+#define FILENAME "test17.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 7336171.
diff --git a/src/external/tng_io/src/tests/compression/test18.h b/src/external/tng_io/src/tests/compression/test18.h
new file mode 100644 (file)
index 0000000..5a329ad
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Initial coding of velocities. Triplet one-to-one. Cubic cell."
+#define FILENAME "test18.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 7089695.
diff --git a/src/external/tng_io/src/tests/compression/test19.h b/src/external/tng_io/src/tests/compression/test19.h
new file mode 100644 (file)
index 0000000..b298309
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Initial coding of velocities. BWLZH one-to-one. Cubic cell."
+#define FILENAME "test19.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 9
+#define INITIALVELCODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 50
+#define EXPECTED_FILESIZE 208809.
diff --git a/src/external/tng_io/src/tests/compression/test2.h b/src/external/tng_io/src/tests/compression/test2.h
new file mode 100644 (file)
index 0000000..0e2c3b0
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC2 algorithm. Cubic cell."
+#define FILENAME "test2.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2796171.
diff --git a/src/external/tng_io/src/tests/compression/test20.h b/src/external/tng_io/src/tests/compression/test20.h
new file mode 100644 (file)
index 0000000..4a45135
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Stopbit one-to-one. Cubic cell."
+#define FILENAME "test20.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 1
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 7237102.
diff --git a/src/external/tng_io/src/tests/compression/test21.h b/src/external/tng_io/src/tests/compression/test21.h
new file mode 100644 (file)
index 0000000..773c1da
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Triplet inter. Cubic cell."
+#define FILENAME "test21.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 2
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 6214307.
diff --git a/src/external/tng_io/src/tests/compression/test22.h b/src/external/tng_io/src/tests/compression/test22.h
new file mode 100644 (file)
index 0000000..1d8cfa9
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Triplet one-to-one. Cubic cell."
+#define FILENAME "test22.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 6988699.
diff --git a/src/external/tng_io/src/tests/compression/test23.h b/src/external/tng_io/src/tests/compression/test23.h
new file mode 100644 (file)
index 0000000..950f7ee
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Stopbit interframe. Cubic cell."
+#define FILENAME "test23.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 6
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 6494602.
diff --git a/src/external/tng_io/src/tests/compression/test24.h b/src/external/tng_io/src/tests/compression/test24.h
new file mode 100644 (file)
index 0000000..4b20897
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. BWLZH interframe. Cubic cell."
+#define FILENAME "test24.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 25
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 50
+#define EXPECTED_FILESIZE 153520.
diff --git a/src/external/tng_io/src/tests/compression/test25.h b/src/external/tng_io/src/tests/compression/test25.h
new file mode 100644 (file)
index 0000000..37d777a
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. BWLZH one-to-one. Cubic cell."
+#define FILENAME "test25.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 25
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 50
+#define EXPECTED_FILESIZE 154753.
diff --git a/src/external/tng_io/src/tests/compression/test26.h b/src/external/tng_io/src/tests/compression/test26.h
new file mode 100644 (file)
index 0000000..2c3b304
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "XTC2 algorithm. Orthorhombic cell."
+#define FILENAME "test26.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 20000
+#define INTMAX2 10000
+#define INTMAX3 30000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2861948.
diff --git a/src/external/tng_io/src/tests/compression/test27.h b/src/external/tng_io/src/tests/compression/test27.h
new file mode 100644 (file)
index 0000000..af7c9ff
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "XTC3 algorithm. Orthorhombic cell."
+#define FILENAME "test27.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 20000
+#define INTMAX2 10000
+#define INTMAX3 30000
+#define NFRAMES 200
+#define EXPECTED_FILESIZE 282600.
diff --git a/src/external/tng_io/src/tests/compression/test28.h b/src/external/tng_io/src/tests/compression/test28.h
new file mode 100644 (file)
index 0000000..99a70fc
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Initial coding. Autoselect algorithm. Repetitive molecule. Cubic cell."
+#define FILENAME "test28.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define REGULAR
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING 0
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 0
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 1677619.
diff --git a/src/external/tng_io/src/tests/compression/test29.h b/src/external/tng_io/src/tests/compression/test29.h
new file mode 100644 (file)
index 0000000..be6a8bf
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Position coding. Autoselect algorithm. Repetitive molecule. Cubic cell."
+#define FILENAME "test29.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define REGULAR
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING -1
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 0
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 228148.
diff --git a/src/external/tng_io/src/tests/compression/test3.h b/src/external/tng_io/src/tests/compression/test3.h
new file mode 100644 (file)
index 0000000..768a208
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Triplet one-to-one algorithm. Cubic cell."
+#define FILENAME "test3.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 7
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 4356773.
diff --git a/src/external/tng_io/src/tests/compression/test30.h b/src/external/tng_io/src/tests/compression/test30.h
new file mode 100644 (file)
index 0000000..b719c7b
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame triple algorithm. Large system. Cubic cell."
+#define FILENAME "test30.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 280198420.
diff --git a/src/external/tng_io/src/tests/compression/test31.h b/src/external/tng_io/src/tests/compression/test31.h
new file mode 100644 (file)
index 0000000..c0763e8
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC2 algorithm. Large system. Cubic cell."
+#define FILENAME "test31.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 301463456.
diff --git a/src/external/tng_io/src/tests/compression/test32.h b/src/external/tng_io/src/tests/compression/test32.h
new file mode 100644 (file)
index 0000000..5f2e2f9
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC3 algorithm. Large system. Cubic cell."
+#define FILENAME "test32.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 2
+#define EXPECTED_FILESIZE 31668187.
diff --git a/src/external/tng_io/src/tests/compression/test33.h b/src/external/tng_io/src/tests/compression/test33.h
new file mode 100644 (file)
index 0000000..3d08a65
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame BWLZH algorithm. Large system. Cubic cell."
+#define FILENAME "test33.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 2
+#define EXPECTED_FILESIZE 7121047.
diff --git a/src/external/tng_io/src/tests/compression/test34.h b/src/external/tng_io/src/tests/compression/test34.h
new file mode 100644 (file)
index 0000000..4242c18
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Stop bits algorithm. Large system. Cubic cell."
+#define FILENAME "test34.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 250247372.
diff --git a/src/external/tng_io/src/tests/compression/test35.h b/src/external/tng_io/src/tests/compression/test35.h
new file mode 100644 (file)
index 0000000..8f742b7
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame triple algorithm. Large system. Cubic cell."
+#define FILENAME "test35.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 2
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 243598962.
diff --git a/src/external/tng_io/src/tests/compression/test36.h b/src/external/tng_io/src/tests/compression/test36.h
new file mode 100644 (file)
index 0000000..fba6feb
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Intra frame triple algorithm. Large system. Cubic cell."
+#define FILENAME "test36.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 3
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 290800607.
diff --git a/src/external/tng_io/src/tests/compression/test37.h b/src/external/tng_io/src/tests/compression/test37.h
new file mode 100644 (file)
index 0000000..d2722bf
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC2 algorithm. Large system. Cubic cell."
+#define FILENAME "test37.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 301463256.
diff --git a/src/external/tng_io/src/tests/compression/test38.h b/src/external/tng_io/src/tests/compression/test38.h
new file mode 100644 (file)
index 0000000..26a6740
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC3 algorithm. Large system. Cubic cell."
+#define FILENAME "test38.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 4
+#define EXPECTED_FILESIZE 63482016.
diff --git a/src/external/tng_io/src/tests/compression/test39.h b/src/external/tng_io/src/tests/compression/test39.h
new file mode 100644 (file)
index 0000000..db09a18
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Intra frame BWLZH algorithm. Large system. Cubic cell."
+#define FILENAME "test39.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 9
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 4
+#define EXPECTED_FILESIZE 67631371.
diff --git a/src/external/tng_io/src/tests/compression/test4.h b/src/external/tng_io/src/tests/compression/test4.h
new file mode 100644 (file)
index 0000000..5254a07
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. BWLZH intra algorithm. Cubic cell."
+#define FILENAME "test4.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2572043.
diff --git a/src/external/tng_io/src/tests/compression/test40.h b/src/external/tng_io/src/tests/compression/test40.h
new file mode 100644 (file)
index 0000000..c7511e5
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame BWLZH algorithm. Large system. Cubic cell."
+#define FILENAME "test40.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 8
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 4
+#define EXPECTED_FILESIZE 63822378.
diff --git a/src/external/tng_io/src/tests/compression/test41.h b/src/external/tng_io/src/tests/compression/test41.h
new file mode 100644 (file)
index 0000000..dab390c
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test41.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 53179342.
diff --git a/src/external/tng_io/src/tests/compression/test42.h b/src/external/tng_io/src/tests/compression/test42.h
new file mode 100644 (file)
index 0000000..4452924
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC2 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test42.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 57283715.
diff --git a/src/external/tng_io/src/tests/compression/test43.h b/src/external/tng_io/src/tests/compression/test43.h
new file mode 100644 (file)
index 0000000..915d58e
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC3 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test43.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 3783912.
diff --git a/src/external/tng_io/src/tests/compression/test44.h b/src/external/tng_io/src/tests/compression/test44.h
new file mode 100644 (file)
index 0000000..f749cce
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test44.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 1436901.
diff --git a/src/external/tng_io/src/tests/compression/test45.h b/src/external/tng_io/src/tests/compression/test45.h
new file mode 100644 (file)
index 0000000..e558083
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Stop bits algorithm. High accuracy. Cubic cell."
+#define FILENAME "test45.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 36794379.
diff --git a/src/external/tng_io/src/tests/compression/test46.h b/src/external/tng_io/src/tests/compression/test46.h
new file mode 100644 (file)
index 0000000..dde45ab
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test46.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 2
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 34508770.
diff --git a/src/external/tng_io/src/tests/compression/test47.h b/src/external/tng_io/src/tests/compression/test47.h
new file mode 100644 (file)
index 0000000..a668946
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding.  Intra frame triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test47.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 53174711.
diff --git a/src/external/tng_io/src/tests/compression/test48.h b/src/external/tng_io/src/tests/compression/test48.h
new file mode 100644 (file)
index 0000000..3b2be95
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC2 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test48.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 55638414.
diff --git a/src/external/tng_io/src/tests/compression/test49.h b/src/external/tng_io/src/tests/compression/test49.h
new file mode 100644 (file)
index 0000000..6c618a0
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC3 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test49.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 10
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 3585605.
diff --git a/src/external/tng_io/src/tests/compression/test5.h b/src/external/tng_io/src/tests/compression/test5.h
new file mode 100644 (file)
index 0000000..4c6a638
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC3 algorithm. Cubic cell."
+#define FILENAME "test5.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 3346179.
diff --git a/src/external/tng_io/src/tests/compression/test50.h b/src/external/tng_io/src/tests/compression/test50.h
new file mode 100644 (file)
index 0000000..130f1b9
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Intra frame BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test50.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 9
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 3143379.
diff --git a/src/external/tng_io/src/tests/compression/test51.h b/src/external/tng_io/src/tests/compression/test51.h
new file mode 100644 (file)
index 0000000..c3a57bf
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test51.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 8
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 2897696.
diff --git a/src/external/tng_io/src/tests/compression/test52.h b/src/external/tng_io/src/tests/compression/test52.h
new file mode 100644 (file)
index 0000000..97f37c7
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Stop bits algorithm. High accuracy. Cubic cell."
+#define FILENAME "test52.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 1
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 173083705.
diff --git a/src/external/tng_io/src/tests/compression/test53.h b/src/external/tng_io/src/tests/compression/test53.h
new file mode 100644 (file)
index 0000000..583e0b2
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test53.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 168548573.
diff --git a/src/external/tng_io/src/tests/compression/test54.h b/src/external/tng_io/src/tests/compression/test54.h
new file mode 100644 (file)
index 0000000..50b051f
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Interframe triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test54.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 2
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 161798573.
diff --git a/src/external/tng_io/src/tests/compression/test55.h b/src/external/tng_io/src/tests/compression/test55.h
new file mode 100644 (file)
index 0000000..f8c98f9
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Interframe stop-bits algorithm. High accuracy. Cubic cell."
+#define FILENAME "test55.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 6
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 166298533.
diff --git a/src/external/tng_io/src/tests/compression/test56.h b/src/external/tng_io/src/tests/compression/test56.h
new file mode 100644 (file)
index 0000000..7fb9b23
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Intraframe BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test56.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 23390767.
diff --git a/src/external/tng_io/src/tests/compression/test57.h b/src/external/tng_io/src/tests/compression/test57.h
new file mode 100644 (file)
index 0000000..5c626d8
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Interframe BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test57.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 13817974.
diff --git a/src/external/tng_io/src/tests/compression/test58.h b/src/external/tng_io/src/tests/compression/test58.h
new file mode 100644 (file)
index 0000000..9988106
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Coding. Test float."
+#define FILENAME "test58.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define TEST_FLOAT
+#define EXPECTED_FILESIZE 6986313.
diff --git a/src/external/tng_io/src/tests/compression/test59.h b/src/external/tng_io/src/tests/compression/test59.h
new file mode 100644 (file)
index 0000000..9817976
--- /dev/null
@@ -0,0 +1,28 @@
+#define TESTNAME "Coding. Test write float, read double."
+#define FILENAME "test59.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#ifdef GEN
+#define TEST_FLOAT
+#endif
+#define EXPECTED_FILESIZE 6986313.
diff --git a/src/external/tng_io/src/tests/compression/test6.h b/src/external/tng_io/src/tests/compression/test6.h
new file mode 100644 (file)
index 0000000..ecbc443
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. XTC2 algorithm. Cubic cell."
+#define FILENAME "test6.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2736662.
diff --git a/src/external/tng_io/src/tests/compression/test60.h b/src/external/tng_io/src/tests/compression/test60.h
new file mode 100644 (file)
index 0000000..cf94e3b
--- /dev/null
@@ -0,0 +1,28 @@
+#define TESTNAME "Coding. Test write double, read float."
+#define FILENAME "test60.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#ifndef GEN
+#define TEST_FLOAT
+#endif
+#define EXPECTED_FILESIZE 6986313.
diff --git a/src/external/tng_io/src/tests/compression/test61.h b/src/external/tng_io/src/tests/compression/test61.h
new file mode 100644 (file)
index 0000000..373456d
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Recompression test. Stage 1: Generate"
+#define FILENAME "test61.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 698801.
diff --git a/src/external/tng_io/src/tests/compression/test62.h b/src/external/tng_io/src/tests/compression/test62.h
new file mode 100644 (file)
index 0000000..c8a2b48
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Coding. Recompression test. Stage 2: Recompress"
+#define FILENAME "test62.tng_compress"
+#define RECOMPRESS "test61.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER 0
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 151226.
diff --git a/src/external/tng_io/src/tests/compression/test63.h b/src/external/tng_io/src/tests/compression/test63.h
new file mode 100644 (file)
index 0000000..65a4768
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Coding. Read int and convert to double."
+#define FILENAME "test63.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define INTTODOUBLE
+#define EXPECTED_FILESIZE 698801.
diff --git a/src/external/tng_io/src/tests/compression/test64.h b/src/external/tng_io/src/tests/compression/test64.h
new file mode 100644 (file)
index 0000000..02f99b5
--- /dev/null
@@ -0,0 +1,27 @@
+#define TESTNAME "Coding. Read int and convert to float."
+#define FILENAME "test64.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define INTTOFLOAT
+#define TEST_FLOAT
+#define EXPECTED_FILESIZE 698801.
diff --git a/src/external/tng_io/src/tests/compression/test65.h b/src/external/tng_io/src/tests/compression/test65.h
new file mode 100644 (file)
index 0000000..c7136ee
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions intra frame triple"
+#define FILENAME "test65.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6315.
diff --git a/src/external/tng_io/src/tests/compression/test66.h b/src/external/tng_io/src/tests/compression/test66.h
new file mode 100644 (file)
index 0000000..95597c8
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions xtc2"
+#define FILENAME "test66.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 4465.
diff --git a/src/external/tng_io/src/tests/compression/test67.h b/src/external/tng_io/src/tests/compression/test67.h
new file mode 100644 (file)
index 0000000..ca8fe4b
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions triple one-to-one"
+#define FILENAME "test67.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 7
+#define INITIALCODINGPARAMETER -1
+#define CODING 7
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6315.
diff --git a/src/external/tng_io/src/tests/compression/test68.h b/src/external/tng_io/src/tests/compression/test68.h
new file mode 100644 (file)
index 0000000..00d8032
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions BWLZH intra"
+#define FILENAME "test68.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 9
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 321.
diff --git a/src/external/tng_io/src/tests/compression/test69.h b/src/external/tng_io/src/tests/compression/test69.h
new file mode 100644 (file)
index 0000000..7ee385d
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions xtc3"
+#define FILENAME "test69.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 867.
diff --git a/src/external/tng_io/src/tests/compression/test7.h b/src/external/tng_io/src/tests/compression/test7.h
new file mode 100644 (file)
index 0000000..d0e3532
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Stopbit interframe algorithm. Cubic cell."
+#define FILENAME "test7.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2545049.
diff --git a/src/external/tng_io/src/tests/compression/test70.h b/src/external/tng_io/src/tests/compression/test70.h
new file mode 100644 (file)
index 0000000..b3b4b25
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions stopbit interframe"
+#define FILENAME "test70.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 7548.
diff --git a/src/external/tng_io/src/tests/compression/test71.h b/src/external/tng_io/src/tests/compression/test71.h
new file mode 100644 (file)
index 0000000..c7d0673
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions triple interframe"
+#define FILENAME "test71.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 2
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6315.
diff --git a/src/external/tng_io/src/tests/compression/test72.h b/src/external/tng_io/src/tests/compression/test72.h
new file mode 100644 (file)
index 0000000..633bb50
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions BWLZH inter"
+#define FILENAME "test72.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 8
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 257.
diff --git a/src/external/tng_io/src/tests/compression/test73.h b/src/external/tng_io/src/tests/compression/test73.h
new file mode 100644 (file)
index 0000000..e64af34
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities stopbits one-to-one"
+#define FILENAME "test73.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 1
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 13863.
diff --git a/src/external/tng_io/src/tests/compression/test74.h b/src/external/tng_io/src/tests/compression/test74.h
new file mode 100644 (file)
index 0000000..73ada23
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities triplet one-to-one"
+#define FILENAME "test74.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 12622.
diff --git a/src/external/tng_io/src/tests/compression/test75.h b/src/external/tng_io/src/tests/compression/test75.h
new file mode 100644 (file)
index 0000000..4fc004b
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities BWLZH one-to-one"
+#define FILENAME "test75.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 9
+#define INITIALVELCODINGPARAMETER 0
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6628.
diff --git a/src/external/tng_io/src/tests/compression/test76.h b/src/external/tng_io/src/tests/compression/test76.h
new file mode 100644 (file)
index 0000000..4a51ecb
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities triplet inter"
+#define FILENAME "test76.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 2
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 12630.
diff --git a/src/external/tng_io/src/tests/compression/test77.h b/src/external/tng_io/src/tests/compression/test77.h
new file mode 100644 (file)
index 0000000..d9394d8
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities stopbits inter"
+#define FILENAME "test77.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 6
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 13863.
diff --git a/src/external/tng_io/src/tests/compression/test78.h b/src/external/tng_io/src/tests/compression/test78.h
new file mode 100644 (file)
index 0000000..f70592e
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities BWLZH inter"
+#define FILENAME "test78.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6572.
diff --git a/src/external/tng_io/src/tests/compression/test8.h b/src/external/tng_io/src/tests/compression/test8.h
new file mode 100644 (file)
index 0000000..2ce103c
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Stopbit interframe algorithm with intraframe compression as initial. Cubic cell."
+#define FILENAME "test8.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2544876.
diff --git a/src/external/tng_io/src/tests/compression/test9.h b/src/external/tng_io/src/tests/compression/test9.h
new file mode 100644 (file)
index 0000000..f0672e2
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Triple interframe algorithm. Cubic cell."
+#define FILENAME "test9.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 2
+#define CODINGPARAMETER -1 
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2418212.
diff --git a/src/external/tng_io/src/tests/compression/test_tng_compress_read.bat b/src/external/tng_io/src/tests/compression/test_tng_compress_read.bat
new file mode 100644 (file)
index 0000000..b3e779b
--- /dev/null
@@ -0,0 +1,13 @@
+@echo off
+setlocal enableextensions enabledelayedexpansion
+SET /A I=0
+:start
+SET /A I+=1
+test_tng_compress_read%I%
+IF "%I%" == "78" (
+  GOTO end
+) ELSE (
+  GOTO start
+)
+:end
+endlocal
diff --git a/src/external/tng_io/src/tests/compression/test_tng_compress_read.sh b/src/external/tng_io/src/tests/compression/test_tng_compress_read.sh
new file mode 100755 (executable)
index 0000000..20f93f0
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+numtests=78
+for x in $(seq 1 $numtests); do
+    ./test_tng_compress_read$x
+done
\ No newline at end of file
diff --git a/src/external/tng_io/src/tests/compression/test_tng_compress_write.bat b/src/external/tng_io/src/tests/compression/test_tng_compress_write.bat
new file mode 100644 (file)
index 0000000..2effa0e
--- /dev/null
@@ -0,0 +1,13 @@
+@echo off
+setlocal enableextensions enabledelayedexpansion
+SET /A I=0
+:start
+SET /A I+=1
+test_tng_compress_gen%I%
+IF "%I%" == "78" (
+  GOTO end
+) ELSE (
+  GOTO start
+)
+:end
+endlocal
diff --git a/src/external/tng_io/src/tests/compression/test_tng_compress_write.sh b/src/external/tng_io/src/tests/compression/test_tng_compress_write.sh
new file mode 100755 (executable)
index 0000000..4545991
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+numtests=78
+for x in $(seq 1 $numtests); do
+    ./test_tng_compress_gen$x
+done
\ No newline at end of file
diff --git a/src/external/tng_io/src/tests/compression/testsuite.c b/src/external/tng_io/src/tests/compression/testsuite.c
new file mode 100644 (file)
index 0000000..2cfe05e
--- /dev/null
@@ -0,0 +1,978 @@
+/* tng compression routines */
+
+/* Only modify testsuite.c
+ *Then* run testsuite.sh to perform the test.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "../../../include/compression/tng_compress.h"
+#include TESTPARAM
+
+#ifdef TEST_FLOAT
+#define REAL float
+#else
+#define REAL double
+#endif
+
+#ifndef TNG_COMPRESS_FILES_DIR
+#define TNG_COMPRESS_FILES_DIR ""
+#endif
+
+#define FUDGE 1.1 /* 10% off target precision is acceptable */
+
+static void keepinbox(int *val)
+{
+  while (val[0]>INTMAX1)
+    val[0]-=(INTMAX1-INTMIN1+1);
+  while (val[0]<INTMIN1)
+    val[0]+=(INTMAX1-INTMIN1+1);
+  while (val[1]>INTMAX2)
+    val[1]-=(INTMAX2-INTMIN2+1);
+  while (val[1]<INTMIN2)
+    val[1]+=(INTMAX2-INTMIN2+1);
+  while (val[2]>INTMAX3)
+    val[2]-=(INTMAX3-INTMIN3+1);
+  while (val[2]<INTMIN3)
+    val[2]+=(INTMAX3-INTMIN3+1);
+}
+
+static int intsintable[128]={
+0 , 3215 , 6423 , 9615 , 12785 , 15923 , 19023 , 22078 ,
+25079 , 28019 , 30892 , 33691 , 36409 , 39039 , 41574 , 44010 ,
+46340 , 48558 , 50659 , 52638 , 54490 , 56211 , 57796 , 59242 ,
+60546 , 61704 , 62713 , 63570 , 64275 , 64825 , 65219 , 65456 ,
+65535 , 65456 , 65219 , 64825 , 64275 , 63570 , 62713 , 61704 ,
+60546 , 59242 , 57796 , 56211 , 54490 , 52638 , 50659 , 48558 ,
+46340 , 44010 , 41574 , 39039 , 36409 , 33691 , 30892 , 28019 ,
+25079 , 22078 , 19023 , 15923 , 12785 , 9615 , 6423 , 3215 ,
+0 , -3215 , -6423 , -9615 , -12785 , -15923 , -19023 , -22078 ,
+-25079 , -28019 , -30892 , -33691 , -36409 , -39039 , -41574 , -44010 ,
+-46340 , -48558 , -50659 , -52638 , -54490 , -56211 , -57796 , -59242 ,
+-60546 , -61704 , -62713 , -63570 , -64275 , -64825 , -65219 , -65456 ,
+-65535 , -65456 , -65219 , -64825 , -64275 , -63570 , -62713 , -61704 ,
+-60546 , -59242 , -57796 , -56211 , -54490 , -52638 , -50659 , -48558 ,
+-46340 , -44010 , -41574 , -39039 , -36409 , -33691 , -30892 , -28019 ,
+-25079 , -22078 , -19023 , -15923 , -12785 , -9615 , -6423 , -3215 ,
+};
+
+static int intsin(int i)
+{
+  int sign=1;
+  if (i<0)
+    {
+      i=0;
+      sign=-1;
+    }
+  return sign*intsintable[i%128];
+}
+
+static int intcos(int i)
+{
+  if (i<0)
+    i=0;
+  return intsin(i+32);
+}
+
+static void molecule(int *target,
+                     int *base,
+                     int length,
+                     int scale, int *direction,
+                     int flip,
+                     int iframe)
+{
+  int i;
+  for (i=0; i<length; i++)
+    {
+      int ifl=i;
+      if ((i==0) && (flip) && (length>1))
+        ifl=1;
+      else if ((i==1) && (flip) && (length>1))
+        ifl=0;
+      target[ifl*3]=base[0]+(intsin((i+iframe)*direction[0])*scale)/256;
+      target[ifl*3+1]=base[1]+(intcos((i+iframe)*direction[1])*scale)/256;
+      target[ifl*3+2]=base[2]+(intcos((i+iframe)*direction[2])*scale)/256;
+      keepinbox(target+ifl*3);
+    }
+}
+
+#ifndef FRAMESCALE
+#define FRAMESCALE 1
+#endif
+
+static void genibox(int *intbox, int iframe)
+{
+  int molecule_length=1;
+  int molpos[3];
+  int direction[3]={1,1,1};
+  int scale=1;
+  int flip=0;
+  int i=0;
+  molpos[0]=intsin(iframe*FRAMESCALE)/32;
+  molpos[1]=1+intcos(iframe*FRAMESCALE)/32;
+  molpos[2]=2+intsin(iframe*FRAMESCALE)/16;
+  keepinbox(molpos);
+  while (i<NATOMS)
+    {
+      int this_mol_length=molecule_length;
+      int dir;
+#ifdef REGULAR
+      this_mol_length=4;
+      flip=0;
+      scale=1;
+#endif
+      if (i+this_mol_length>NATOMS)
+        this_mol_length=NATOMS-i;
+      /* We must test the large rle as well. This requires special
+         sequencies to get triggered. So insert these from time to
+         time */
+#ifndef REGULAR
+      if ((i%10)==0)
+        {
+          int j;
+          intbox[i*3]=molpos[0];
+          intbox[i*3+1]=molpos[1];
+          intbox[i*3+2]=molpos[2];
+          for (j=1; j<this_mol_length; j++)
+            {
+              intbox[(i+j)*3]=intbox[(i+j-1)*3]+(INTMAX1-INTMIN1+1)/5;
+              intbox[(i+j)*3+1]=intbox[(i+j-1)*3+1]+(INTMAX2-INTMIN2+1)/5;
+              intbox[(i+j)*3+2]=intbox[(i+j-1)*3+2]+(INTMAX3-INTMIN3+1)/5;
+              keepinbox(intbox+(i+j)*3);
+            }
+        }
+      else
+#endif
+        molecule(intbox+i*3,molpos,this_mol_length,scale,direction,flip,iframe*FRAMESCALE);
+      i+=this_mol_length;
+      dir=1;
+      if (intsin(i*3)<0)
+        dir=-1;
+      molpos[0]+=dir*(INTMAX1-INTMIN1+1)/20;
+      dir=1;
+      if (intsin(i*5)<0)
+        dir=-1;
+      molpos[1]+=dir*(INTMAX2-INTMIN2+1)/20;
+      dir=1;
+      if (intsin(i*7)<0)
+        dir=-1;
+      molpos[2]+=dir*(INTMAX3-INTMIN3+1)/20;
+      keepinbox(molpos);
+
+      direction[0]=((direction[0]+1)%7)+1;
+      direction[1]=((direction[1]+1)%3)+1;
+      direction[2]=((direction[2]+1)%6)+1;
+
+      scale++;
+      if (scale>5)
+        scale=1;
+
+      molecule_length++;
+      if (molecule_length>30)
+        molecule_length=1;
+      if (i%9)
+        flip=1-flip;
+    }
+}
+
+static void genivelbox(int *intvelbox, int iframe)
+{
+  int i;
+  for (i=0; i<NATOMS; i++)
+    {
+#ifdef VELINTMUL
+      intvelbox[i*3]=((intsin((i+iframe*FRAMESCALE)*3))/10)*VELINTMUL+i;
+      intvelbox[i*3+1]=1+((intcos((i+iframe*FRAMESCALE)*5))/10)*VELINTMUL+i;
+      intvelbox[i*3+2]=2+((intsin((i+iframe*FRAMESCALE)*7)+intcos((i+iframe*FRAMESCALE)*9))/20)*VELINTMUL+i;
+#else
+      intvelbox[i*3]=((intsin((i+iframe*FRAMESCALE)*3))/10);
+      intvelbox[i*3+1]=1+((intcos((i+iframe*FRAMESCALE)*5))/10);
+      intvelbox[i*3+2]=2+((intsin((i+iframe*FRAMESCALE)*7)+intcos((i+iframe*FRAMESCALE)*9))/20);
+#endif
+    }
+}
+
+#ifndef STRIDE1
+#define STRIDE1 3
+#endif
+
+#ifndef STRIDE2
+#define STRIDE2 3
+#endif
+
+#ifndef GENPRECISION
+#define GENPRECISION PRECISION
+#endif
+
+#ifndef GENVELPRECISION
+#define GENVELPRECISION VELPRECISION
+#endif
+
+static void realbox(int *intbox, REAL *realbox, int stride)
+{
+  int i,j;
+  for (i=0; i<NATOMS; i++)
+    {
+      for (j=0; j<3; j++)
+        realbox[i*stride+j]=(REAL)(intbox[i*3+j]*GENPRECISION*SCALE);
+      for (j=3; j<stride; j++)
+        realbox[i*stride+j]=0.;
+    }
+}
+
+static void realvelbox(int *intbox, REAL *realbox, int stride)
+{
+  int i,j;
+  for (i=0; i<NATOMS; i++)
+    {
+      for (j=0; j<3; j++)
+        realbox[i*stride+j]=(REAL)(intbox[i*3+j]*GENVELPRECISION*SCALE);
+      for (j=3; j<stride; j++)
+        realbox[i*stride+j]=0.;
+    }
+}
+
+static int equalarr(REAL *arr1, REAL *arr2, REAL prec, int len, int itemlen, int stride1, int stride2)
+{
+  REAL maxdiff=0.;
+  int i,j;
+  for (i=0; i<len; i++)
+    {
+      for (j=0; j<itemlen; j++)
+        if (fabs(arr1[i*stride1+j]-arr2[i*stride2+j])>maxdiff)
+          maxdiff=(REAL)fabs(arr1[i*stride1+j]-arr2[i*stride2+j]);
+    }
+#if 0
+  for (i=0; i<len; i++)
+    {
+      for (j=0; j<itemlen; j++)
+        printf("%d %d: %g %g\n",i,j,arr1[i*stride1+j],arr2[i*stride2+j]);
+    }
+#endif
+#if 0
+  fprintf(stderr,"Error is %g. Acceptable error is %g.\n",maxdiff,prec*0.5*FUDGE);
+#endif
+  if (maxdiff>prec*0.5*FUDGE)
+    {
+      return 0;
+    }
+  else
+    return 1;
+}
+
+struct tng_file
+{
+  FILE *f;
+  int natoms;
+  int chunky;
+  REAL precision;
+  REAL velprecision;
+  int initial_coding;
+  int initial_coding_parameter;
+  int coding;
+  int coding_parameter;
+  int initial_velcoding;
+  int initial_velcoding_parameter;
+  int velcoding;
+  int velcoding_parameter;
+  int speed;
+  int nframes;
+  int nframes_delivered;
+  int writevel;
+  REAL *pos;
+  REAL *vel;
+  int *ipos;
+  int *ivel;
+  unsigned int prec_hi, prec_lo;
+  unsigned int velprec_hi, velprec_lo;
+};
+
+static size_t fwrite_int_le(int *x,FILE *f)
+{
+  unsigned char c[4];
+  unsigned int i=(unsigned int)*x;
+  c[0]=(unsigned char)(i&0xFFU);
+  c[1]=(unsigned char)((i>>8)&0xFFU);
+  c[2]=(unsigned char)((i>>16)&0xFFU);
+  c[3]=(unsigned char)((i>>24)&0xFFU);
+  return fwrite(c,1,4,f);
+}
+
+static size_t fread_int_le(int *x,FILE *f)
+{
+  unsigned char c[4];
+  unsigned int i;
+  size_t n=fread(c,1,4,f);
+  if (n)
+    {
+      i=(((unsigned int)c[3])<<24)|(((unsigned int)c[2])<<16)|(((unsigned int)c[1])<<8)|((unsigned int)c[0]);
+      *x=(int)i;
+    }
+  return n;
+}
+
+static struct tng_file *open_tng_file_write(char *filename,
+                                            int natoms,int chunky,
+                                            REAL precision,
+                                            int writevel,
+                                            REAL velprecision,
+                                            int initial_coding,
+                                            int initial_coding_parameter,
+                                            int coding,
+                                            int coding_parameter,
+                                            int initial_velcoding,
+                                            int initial_velcoding_parameter,
+                                            int velcoding,
+                                            int velcoding_parameter,
+                                            int speed)
+{
+  struct tng_file *tng_file=malloc(sizeof *tng_file);
+  tng_file->pos=NULL;
+  tng_file->vel=NULL;
+  tng_file->ipos=NULL;
+  tng_file->ivel=NULL;
+  tng_file->nframes=0;
+  tng_file->chunky=chunky;
+  tng_file->precision=precision;
+  tng_file->natoms=natoms;
+  tng_file->writevel=writevel;
+  tng_file->velprecision=velprecision;
+  tng_file->initial_coding=initial_coding;
+  tng_file->initial_coding_parameter=initial_coding_parameter;
+  tng_file->coding=coding;
+  tng_file->coding_parameter=coding_parameter;
+  tng_file->initial_velcoding=initial_velcoding;
+  tng_file->initial_velcoding_parameter=initial_velcoding_parameter;
+  tng_file->velcoding=velcoding;
+  tng_file->velcoding_parameter=velcoding_parameter;
+  tng_file->speed=speed;
+  tng_file->pos=malloc(natoms*chunky*3*sizeof *tng_file->pos);
+  tng_file->f=fopen(filename,"wb");
+  if (writevel)
+    tng_file->vel=malloc(natoms*chunky*3*sizeof *tng_file->vel);
+  fwrite_int_le(&natoms,tng_file->f);
+  return tng_file;
+}
+
+static struct tng_file *open_tng_file_write_int(char *filename,
+                                                int natoms,int chunky,
+                                                int writevel,
+                                                int initial_coding,
+                                                int initial_coding_parameter,
+                                                int coding,
+                                                int coding_parameter,
+                                                int initial_velcoding,
+                                                int initial_velcoding_parameter,
+                                                int velcoding,
+                                                int velcoding_parameter,
+                                                int speed)
+{
+  struct tng_file *tng_file=malloc(sizeof *tng_file);
+  tng_file->pos=NULL;
+  tng_file->vel=NULL;
+  tng_file->ipos=NULL;
+  tng_file->ivel=NULL;
+  tng_file->nframes=0;
+  tng_file->chunky=chunky;
+  tng_file->natoms=natoms;
+  tng_file->writevel=writevel;
+  tng_file->initial_coding=initial_coding;
+  tng_file->initial_coding_parameter=initial_coding_parameter;
+  tng_file->coding=coding;
+  tng_file->coding_parameter=coding_parameter;
+  tng_file->initial_velcoding=initial_velcoding;
+  tng_file->initial_velcoding_parameter=initial_velcoding_parameter;
+  tng_file->velcoding=velcoding;
+  tng_file->velcoding_parameter=velcoding_parameter;
+  tng_file->speed=speed;
+  tng_file->ipos=malloc(natoms*chunky*3*sizeof *tng_file->ipos);
+  tng_file->f=fopen(filename,"wb");
+  if (writevel)
+    tng_file->ivel=malloc(natoms*chunky*3*sizeof *tng_file->ivel);
+  fwrite_int_le(&natoms,tng_file->f);
+  return tng_file;
+}
+
+static void flush_tng_frames(struct tng_file *tng_file,
+                             unsigned long prec_hi, unsigned long prec_lo,
+                             unsigned long velprec_hi, unsigned long velprec_lo)
+{
+  int algo[4];
+  char *buf;
+  int nitems;
+
+  /* Make sure these variables are used to avoid compilation warnings */
+  (void)prec_hi;
+  (void)prec_lo;
+  (void)velprec_hi;
+  (void)velprec_lo;
+
+  fwrite_int_le(&tng_file->nframes,tng_file->f);
+  algo[0]=tng_file->initial_coding;
+  algo[1]=tng_file->initial_coding_parameter;
+  algo[2]=tng_file->coding;
+  algo[3]=tng_file->coding_parameter;
+#ifdef RECOMPRESS
+  buf=tng_compress_pos_int(tng_file->ipos,
+                             tng_file->natoms,
+                             tng_file->nframes,
+                             prec_hi,prec_lo,
+                             tng_file->speed,algo,&nitems);
+#else /* RECOMPRESS */
+#ifdef TEST_FLOAT
+  buf=tng_compress_pos_float(tng_file->pos,
+                             tng_file->natoms,
+                             tng_file->nframes,
+                             tng_file->precision,
+                             tng_file->speed,algo,&nitems);
+#else  /* TEST_FLOAT */
+  buf=tng_compress_pos(tng_file->pos,
+                       tng_file->natoms,
+                       tng_file->nframes,
+                       tng_file->precision,
+                       tng_file->speed,algo,&nitems);
+#endif  /* TEST_FLOAT */
+#endif /* RECOMPRESS */
+  tng_file->initial_coding=algo[0];
+  tng_file->initial_coding_parameter=algo[1];
+  tng_file->coding=algo[2];
+  tng_file->coding_parameter=algo[3];
+  fwrite_int_le(&nitems,tng_file->f);
+  fwrite(buf,1,nitems,tng_file->f);
+  free(buf);
+  if (tng_file->writevel)
+    {
+      algo[0]=tng_file->initial_velcoding;
+      algo[1]=tng_file->initial_velcoding_parameter;
+      algo[2]=tng_file->velcoding;
+      algo[3]=tng_file->velcoding_parameter;
+#ifdef RECOMPRESS
+      buf=tng_compress_vel_int(tng_file->ivel,
+                               tng_file->natoms,
+                               tng_file->nframes,
+                               velprec_hi,velprec_lo,
+                               tng_file->speed,algo,&nitems);
+#else /* RECOMPRESS */
+#ifdef TEST_FLOAT
+      buf=tng_compress_vel_float(tng_file->vel,
+                           tng_file->natoms,
+                           tng_file->nframes,
+                           tng_file->velprecision,
+                           tng_file->speed,algo,&nitems);
+#else /* TEST_FLOAT */
+      buf=tng_compress_vel(tng_file->vel,
+                           tng_file->natoms,
+                           tng_file->nframes,
+                           tng_file->velprecision,
+                           tng_file->speed,algo,&nitems);
+#endif /* TEST_FLOAT */
+#endif /* RECOMPRESS */
+      tng_file->initial_velcoding=algo[0];
+      tng_file->initial_velcoding_parameter=algo[1];
+      tng_file->velcoding=algo[2];
+      tng_file->velcoding_parameter=algo[3];
+      fwrite_int_le(&nitems,tng_file->f);
+      fwrite(buf,1,nitems,tng_file->f);
+      free(buf);
+    }
+  tng_file->nframes=0;
+}
+
+static void write_tng_file(struct tng_file *tng_file,
+                           REAL *pos,REAL *vel)
+{
+  memcpy(tng_file->pos+tng_file->nframes*tng_file->natoms*3,pos,tng_file->natoms*3*sizeof *tng_file->pos);
+  if (tng_file->writevel)
+    memcpy(tng_file->vel+tng_file->nframes*tng_file->natoms*3,vel,tng_file->natoms*3*sizeof *tng_file->vel);
+  tng_file->nframes++;
+  if (tng_file->nframes==tng_file->chunky)
+    flush_tng_frames(tng_file,0,0,0,0);
+}
+
+static void write_tng_file_int(struct tng_file *tng_file,
+                               int *ipos,int *ivel,
+                               unsigned long prec_hi, unsigned long prec_lo,
+                               unsigned long velprec_hi, unsigned long velprec_lo)
+{
+  memcpy(tng_file->ipos+tng_file->nframes*tng_file->natoms*3,ipos,tng_file->natoms*3*sizeof *tng_file->ipos);
+  if (tng_file->writevel)
+    memcpy(tng_file->ivel+tng_file->nframes*tng_file->natoms*3,ivel,tng_file->natoms*3*sizeof *tng_file->ivel);
+  tng_file->nframes++;
+  if (tng_file->nframes==tng_file->chunky)
+    flush_tng_frames(tng_file,prec_hi,prec_lo,velprec_hi,velprec_lo);
+  tng_file->prec_hi=prec_hi;
+  tng_file->prec_lo=prec_lo;
+  tng_file->velprec_hi=velprec_hi;
+  tng_file->velprec_lo=velprec_lo;
+}
+
+static void close_tng_file_write(struct tng_file *tng_file)
+{
+  if (tng_file->nframes)
+    flush_tng_frames(tng_file,tng_file->prec_hi,tng_file->prec_lo,tng_file->velprec_hi,tng_file->velprec_lo);
+  fclose(tng_file->f);
+  free(tng_file->pos);
+  free(tng_file->vel);
+  free(tng_file->ipos);
+  free(tng_file->ivel);
+  free(tng_file);
+}
+
+static struct tng_file *open_tng_file_read(char *filename, int writevel)
+{
+  struct tng_file *tng_file=malloc(sizeof *tng_file);
+  tng_file->pos=NULL;
+  tng_file->vel=NULL;
+  tng_file->ipos=NULL;
+  tng_file->ivel=NULL;
+  tng_file->f=fopen(filename,"rb");
+  tng_file->nframes=0;
+  tng_file->nframes_delivered=0;
+  tng_file->writevel=writevel;
+  if (tng_file->f)
+    fread_int_le(&tng_file->natoms,tng_file->f);
+  else
+    {
+      free(tng_file);
+      tng_file=NULL;
+    }
+  return tng_file;
+}
+
+static struct tng_file *open_tng_file_read_int(char *filename, int writevel)
+{
+  struct tng_file *tng_file=malloc(sizeof *tng_file);
+  tng_file->pos=NULL;
+  tng_file->vel=NULL;
+  tng_file->ipos=NULL;
+  tng_file->ivel=NULL;
+  tng_file->f=fopen(filename,"rb");
+  tng_file->nframes=0;
+  tng_file->nframes_delivered=0;
+  tng_file->writevel=writevel;
+  if (tng_file->f)
+    fread_int_le(&tng_file->natoms,tng_file->f);
+  else
+    {
+      free(tng_file);
+      tng_file=NULL;
+    }
+  return tng_file;
+}
+
+static int read_tng_file(struct tng_file *tng_file,
+                         REAL *pos,
+                         REAL *vel)
+{
+  if (tng_file->nframes==tng_file->nframes_delivered)
+    {
+      int nitems;
+      char *buf;
+      free(tng_file->pos);
+      free(tng_file->vel);
+      if (!fread_int_le(&tng_file->nframes,tng_file->f))
+        return 1;
+      if (!fread_int_le(&nitems,tng_file->f))
+        return 1;
+      buf=malloc(nitems);
+      if (!fread(buf,1,nitems,tng_file->f))
+      {
+          free(buf);
+          return 1;
+      }
+      tng_file->pos=malloc(tng_file->natoms*tng_file->nframes*3*sizeof *tng_file->pos);
+      if (tng_file->writevel)
+        tng_file->vel=malloc(tng_file->natoms*tng_file->nframes*3*sizeof *tng_file->vel);
+#if 0
+      {
+        int natoms, nframes, algo[4];
+        double precision;
+        int ivel;
+        char *initial_coding, *coding;
+        tng_compress_inquire(buf,&ivel,&natoms,&nframes,&precision,algo);
+        initial_coding=tng_compress_initial_pos_algo(algo);
+        coding=tng_compress_pos_algo(algo);
+        printf("ivel=%d natoms=%d nframes=%d precision=%g initial pos=%s pos=%s\n",ivel,natoms,nframes,precision,initial_coding,coding);
+      }
+#endif
+#ifdef TEST_FLOAT
+      tng_compress_uncompress_float(buf,tng_file->pos);
+#else
+      tng_compress_uncompress(buf,tng_file->pos);
+#endif
+      free(buf);
+      if (tng_file->writevel)
+        {
+          if (!fread_int_le(&nitems,tng_file->f))
+            return 1;
+          buf=malloc(nitems);
+          if (!fread(buf,1,nitems,tng_file->f))
+          {
+              free(buf);
+              return 1;
+          }
+#if 0
+          {
+            int natoms, nframes, algo[4];
+            double precision;
+            int ivel;
+            char *initial_coding, *coding;
+            tng_compress_inquire(buf,&ivel,&natoms,&nframes,&precision,algo);
+            initial_coding=tng_compress_initial_vel_algo(algo);
+            coding=tng_compress_vel_algo(algo);
+            printf("ivel=%d natoms=%d nframes=%d precision=%g initial vel=%s vel=%s\n",ivel,natoms,nframes,precision,initial_coding,coding);
+          }
+#endif
+#ifdef TEST_FLOAT
+          tng_compress_uncompress_float(buf,tng_file->vel);
+#else
+          tng_compress_uncompress(buf,tng_file->vel);
+#endif
+          free(buf);
+        }
+      tng_file->nframes_delivered=0;
+    }
+  memcpy(pos,tng_file->pos+tng_file->nframes_delivered*tng_file->natoms*3,tng_file->natoms*3*sizeof *pos);
+  if (tng_file->writevel)
+    memcpy(vel,tng_file->vel+tng_file->nframes_delivered*tng_file->natoms*3,tng_file->natoms*3*sizeof *vel);
+  tng_file->nframes_delivered++;
+  return 0;
+}
+
+static int read_tng_file_int(struct tng_file *tng_file,
+                             int *ipos,
+                             int *ivel,
+                             unsigned long *prec_hi, unsigned long *prec_lo,
+                             unsigned long *velprec_hi, unsigned long *velprec_lo)
+{
+  if (tng_file->nframes==tng_file->nframes_delivered)
+    {
+      int nitems;
+      char *buf;
+      free(tng_file->ipos);
+      free(tng_file->ivel);
+      if (!fread_int_le(&tng_file->nframes,tng_file->f))
+        return 1;
+      if (!fread_int_le(&nitems,tng_file->f))
+        return 1;
+      buf=malloc(nitems);
+      if (!fread(buf,1,nitems,tng_file->f))
+      {
+          free(buf);
+          return 1;
+      }
+      tng_file->ipos=malloc(tng_file->natoms*tng_file->nframes*3*sizeof *tng_file->ipos);
+      if (tng_file->writevel)
+        tng_file->ivel=malloc(tng_file->natoms*tng_file->nframes*3*sizeof *tng_file->ivel);
+      tng_compress_uncompress_int(buf,tng_file->ipos,prec_hi,prec_lo);
+      free(buf);
+      if (tng_file->writevel)
+        {
+          if (!fread_int_le(&nitems,tng_file->f))
+            return 1;
+          buf=malloc(nitems);
+          if (!fread(buf,1,nitems,tng_file->f))
+          {
+              free(buf);
+              return 1;
+          }
+          tng_compress_uncompress_int(buf,tng_file->ivel,velprec_hi,velprec_lo);
+          free(buf);
+        }
+      tng_file->nframes_delivered=0;
+    }
+  memcpy(ipos,tng_file->ipos+tng_file->nframes_delivered*tng_file->natoms*3,tng_file->natoms*3*sizeof *ipos);
+  if (tng_file->writevel)
+    memcpy(ivel,tng_file->ivel+tng_file->nframes_delivered*tng_file->natoms*3,tng_file->natoms*3*sizeof *ivel);
+  tng_file->nframes_delivered++;
+  return 0;
+}
+
+static void close_tng_file_read(struct tng_file *tng_file)
+{
+  free(tng_file->vel);
+  free(tng_file->pos);
+  free(tng_file->ivel);
+  free(tng_file->ipos);
+  fclose(tng_file->f);
+  free(tng_file);
+}
+
+
+
+#ifndef EXPECTED_FILESIZE
+#define EXPECTED_FILESIZE 1
+#endif
+
+#ifndef INITIALVELCODING
+#define INITIALVELCODING -1
+#endif
+#ifndef INITIALVELCODINGPARAMETER
+#define INITIALVELCODINGPARAMETER -1
+#endif
+
+#ifndef SPEED
+#define SPEED 5
+#endif
+
+/* Return value 1 means file error.
+   Return value 4 means coding error in coordinates.
+   Return value 5 means coding error in velocities.
+   Return value 9 means filesize seems too off.
+
+   Return value 100+ means test specific error.
+ */
+static int algotest()
+{
+  int i;
+  int *intbox=malloc(NATOMS*3*sizeof *intbox);
+  int *intvelbox=malloc(NATOMS*3*sizeof *intvelbox);
+#ifdef RECOMPRESS
+  unsigned long pos_prec_hi,pos_prec_lo;
+  unsigned long vel_prec_hi,vel_prec_lo;
+#endif
+  REAL *box1=malloc(NATOMS*STRIDE1*sizeof *box1);
+  REAL *velbox1=malloc(NATOMS*STRIDE1*sizeof *velbox1);
+  int startframe=0;
+  int endframe=NFRAMES;
+#ifdef GEN
+  FILE *file;
+  REAL filesize;
+  REAL *box2=0;
+  REAL *velbox2=0;
+#else
+  int readreturn;
+  REAL *box2=malloc(NATOMS*STRIDE2*sizeof *box2);
+  REAL *velbox2=malloc(NATOMS*STRIDE2*sizeof *velbox2);
+#endif
+#ifdef RECOMPRESS
+  void *dumpfile=open_tng_file_write_int(TNG_COMPRESS_FILES_DIR FILENAME,NATOMS,CHUNKY,
+                                     WRITEVEL,
+                                     INITIALCODING,
+                                     INITIALCODINGPARAMETER,CODING,CODINGPARAMETER,
+                                     INITIALVELCODING,INITIALVELCODINGPARAMETER,
+                                     VELCODING,VELCODINGPARAMETER,SPEED);
+  void *dumpfile_recompress=open_tng_file_read_int(TNG_COMPRESS_FILES_DIR RECOMPRESS,WRITEVEL);
+  if (!dumpfile_recompress)
+    {
+      free(intbox);
+      free(intvelbox);
+      free(box1);
+      free(velbox1);
+      if(box2)
+        free(box2);
+      if(velbox2)
+        free(velbox2);
+      return 1;
+    }
+#else /* RECOMPRESS */
+#ifdef GEN
+  void *dumpfile=open_tng_file_write(TNG_COMPRESS_FILES_DIR FILENAME,NATOMS,CHUNKY,
+                                     PRECISION,WRITEVEL,VELPRECISION,
+                                     INITIALCODING,
+                                     INITIALCODINGPARAMETER,CODING,CODINGPARAMETER,
+                                     INITIALVELCODING,INITIALVELCODINGPARAMETER,
+                                     VELCODING,VELCODINGPARAMETER,SPEED);
+#else
+  void *dumpfile=open_tng_file_read(TNG_COMPRESS_FILES_DIR FILENAME,WRITEVEL);
+#endif
+#endif /* RECOMPRESS */
+  if (!dumpfile)
+    {
+      free(intbox);
+      free(intvelbox);
+      free(box1);
+      free(velbox1);
+      if(box2)
+        free(box2);
+      if(velbox2)
+        free(velbox2);
+      return 1;
+    }
+  for (i=startframe; i<endframe; i++)
+    {
+#ifdef RECOMPRESS
+      unsigned long prec_hi, prec_lo;
+      unsigned long velprec_hi, velprec_lo;
+      if (read_tng_file_int(dumpfile_recompress,intbox,intvelbox,&prec_hi,&prec_lo,&velprec_hi,&velprec_lo))
+        return 1;
+      write_tng_file_int(dumpfile,intbox,intvelbox,prec_hi,prec_lo,velprec_hi,velprec_lo);
+#else /* RECOMPRESS */
+      genibox(intbox,i);
+      realbox(intbox,box1,STRIDE1);
+#if WRITEVEL
+      genivelbox(intvelbox,i);
+      realvelbox(intvelbox,velbox1,STRIDE1);
+#endif
+#ifdef GEN
+      write_tng_file(dumpfile,box1,velbox1);
+#else /* GEN */
+#ifdef INTTOFLOAT
+      {
+        unsigned long prec_hi, prec_lo;
+        unsigned long velprec_hi, velprec_lo;
+        readreturn=read_tng_file_int(dumpfile,intbox,intvelbox,&prec_hi,&prec_lo,&velprec_hi,&velprec_lo);
+        if (!readreturn)
+          {
+            tng_compress_int_to_float(intbox,prec_hi,prec_lo,NATOMS,1,box2);
+#if WRITEVEL
+            tng_compress_int_to_float(intvelbox,velprec_hi,velprec_lo,NATOMS,1,velbox2);
+#endif
+          }
+      }
+#else /* INTTOFLOAT */
+#ifdef INTTODOUBLE
+      {
+        unsigned long prec_hi, prec_lo;
+        unsigned long velprec_hi, velprec_lo;
+        readreturn=read_tng_file_int(dumpfile,intbox,intvelbox,&prec_hi,&prec_lo,&velprec_hi,&velprec_lo);
+        if (!readreturn)
+          {
+            tng_compress_int_to_double(intbox,prec_hi,prec_lo,NATOMS,1,box2);
+#if WRITEVEL
+            tng_compress_int_to_double(intvelbox,velprec_hi,velprec_lo,NATOMS,1,velbox2);
+#endif
+          }
+      }
+#else /* INTTODOUBLE */
+      readreturn=read_tng_file(dumpfile,box2,velbox2);
+#endif /* INTTODOUBLE */
+#endif /* INTTOFLOAT */
+      if (readreturn==1) /* general read error  */
+        {
+          free(intbox);
+          free(intvelbox);
+          free(box1);
+          free(velbox1);
+          if(box2)
+            free(box2);
+          if(velbox2)
+            free(velbox2);
+          return 1;
+        }
+#endif /* GEN */
+#ifndef GEN
+      /* Check for equality of boxes. */
+      if (!equalarr(box1,box2,(REAL)PRECISION,NATOMS,3,STRIDE1,STRIDE2))
+        {
+          free(intbox);
+          free(intvelbox);
+          free(box1);
+          free(velbox1);
+          if(box2)
+            free(box2);
+          if(velbox2)
+            free(velbox2);
+          return 4;
+        }
+#if WRITEVEL
+      if (!equalarr(velbox1,velbox2,(REAL)VELPRECISION,NATOMS,3,STRIDE1,STRIDE2))
+        {
+          free(intbox);
+          free(intvelbox);
+          free(box1);
+          free(velbox1);
+          if(box2)
+            free(box2);
+          if(velbox2)
+            free(velbox2);
+          return 5;
+        }
+#endif
+#endif /* GEN */
+#endif /* RECOMPRESS */
+    }
+#ifdef GEN
+  close_tng_file_write(dumpfile);
+#else
+  close_tng_file_read(dumpfile);
+#endif
+#ifdef RECOMPRESS
+  close_tng_file_read(dumpfile_recompress);
+#endif
+#ifdef GEN
+  /* Check against expected filesize for this test. */
+  if (!(file=fopen(TNG_COMPRESS_FILES_DIR FILENAME,"rb")))
+    {
+      fprintf(stderr,"ERROR: Cannot open file "TNG_COMPRESS_FILES_DIR FILENAME"\n");
+      exit(EXIT_FAILURE);
+    }
+  filesize=0;
+  while(1)
+    {
+      char b;
+      if (!fread(&b,1,1,file))
+        break;
+      filesize++;
+    }
+  fclose(file);
+  if (filesize>0)
+    {
+      if ((fabs(filesize-EXPECTED_FILESIZE)/EXPECTED_FILESIZE)>0.05)
+      {
+          free(intvelbox);
+          free(box1);
+          free(velbox1);
+          if(box2)
+            free(box2);
+          if(velbox2)
+            free(velbox2);
+          return 9;
+      }
+    }
+#endif
+  free(intvelbox);
+  free(box1);
+  free(velbox1);
+  if(box2)
+    free(box2);
+  if(velbox2)
+    free(velbox2);
+  return 0;
+}
+
+int main()
+{
+  int testval;
+  if (sizeof(int)<4)
+    {
+      fprintf(stderr,"ERROR: sizeof(int) is too small: %d<4\n",(int)sizeof(int));
+      exit(EXIT_FAILURE);
+    }
+#ifdef GEN
+  printf("Tng compress testsuite generating (writing) test: %s\n",TESTNAME);
+#else
+  printf("Tng compress testsuite running (reading) test: %s\n",TESTNAME);
+#endif
+  testval=algotest();
+  if (testval==0)
+    printf("Passed.\n");
+  else if (testval==1)
+    {
+      printf("ERROR: File error.\n");
+      exit(EXIT_FAILURE);
+    }
+  else if (testval==4)
+    {
+      printf("ERROR: Read coding error in coordinates.\n");
+      exit(EXIT_FAILURE);
+    }
+  else if (testval==5)
+    {
+      printf("ERROR: Read coding error in velocities.\n");
+      exit(EXIT_FAILURE);
+    }
+  else if (testval==9)
+    {
+      printf("ERROR: Generated filesize differs too much.\n");
+      exit(EXIT_FAILURE);
+    }
+  else
+    {
+      printf("ERROR: Unknown error.\n");
+      exit(EXIT_FAILURE);
+    }
+  return 0;
+}
diff --git a/src/external/tng_io/src/tests/md_openmp.c b/src/external/tng_io/src/tests/md_openmp.c
new file mode 100644 (file)
index 0000000..449810d
--- /dev/null
@@ -0,0 +1,840 @@
+#ifdef TNG_BUILD_OPENMP_EXAMPLES
+
+# include <stdlib.h>
+# include <stdio.h>
+# include <time.h>
+# include <math.h>
+# include <omp.h>
+# include "tng_io.h"
+
+int main ();
+void compute ( int np, int nd, double pos[], double vel[],
+    double mass, double f[], double *pot, double *kin );
+double dist ( int nd, double r1[], double r2[], double dr[] );
+void initialize ( int np, int nd, double box[], int *seed, double pos[],
+    double vel[], double acc[] );
+double r8_uniform_01 ( int *seed );
+void timestamp ( void );
+void update ( int np, int nd, double pos[], double vel[], double f[],
+    double acc[], double mass, double dt );
+
+/******************************************************************************/
+
+int main ()
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        MAIN is the main program for MD_OPENMP.
+
+    Discussion:
+
+        MD implements a simple molecular dynamics simulation.
+
+        The program uses Open MP directives to allow parallel computation.
+
+        The velocity Verlet time integration scheme is used.
+
+        The particles interact with a central pair potential.
+
+        Output of the program is saved in the TNG format, which is why this
+        code is included in the TNG API release.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        8 Jan 2013
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+        TNG trajectory output by Magnus Lundborg.
+
+    Parameters:
+
+        None
+*/
+{
+    double *acc;
+    double *box;
+    double *box_shape;
+    double dt = 0.0002;
+    double e0;
+    double *force;
+    int i;
+    double kinetic;
+    double mass = 1.0;
+    int nd = 3;
+    int np = 50;
+    double *pos;
+    double potential;
+    int proc_num;
+    int seed = 123456789;
+    int step;
+    int step_num = 50000;
+    int step_print;
+    int step_print_index;
+    int step_print_num;
+    int step_save;
+    int64_t sparse_save;
+    double *vel;
+    double wtime;
+    tng_trajectory_t traj;
+    tng_molecule_t molecule;
+    tng_chain_t chain;
+    tng_residue_t residue;
+    tng_atom_t atom;
+    int64_t n_frames_per_frame_set;
+    int frames_saved_cnt = 0;
+
+    timestamp ( );
+
+    proc_num = omp_get_num_procs ( );
+
+    acc = ( double * ) malloc ( nd * np * sizeof ( double ) );
+    box = ( double * ) malloc ( nd * sizeof ( double ) );
+    box_shape = (double *) malloc (9 * sizeof (double));
+    force = ( double * ) malloc ( nd * np * sizeof ( double ) );
+    pos = ( double * ) malloc ( nd * np * sizeof ( double ) );
+    vel = ( double * ) malloc ( nd * np * sizeof ( double ) );
+
+    printf ( "\n" );
+    printf ( "MD_OPENMP\n" );
+    printf ( "  C/OpenMP version\n" );
+    printf ( "\n" );
+    printf ( "  A molecular dynamics program.\n" );
+
+    printf ( "\n" );
+    printf ( "  NP, the number of particles in the simulation is %d\n", np );
+    printf ( "  STEP_NUM, the number of time steps, is %d\n", step_num );
+    printf ( "  DT, the size of each time step, is %f\n", dt );
+
+    printf ( "\n" );
+    printf ( "  Number of processors available = %d\n", proc_num );
+    printf ( "  Number of threads =              %d\n", omp_get_max_threads ( ) );
+
+
+    printf("\n");
+    printf("  Initializing trajectory storage.\n");
+    if(tng_trajectory_init(&traj) != TNG_SUCCESS)
+    {
+        tng_trajectory_destroy(&traj);
+        printf("  Cannot init trajectory.\n");
+        exit(1);
+    }
+#ifdef TNG_EXAMPLE_FILES_DIR
+    tng_output_file_set(traj, TNG_EXAMPLE_FILES_DIR "tng_md_out.tng");
+#else
+    tng_output_file_set(traj, "/tmp/tng_md_out.tng");
+#endif
+
+
+
+    /* Set molecules data */
+    printf("  Creating molecules in trajectory.\n");
+    tng_molecule_add(traj, "water", &molecule);
+    tng_molecule_chain_add(traj, molecule, "W", &chain);
+    tng_chain_residue_add(traj, chain, "WAT", &residue);
+    if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
+    {
+        tng_trajectory_destroy(&traj);
+        printf("  Cannot create molecules.\n");
+        exit(1);
+    }
+    tng_molecule_cnt_set(traj, molecule, np);
+
+
+/*
+    Set the dimensions of the box.
+*/
+    for(i = 0; i < 9; i++)
+    {
+        box_shape[i] = 0.0;
+    }
+    for ( i = 0; i < nd; i++ )
+    {
+        box[i] = 10.0;
+        box_shape[i*nd + i] = box[i];
+    }
+
+
+    /* Add the box shape data block and write the file headers */
+    if(tng_data_block_add(traj, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE", TNG_DOUBLE_DATA,
+                       TNG_NON_TRAJECTORY_BLOCK, 1, 9, 1, TNG_UNCOMPRESSED,
+                       box_shape) == TNG_CRITICAL ||
+                       tng_file_headers_write(traj, TNG_USE_HASH) == TNG_CRITICAL)
+    {
+        free(box_shape);
+        tng_trajectory_destroy(&traj);
+        printf("  Cannot write trajectory headers and box shape.\n");
+        exit(1);
+    }
+    free(box_shape);
+
+    printf ( "\n" );
+    printf ( "  Initializing positions, velocities, and accelerations.\n" );
+/*
+    Set initial positions, velocities, and accelerations.
+*/
+    initialize ( np, nd, box, &seed, pos, vel, acc );
+/*
+    Compute the forces and energies.
+*/
+    printf ( "\n" );
+    printf ( "  Computing initial forces and energies.\n" );
+
+    compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
+
+    e0 = potential + kinetic;
+
+    /* Saving frequency */
+    step_save = 500;
+
+    step_print = 0;
+    step_print_index = 0;
+    step_print_num = 10;
+    sparse_save = 100;
+
+/*
+    This is the main time stepping loop:
+        Compute forces and energies,
+        Update positions, velocities, accelerations.
+*/
+    printf("  Every %d steps particle positions, velocities and forces are\n",
+           step_save);
+    printf("  saved to a TNG trajectory file.\n");
+    printf ( "\n" );
+    printf ( "  At certain step intervals, we report the potential and kinetic energies.\n" );
+    printf ( "  The sum of these energies should be a constant.\n" );
+    printf ( "  As an accuracy check, we also print the relative error\n" );
+    printf ( "  in the total energy.\n" );
+    printf ( "\n" );
+    printf ( "      Step      Potential       Kinetic        (P+K-E0)/E0\n" );
+    printf ( "                Energy P        Energy K       Relative Energy Error\n" );
+    printf ( "\n" );
+
+    step = 0;
+    printf ( "  %8d  %14f  %14f  %14e\n",
+        step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
+    step_print_index++;
+    step_print = ( step_print_index * step_num ) / step_print_num;
+
+    /* Create a frame set for writing data */
+    tng_num_frames_per_frame_set_get(traj, &n_frames_per_frame_set);
+    if(tng_frame_set_new(traj, 0,
+        n_frames_per_frame_set) != TNG_SUCCESS)
+    {
+        printf("Error creating frame set %d. %s: %d\n",
+                i, __FILE__, __LINE__);
+        exit(1);
+    }
+
+    /* Add empty data blocks */
+    if(tng_particle_data_block_add(traj, TNG_TRAJ_POSITIONS,
+                                "POSITIONS",
+                                TNG_DOUBLE_DATA,
+                                TNG_TRAJECTORY_BLOCK,
+                                n_frames_per_frame_set, 3,
+                                1, 0, np,
+                                TNG_UNCOMPRESSED,
+                                0) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_particle_data_block_add(traj, TNG_TRAJ_VELOCITIES,
+                                "VELOCITIES",
+                                TNG_DOUBLE_DATA,
+                                TNG_TRAJECTORY_BLOCK,
+                                n_frames_per_frame_set, 3,
+                                1, 0, np,
+                                TNG_UNCOMPRESSED,
+                                0) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_particle_data_block_add(traj, TNG_TRAJ_FORCES,
+                                "FORCES",
+                                TNG_DOUBLE_DATA,
+                                TNG_TRAJECTORY_BLOCK,
+                                n_frames_per_frame_set, 3,
+                                1, 0, np,
+                                TNG_UNCOMPRESSED, 0) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+
+    /* There is no standard ID for potential energy. Pick one. The
+       potential energy will not be saved every frame - it is sparsely
+       saved. */
+    if(tng_data_block_add(traj, 10101,
+                          "POTENTIAL ENERGY",
+                          TNG_DOUBLE_DATA,
+                          TNG_TRAJECTORY_BLOCK,
+                          n_frames_per_frame_set, 1,
+                          sparse_save, TNG_UNCOMPRESSED,
+                          0) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+
+    /* Write the frame set to disk */
+    if(tng_frame_set_write(traj, TNG_USE_HASH) != TNG_SUCCESS)
+    {
+        printf("Error writing frame set. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+
+    wtime = omp_get_wtime ( );
+
+    if(tng_frame_particle_data_write(traj, frames_saved_cnt,
+                                    TNG_TRAJ_POSITIONS, 0, np,
+                                    pos, TNG_USE_HASH) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_frame_particle_data_write(traj, frames_saved_cnt,
+                                    TNG_TRAJ_VELOCITIES, 0, np,
+                                    vel, TNG_USE_HASH) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_frame_particle_data_write(traj, frames_saved_cnt,
+                                    TNG_TRAJ_FORCES, 0, np,
+                                    force, TNG_USE_HASH) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(step % (step_save * sparse_save) == 0)
+    {
+        if(tng_frame_data_write(traj, frames_saved_cnt, 10101, &potential,
+                                TNG_USE_HASH) != TNG_SUCCESS)
+        {
+            printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+            exit(1);
+        }
+    }
+    frames_saved_cnt++;
+
+    for ( step = 1; step < step_num; step++ )
+    {
+        compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
+
+        if ( step == step_print )
+        {
+            printf ( "  %8d  %14f  %14f  %14e\n", step, potential, kinetic,
+             ( potential + kinetic - e0 ) / e0 );
+            step_print_index++;
+            step_print = ( step_print_index * step_num ) / step_print_num;
+        }
+        if(step % step_save == 0)
+        {
+            if(tng_frame_particle_data_write(traj, frames_saved_cnt,
+                                            TNG_TRAJ_POSITIONS, 0, np,
+                                            pos, TNG_USE_HASH) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                exit(1);
+            }
+            if(tng_frame_particle_data_write(traj, frames_saved_cnt,
+                                            TNG_TRAJ_VELOCITIES, 0, np,
+                                            vel, TNG_USE_HASH) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                exit(1);
+            }
+            if(tng_frame_particle_data_write(traj, frames_saved_cnt,
+                                            TNG_TRAJ_FORCES, 0, np,
+                                            force, TNG_USE_HASH) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                exit(1);
+            }
+            if(step % (step_save * sparse_save) == 0)
+            {
+                if(tng_frame_data_write(traj, frames_saved_cnt, 10101, &potential,
+                                     TNG_USE_HASH) != TNG_SUCCESS)
+                {
+                    printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                    exit(1);
+                }
+            }
+            frames_saved_cnt++;
+        }
+        update ( np, nd, pos, vel, force, acc, mass, dt );
+    }
+    wtime = omp_get_wtime ( ) - wtime;
+
+    printf ( "\n" );
+    printf ( "  Elapsed time for main computation:\n" );
+    printf ( "  %f seconds.\n", wtime );
+
+    free ( acc );
+    free ( box );
+    free ( force );
+    free ( pos );
+    free ( vel );
+/*
+    Terminate.
+*/
+    tng_trajectory_destroy(&traj);
+
+    printf ( "\n" );
+    printf ( "MD_OPENMP\n" );
+    printf ( "  Normal end of execution.\n" );
+
+    printf ( "\n" );
+    timestamp ( );
+
+    return 0;
+}
+/******************************************************************************/
+
+void compute ( int np, int nd, double pos[], double vel[],
+    double mass, double f[], double *pot, double *kin )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        COMPUTE computes the forces and energies.
+
+    Discussion:
+
+        The computation of forces and energies is fully parallel.
+
+        The potential function V(X) is a harmonic well which smoothly
+        saturates to a maximum value at PI/2:
+
+            v(x) = ( sin ( min ( x, PI2 ) ) )**2
+
+        The derivative of the potential is:
+
+            dv(x) = 2.0 * sin ( min ( x, PI2 ) ) * cos ( min ( x, PI2 ) )
+                        = sin ( 2.0 * min ( x, PI2 ) )
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        21 November 2007
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int NP, the number of particles.
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input, double POS[ND*NP], the position of each particle.
+
+        Input, double VEL[ND*NP], the velocity of each particle.
+
+        Input, double MASS, the mass of each particle.
+
+        Output, double F[ND*NP], the forces.
+
+        Output, double *POT, the total potential energy.
+
+        Output, double *KIN, the total kinetic energy.
+*/
+{
+    double d;
+    double d2;
+    int i;
+    int j;
+    int k;
+    double ke;
+    double pe;
+    double PI2 = 3.141592653589793 / 2.0;
+    double rij[3];
+
+    pe = 0.0;
+    ke = 0.0;
+
+# pragma omp parallel \
+    shared ( f, nd, np, pos, vel ) \
+    private ( i, j, k, rij, d, d2 )
+
+
+# pragma omp for reduction ( + : pe, ke )
+    for ( k = 0; k < np; k++ )
+    {
+/*
+    Compute the potential energy and forces.
+*/
+        for ( i = 0; i < nd; i++ )
+        {
+            f[i+k*nd] = 0.0;
+        }
+
+        for ( j = 0; j < np; j++ )
+        {
+            if ( k != j )
+            {
+                d = dist ( nd, pos+k*nd, pos+j*nd, rij );
+/*
+    Attribute half of the potential energy to particle J.
+*/
+                if ( d < PI2 )
+                {
+                    d2 = d;
+                }
+                else
+                {
+                    d2 = PI2;
+                }
+
+                pe = pe + 0.5 * pow ( sin ( d2 ), 2 );
+
+                for ( i = 0; i < nd; i++ )
+                {
+                    f[i+k*nd] = f[i+k*nd] - rij[i] * sin ( 2.0 * d2 ) / d;
+                }
+            }
+        }
+/*
+    Compute the kinetic energy.
+*/
+        for ( i = 0; i < nd; i++ )
+        {
+            ke = ke + vel[i+k*nd] * vel[i+k*nd];
+        }
+    }
+
+    ke = ke * 0.5 * mass;
+
+    *pot = pe;
+    *kin = ke;
+
+    return;
+}
+/******************************************************************************/
+
+double dist ( int nd, double r1[], double r2[], double dr[] )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        DIST computes the displacement (and its norm) between two particles.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        21 November 2007
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input, double R1[ND], R2[ND], the positions of the particles.
+
+        Output, double DR[ND], the displacement vector.
+
+        Output, double D, the Euclidean norm of the displacement.
+*/
+{
+    double d;
+    int i;
+
+    d = 0.0;
+    for ( i = 0; i < nd; i++ )
+    {
+        dr[i] = r1[i] - r2[i];
+        d = d + dr[i] * dr[i];
+    }
+    d = sqrt ( d );
+
+    return d;
+}
+/******************************************************************************/
+
+void initialize ( int np, int nd, double box[], int *seed, double pos[],
+    double vel[], double acc[] )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        INITIALIZE initializes the positions, velocities, and accelerations.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        21 November 2007
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int NP, the number of particles.
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input, double BOX[ND], specifies the maximum position
+        of particles in each dimension.
+
+        Input, int *SEED, a seed for the random number generator.
+
+        Output, double POS[ND*NP], the position of each particle.
+
+        Output, double VEL[ND*NP], the velocity of each particle.
+
+        Output, double ACC[ND*NP], the acceleration of each particle.
+*/
+{
+    int i;
+    int j;
+/*
+    Give the particles random positions within the box.
+*/
+    for ( i = 0; i < nd; i++ )
+    {
+        for ( j = 0; j < np; j++ )
+        {
+            pos[i+j*nd] = box[i] * r8_uniform_01 ( seed );
+        }
+    }
+
+    for ( j = 0; j < np; j++ )
+    {
+        for ( i = 0; i < nd; i++ )
+        {
+            vel[i+j*nd] = 0.0;
+        }
+    }
+    for ( j = 0; j < np; j++ )
+    {
+        for ( i = 0; i < nd; i++ )
+        {
+            acc[i+j*nd] = 0.0;
+        }
+    }
+    return;
+}
+/******************************************************************************/
+
+double r8_uniform_01 ( int *seed )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        R8_UNIFORM_01 is a unit pseudorandom R8.
+
+    Discussion:
+
+        This routine implements the recursion
+
+            seed = 16807 * seed mod ( 2**31 - 1 )
+            unif = seed / ( 2**31 - 1 )
+
+        The integer arithmetic never requires more than 32 bits,
+        including a sign bit.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        11 August 2004
+
+    Author:
+
+        John Burkardt
+
+    Reference:
+
+        Paul Bratley, Bennett Fox, Linus Schrage,
+        A Guide to Simulation,
+        Springer Verlag, pages 201-202, 1983.
+
+        Bennett Fox,
+        Algorithm 647:
+        Implementation and Relative Efficiency of Quasirandom
+        Sequence Generators,
+        ACM Transactions on Mathematical Software,
+        Volume 12, Number 4, pages 362-376, 1986.
+
+    Parameters:
+
+        Input/output, int *SEED, a seed for the random number generator.
+
+        Output, double R8_UNIFORM_01, a new pseudorandom variate, strictly between
+        0 and 1.
+*/
+{
+    int k;
+    double r;
+
+    k = *seed / 127773;
+
+    *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
+
+    if ( *seed < 0 )
+    {
+        *seed = *seed + 2147483647;
+    }
+
+    r = ( double ) ( *seed ) * 4.656612875E-10;
+
+    return r;
+}
+/******************************************************************************/
+
+void timestamp ( void )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        TIMESTAMP prints the current YMDHMS date as a time stamp.
+
+    Example:
+
+        31 May 2001 09:45:54 AM
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        24 September 2003
+
+    Author:
+
+        John Burkardt
+
+    Parameters:
+
+        None
+*/
+{
+# define TIME_SIZE 40
+
+    static char time_buffer[TIME_SIZE];
+    const struct tm *tm;
+    time_t now;
+
+    now = time ( NULL );
+    tm = localtime ( &now );
+
+    strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
+
+    printf ( "%s\n", time_buffer );
+
+    return;
+# undef TIME_SIZE
+}
+/******************************************************************************/
+
+void update ( int np, int nd, double pos[], double vel[], double f[],
+    double acc[], double mass, double dt )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        UPDATE updates positions, velocities and accelerations.
+
+    Discussion:
+
+        The time integration is fully parallel.
+
+        A velocity Verlet algorithm is used for the updating.
+
+        x(t+dt) = x(t) + v(t) * dt + 0.5 * a(t) * dt * dt
+        v(t+dt) = v(t) + 0.5 * ( a(t) + a(t+dt) ) * dt
+        a(t+dt) = f(t) / m
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        17 April 2009
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int NP, the number of particles.
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input/output, double POS[ND*NP], the position of each particle.
+
+        Input/output, double VEL[ND*NP], the velocity of each particle.
+
+        Input, double F[ND*NP], the force on each particle.
+
+        Input/output, double ACC[ND*NP], the acceleration of each particle.
+
+        Input, double MASS, the mass of each particle.
+
+        Input, double DT, the time step.
+*/
+{
+    int i;
+    int j;
+    double rmass;
+
+    rmass = 1.0 / mass;
+
+# pragma omp parallel \
+    shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
+    private ( i, j )
+
+# pragma omp for
+    for ( j = 0; j < np; j++ )
+    {
+        for ( i = 0; i < nd; i++ )
+        {
+            pos[i+j*nd] = pos[i+j*nd] + vel[i+j*nd] * dt + 0.5 * acc[i+j*nd] * dt * dt;
+            vel[i+j*nd] = vel[i+j*nd] + 0.5 * dt * ( f[i+j*nd] * rmass + acc[i+j*nd] );
+            acc[i+j*nd] = f[i+j*nd] * rmass;
+        }
+    }
+
+    return;
+}
+
+#endif
diff --git a/src/external/tng_io/src/tests/md_openmp.f b/src/external/tng_io/src/tests/md_openmp.f
new file mode 100644 (file)
index 0000000..de05ca2
--- /dev/null
@@ -0,0 +1,841 @@
+      program main
+
+c*********************************************************************72
+c
+cc MAIN is the main program for MD_OPENMP.
+c
+c  Discussion:
+c
+c    The program implements a simple molecular dynamics simulation.
+c
+c    The program uses Open MP directives to allow parallel computation.
+c
+c    The velocity Verlet time integration scheme is used. 
+c
+c    The particles interact with a central pair potential.
+c
+c    Output of the program is saved in the TNG format, which is why this
+c    code is included in the TNG API release.
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    8 Jan 2013
+c
+c  Author:
+c
+c    Original FORTRAN90 version by Bill Magro.
+c    FORTRAN77 version by John Burkardt.
+c    TNG trajectory output by Magnus Lundborg.
+c
+c  Parameters:
+c
+c    None
+c
+      implicit none
+
+      include 'omp_lib.h'
+
+      integer nd
+      parameter ( nd = 3 )
+      integer np
+      parameter ( np = 250 )
+      integer step_num
+      parameter ( step_num = 1000 )
+
+      double precision acc(nd,np)
+      double precision box(nd)
+      double precision box_shape(9)
+      double precision dt
+      parameter ( dt = 0.0001D+00 )
+      double precision e0
+      double precision force(nd,np)
+      integer i
+      integer id
+      double precision kinetic
+      double precision mass
+      parameter ( mass = 1.0D+00 )
+      double precision pos(nd,np)
+      double precision potential
+      integer proc_num
+      integer seed
+      integer step
+      integer step_print
+      integer step_print_index
+      integer step_print_num
+      integer step_save
+      integer*8 sparse_save
+      integer thread_num
+      double precision vel(nd,np)
+      double precision wtime
+
+c
+c  Cray pointers are not standard fortran 77, but must be used to allocate
+c  memory properly.
+c
+      pointer (traj_p, traj)
+      pointer (molecule_p, molecule)
+      pointer (chain_p, chain)
+      pointer (residue_p, residue)
+      pointer (atom_p, atom)
+      byte traj
+      byte molecule
+      byte chain
+      byte residue
+      byte atom
+
+c
+c  The TNG functions expect 64 bit integers
+c
+      integer*8 n_frames_per_frame_set
+      integer*8 frames_saved_cnt
+      integer*8 tng_n_particles
+
+c
+c  These constants are also defined in tng_io.h, but need to
+c  set in fortran as well. This can be copied to any fortran
+c  source code using the tng_io library.
+c
+      integer*8 TNG_UNCOMPRESSED
+      parameter ( TNG_UNCOMPRESSED = 0)
+      integer TNG_NON_TRAJECTORY_BLOCK
+      parameter ( TNG_NON_TRAJECTORY_BLOCK = 0)
+      integer TNG_TRAJECTORY_BLOCK
+      parameter ( TNG_TRAJECTORY_BLOCK = 1)
+      integer*8 TNG_GENERAL_INFO
+      parameter ( TNG_GENERAL_INFO = 0 )
+      integer*8 TNG_MOLECULES
+      parameter ( TNG_MOLECULES = 1 )
+      integer*8 TNG_TRAJECTORY_FRAME_SET
+      parameter ( TNG_TRAJECTORY_FRAME_SET = 2 )
+      integer*8 TNG_PARTICLE_MAPPING
+      parameter ( TNG_PARTICLE_MAPPING = 3 )
+      integer*8 TNG_TRAJ_BOX_SHAPE
+      parameter ( TNG_TRAJ_BOX_SHAPE = 10000 )
+      integer*8 TNG_TRAJ_POSITIONS
+      parameter ( TNG_TRAJ_POSITIONS = 10001 )
+      integer*8 TNG_TRAJ_VELOCITIES
+      parameter ( TNG_TRAJ_VELOCITIES = 10002 )
+      integer*8 TNG_TRAJ_FORCES
+      parameter ( TNG_TRAJ_FORCES = 10003 )
+      integer TNG_SKIP_HASH
+      parameter ( TNG_SKIP_HASH = 0 )
+      integer TNG_USE_HASH
+      parameter ( TNG_USE_HASH = 1 )
+      integer TNG_CHAR_DATA
+      parameter ( TNG_CHAR_DATA = 0 )
+      integer TNG_INT_DATA
+      parameter ( TNG_INT_DATA = 1 )
+      integer TNG_FLOAT_DATA
+      parameter ( TNG_FLOAT_DATA = 2 )
+      integer TNG_DOUBLE_DATA
+      parameter ( TNG_DOUBLE_DATA = 3 )
+
+      call timestamp ( )
+
+      write ( *, '(a)' ) ' '
+      write ( *, '(a)' ) 'MD_OPENMP'
+      write ( *, '(a)' ) '  FORTRAN77/OpenMP version'
+      write ( *, '(a)' ) ' '
+      write ( *, '(a)' ) '  A molecular dynamics program.'
+      write ( *, '(a)' ) ' '
+      write ( *, '(a,i8)' ) 
+     &  '  NP, the number of particles in the simulation is ', np
+      write ( *, '(a,i8)' ) 
+     &  '  STEP_NUM, the number of time steps, is ', step_num
+      write ( *, '(a,g14.6)' ) 
+     &  '  DT, the size of each time step, is ', dt
+      write ( *, '(a)' ) ' '
+      write ( *, '(a,i8)' ) 
+     &  '  The number of processors = ', omp_get_num_procs ( )
+      write ( *, '(a,i8)' ) 
+     &  '  The number of threads    = ', omp_get_max_threads ( )
+
+      write ( *, '(a)' ) ' '     
+      write ( *, '(a)' ) '  Initializing trajectory storage.'
+      call tng_trajectory_init(traj_p)
+
+c
+c  N.B. The TNG output file should be modified according to needs
+c      
+      call tng_output_file_set(traj, "/tmp/tng_md_out_f77.tng")
+
+      write ( *, '(a)' ) '  Creating molecules in trajectory.'
+      tng_n_particles = np
+      call tng_molecule_add(traj, "water", molecule_p)
+      call tng_molecule_chain_add(traj, molecule, "W", chain_p)
+      call tng_chain_residue_add(traj, chain, "WAT", residue_p)
+      call tng_residue_atom_add(traj, residue, "O", "O", atom_p)
+      call tng_molecule_cnt_set(traj, molecule, tng_n_particles)
+c
+c  Set the dimensions of the box.
+c
+      do i = 1, 9
+        box_shape(i) = 0.0
+      end do
+      do i = 1, nd
+        box(i) = 10.0D+00
+        box_shape(i*nd + i) = box(i)
+      end do
+
+c
+c  Add the box shape data block
+c     
+      call tng_data_block_add(traj, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
+     &  TNG_DOUBLE_DATA, TNG_NON_TRAJECTORY_BLOCK, int(1, 8),
+     &  int(9, 8), int(1, 8), TNG_UNCOMPRESSED, box_shape)
+
+c
+c  Write the file headers
+c    
+      call tng_file_headers_write(traj, TNG_USE_HASH)
+      
+c
+c  Set initial positions, velocities, and accelerations.
+c      
+      write ( *, '(a)' ) 
+     &  '  Initializing positions, velocities, and accelerations.'
+      seed = 123456789
+      call initialize ( np, nd, box, seed, pos, vel, acc )
+c
+c  Compute the forces and energies.
+c
+      write ( *, '(a)' ) ' '
+      write ( *, '(a)' ) '  Computing initial forces and energies.'
+
+      call compute ( np, nd, pos, vel, mass, force, potential, 
+     &  kinetic )
+
+      e0 = potential + kinetic
+
+c
+c  Saving frequency
+c
+      step_save = 5
+      
+      step_print = 0
+      step_print_index = 0
+      step_print_num = 10
+      sparse_save = 10
+
+      frames_saved_cnt = 0
+
+c
+c  This is the main time stepping loop.
+c
+      write ( *, '(a,i4,a)' ) '  Every', step_save,
+     &  ' steps particle positions, velocities and forces are'
+      write ( *, '(a)' ) '  saved to a TNG trajectory file.'
+      write ( *, '(a)' )
+      write ( *, '(a)' ) 
+     &  '  At each step, we report the potential and kinetic energies.'
+      write ( *, '(a)' ) 
+     &  '  The sum of these energies should be a constant.'
+      write ( *, '(a)' ) 
+     &  '  As an accuracy check, we also print the relative error'
+      write ( *, '(a)' ) '  in the total energy.'
+      write ( *, '(a)' ) ' '
+      write ( *, '(a)' ) 
+     &  '      Step      Potential       Kinetic        (P+K-E0)/E0'
+      write ( *, '(a)' ) 
+     &  '                Energy P        Energy K       ' //
+     &  'Relative Energy Error'
+      write ( *, '(a)' ) ' '
+
+      step = 0
+      write ( *, '(2x,i8,2x,g14.6,2x,g14.6,2x,g14.6)' ) 
+     &  step, potential, kinetic, ( potential + kinetic - e0 ) / e0
+      step_print_index = step_print_index + 1
+      step_print = ( step_print_index * step_num ) / step_print_num
+
+c      
+c  Create a frame set for writing data      
+c
+      call tng_num_frames_per_frame_set_get(traj,
+     &  n_frames_per_frame_set)
+      call tng_frame_set_new(traj, int(0, 8), n_frames_per_frame_set)
+
+c
+c  Add empty data blocks.
+c
+      call tng_particle_data_block_add(traj, TNG_TRAJ_POSITIONS,
+     &  "POSITIONS", TNG_DOUBLE_DATA, TNG_TRAJECTORY_BLOCK,
+     &  n_frames_per_frame_set, int(3, 8), int(1, 8), int(0, 8),
+     &  tng_n_particles, TNG_UNCOMPRESSED, %VAL(int(0, 8)))
+      
+      call tng_particle_data_block_add(traj, TNG_TRAJ_VELOCITIES,
+     &  "VELOCITIES", TNG_DOUBLE_DATA, TNG_TRAJECTORY_BLOCK,
+     &  n_frames_per_frame_set, int(3, 8), int(1, 8), int(0, 8),
+     &  tng_n_particles, TNG_UNCOMPRESSED, %VAL(int(0, 8)))
+      
+      call tng_particle_data_block_add(traj, TNG_TRAJ_FORCES,
+     &  "FORCES", TNG_DOUBLE_DATA, TNG_TRAJECTORY_BLOCK,
+     &  n_frames_per_frame_set, int(3, 8), int(1, 8), int(0, 8),
+     &  tng_n_particles, TNG_UNCOMPRESSED, %VAL(int(0, 8)))
+
+c
+c  The potential energy data block is saved sparsely.
+c     
+      call tng_data_block_add(traj, int(10101, 8),
+     &  "POTENTIAL ENERGY", TNG_DOUBLE_DATA, TNG_TRAJECTORY_BLOCK,
+     &  n_frames_per_frame_set, int(1, 8), sparse_save,
+     &  TNG_UNCOMPRESSED, %VAL(int(0, 8)))
+     
+
+c
+c  Write the frame set to disk
+c
+      call tng_frame_set_write(traj, TNG_USE_HASH)
+      
+      wtime = omp_get_wtime ( )
+
+      do step = 1, step_num
+
+        call compute ( np, nd, pos, vel, mass, force, potential, 
+     &    kinetic )
+
+        if ( step .eq. step_print ) then
+
+          write ( *, '(2x,i8,2x,g14.6,2x,g14.6,2x,g14.6)' ) 
+     &      step, potential, kinetic, ( potential + kinetic - e0 ) / e0
+
+          step_print_index = step_print_index + 1
+          step_print = ( step_print_index * step_num ) / step_print_num
+
+        end if
+
+c
+c  Output to TNG file at regular intervals, specified by step_save
+c
+        if ( step_save .EQ. 0 .OR. mod(step, step_save) .EQ. 0 ) then
+          call tng_frame_particle_data_write(traj, frames_saved_cnt,
+     &    TNG_TRAJ_POSITIONS, int(0, 8), tng_n_particles, pos,
+     &    TNG_USE_HASH)
+          call tng_frame_particle_data_write(traj, frames_saved_cnt,
+     &    TNG_TRAJ_VELOCITIES, int(0, 8), tng_n_particles, vel,
+     &    TNG_USE_HASH)
+          call tng_frame_particle_data_write(traj, frames_saved_cnt,
+     &    TNG_TRAJ_FORCES, int(0, 8), tng_n_particles, force,
+     &    TNG_USE_HASH)
+          frames_saved_cnt = frames_saved_cnt + 1
+          
+          if (mod(step, step_save * sparse_save) .EQ. 0) then
+            call tng_frame_data_write(traj, frames_saved_cnt,
+     &      int(10101, 8), potential, TNG_USE_HASH)
+          end if
+          
+        end if
+
+        call update ( np, nd, pos, vel, force, acc, mass, dt )
+
+      end do
+
+      wtime = omp_get_wtime ( ) - wtime
+
+      write ( *, '(a)' ) ' '
+      write ( *, '(a)' ) 
+     &  '  Elapsed time for main computation:'
+      write ( *, '(2x,g14.6,a)' ) wtime, ' seconds'
+c
+c  Terminate.
+c
+      call tng_trajectory_destroy(traj_p)
+
+      write ( *, '(a)' ) ' '
+      write ( *, '(a)' ) 'MD_OPENMP'
+      write ( *, '(a)' ) '  Normal end of execution.'
+
+      write ( *, '(a)' ) ' '
+      call timestamp ( )
+
+      stop
+      end
+      subroutine compute ( np, nd, pos, vel, mass, f, pot, kin )
+
+c*********************************************************************72
+c
+cc COMPUTE computes the forces and energies.
+c
+c  Discussion:
+c
+c    The computation of forces and energies is fully parallel.
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    31 July 2009
+c
+c  Author:
+c
+c    Original FORTRAN90 version by Bill Magro.
+c    FORTRAN77 version by John Burkardt.
+c
+c  Parameters:
+c
+c    Input, integer NP, the number of particles.
+c
+c    Input, integer ND, the number of spatial dimensions.
+c
+c    Input, double precision POS(ND,NP), the position of each particle.
+c
+c    Input, double precision VEL(ND,NP), the velocity of each particle.
+c
+c    Input, double precision MASS, the mass of each particle.
+c
+      implicit none
+
+      integer np
+      integer nd
+
+      double precision d
+      double precision d2
+      double precision dv
+      double precision f(nd,np)
+      integer i
+      integer j
+      integer k
+      double precision kin
+      double precision mass
+      double precision PI2
+      parameter ( PI2 = 3.141592653589793D+00 / 2.0D+00 )
+      double precision pos(nd,np)
+      double precision pot
+      double precision rij(nd)
+      double precision v
+      double precision vel(nd,np)
+
+      pot = 0.0D+00
+      kin = 0.0D+00
+
+c$omp parallel 
+c$omp& shared ( f, nd, np, pos, vel ) 
+c$omp& private ( d, d2, i, j, k, rij ) 
+
+c$omp do reduction ( + : pot, kin )
+      do i = 1, np
+c
+c  Compute the potential energy and forces.
+c
+        do k = 1, nd
+          f(k,i) = 0.0D+00
+        end do
+
+        do j = 1, np
+
+          if ( i .ne. j ) then
+
+            call dist ( nd, pos(1,i), pos(1,j), rij, d )
+c
+c  Attribute half of the potential energy to particle J.
+c
+            d2 = min ( d, pi2 )
+
+            pot = pot + 0.5D+00 * ( sin ( d2 ) )**2
+
+            do k = 1, nd
+              f(k,i) = f(k,i) - rij(k) * sin ( 2.0D+00 * d2 ) / d
+            end do
+
+          end if
+
+        end do
+c
+c  Compute the kinetic energy.
+c
+        do k = 1, nd
+          kin = kin + vel(k,i)**2
+        end do
+
+      end do
+c$omp end do
+
+c$omp end parallel
+
+      kin = kin * 0.5D+00 * mass
+      
+      return
+      end
+      subroutine dist ( nd, r1, r2, dr, d )
+
+c*********************************************************************72
+c
+cc DIST computes the displacement (and its norm) between two particles.
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    13 November 2007
+c
+c  Author:
+c
+c    Original FORTRAN90 version by Bill Magro.
+c    FORTRAN77 version by John Burkardt.
+c
+c  Parameters:
+c
+c    Input, integer ND, the number of spatial dimensions.
+c
+c    Input, double precision R1(ND), R2(ND), the positions of the particles.
+c
+c    Output, double precision DR(ND), the displacement vector.
+c
+c    Output, double precision D, the Euclidean norm of the displacement.
+c
+      implicit none
+
+      integer nd
+
+      double precision d
+      double precision dr(nd)
+      integer i
+      double precision r1(nd)
+      double precision r2(nd)
+
+      do i = 1, nd
+        dr(i) = r1(i) - r2(i)
+      end do
+
+      d = 0.0D+00
+      do i = 1, nd
+        d = d + dr(i)**2
+      end do
+      d = sqrt ( d )
+
+      return
+      end
+      subroutine initialize ( np, nd, box, seed, pos, vel, acc )
+
+c*********************************************************************72
+c
+cc INITIALIZE initializes the positions, velocities, and accelerations.
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    13 November 2007
+c
+c  Author:
+c
+c    Original FORTRAN90 version by Bill Magro.
+c    FORTRAN77 version by John Burkardt.
+c
+c  Parameters:
+c
+c    Input, integer NP, the number of particles.
+c
+c    Input, integer ND, the number of spatial dimensions.
+c
+c    Input, double precision BOX(ND), specifies the maximum position
+c    of particles in each dimension.
+c
+c    Input/output, integer SEED, a seed for the random number generator.
+c
+c    Output, double precision POS(ND,NP), the position of each particle.
+c
+c    Output, double precision VEL(ND,NP), the velocity of each particle.
+c
+c    Output, double precision ACC(ND,NP), the acceleration of each particle.
+c
+      implicit none
+
+      integer np
+      integer nd
+
+      double precision acc(nd,np)
+      double precision box(nd)
+      integer i
+      integer j
+      double precision pos(nd,np)
+      double precision r8_uniform_01
+      integer seed
+      double precision vel(nd,np)
+c
+c  Give the particles random positions within the box.
+c
+      do i = 1, nd
+        do j = 1, np
+          pos(i,j) = r8_uniform_01 ( seed )
+        end do
+      end do
+
+c$omp parallel 
+c$omp& shared ( acc, box, nd, np, pos, vel )
+c$omp& private ( i, j )
+
+c$omp do
+      do j = 1, np
+        do i = 1, nd
+          pos(i,j) = box(i) * pos(i,j)
+          vel(i,j) = 0.0D+00
+          acc(i,j) = 0.0D+00
+        end do
+      end do
+c$omp end do
+
+c$omp end parallel
+
+      return
+      end
+      function r8_uniform_01 ( seed )
+
+c*********************************************************************72
+c
+cc R8_UNIFORM_01 returns a unit pseudorandom R8.
+c
+c  Discussion:
+c
+c    This routine implements the recursion
+c
+c      seed = 16807 * seed mod ( 2**31 - 1 )
+c      r8_uniform_01 = seed / ( 2**31 - 1 )
+c
+c    The integer arithmetic never requires more than 32 bits,
+c    including a sign bit.
+c
+c    If the initial seed is 12345, then the first three computations are
+c
+c      Input     Output      R8_UNIFORM_01
+c      SEED      SEED
+c
+c         12345   207482415  0.096616
+c     207482415  1790989824  0.833995
+c    1790989824  2035175616  0.947702
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    11 August 2004
+c
+c  Author:
+c
+c    John Burkardt
+c
+c  Reference:
+c
+c    Paul Bratley, Bennett Fox, Linus Schrage,
+c    A Guide to Simulation,
+c    Springer Verlag, pages 201-202, 1983.
+c
+c    Pierre L'Ecuyer,
+c    Random Number Generation,
+c    in Handbook of Simulation,
+c    edited by Jerry Banks,
+c    Wiley Interscience, page 95, 1998.
+c
+c    Bennett Fox,
+c    Algorithm 647:
+c    Implementation and Relative Efficiency of Quasirandom
+c    Sequence Generators,
+c    ACM Transactions on Mathematical Software,
+c    Volume 12, Number 4, pages 362-376, 1986.
+c
+c    Peter Lewis, Allen Goodman, James Miller,
+c    A Pseudo-Random Number Generator for the System/360,
+c    IBM Systems Journal,
+c    Volume 8, pages 136-143, 1969.
+c
+c  Parameters:
+c
+c    Input/output, integer SEED, the "seed" value, which should NOT be 0.
+c    On output, SEED has been updated.
+c
+c    Output, double precision R8_UNIFORM_01, a new pseudorandom variate,
+c    strictly between 0 and 1.
+c
+      implicit none
+
+      double precision r8_uniform_01
+      integer k
+      integer seed
+
+      if ( seed .eq. 0 ) then
+        write ( *, '(a)' ) ' '
+        write ( *, '(a)' ) 'R8_UNIFORM_01 - Fatal error!'
+        write ( *, '(a)' ) '  Input value of SEED = 0.'
+        stop
+      end if
+
+      k = seed / 127773
+
+      seed = 16807 * ( seed - k * 127773 ) - k * 2836
+
+      if ( seed .lt. 0 ) then
+        seed = seed + 2147483647
+      end if
+c
+c  Although SEED can be represented exactly as a 32 bit integer,
+c  it generally cannot be represented exactly as a 32 bit real number!
+c
+      r8_uniform_01 = dble ( seed ) * 4.656612875D-10
+
+      return
+      end
+      subroutine timestamp ( )
+
+c*********************************************************************72
+c
+cc TIMESTAMP prints out the current YMDHMS date as a timestamp.
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    12 January 2007
+c
+c  Author:
+c
+c    John Burkardt
+c
+c  Parameters:
+c
+c    None
+c
+      implicit none
+
+      character * ( 8 ) ampm
+      integer d
+      character * ( 8 ) date
+      integer h
+      integer m
+      integer mm
+      character * ( 9 ) month(12)
+      integer n
+      integer s
+      character * ( 10 ) time
+      integer y
+
+      save month
+
+      data month /
+     &  'January  ', 'February ', 'March    ', 'April    ', 
+     &  'May      ', 'June     ', 'July     ', 'August   ', 
+     &  'September', 'October  ', 'November ', 'December ' /
+
+      call date_and_time ( date, time )
+
+      read ( date, '(i4,i2,i2)' ) y, m, d
+      read ( time, '(i2,i2,i2,1x,i3)' ) h, n, s, mm
+
+      if ( h .lt. 12 ) then
+        ampm = 'AM'
+      else if ( h .eq. 12 ) then
+        if ( n .eq. 0 .and. s .eq. 0 ) then
+          ampm = 'Noon'
+        else
+          ampm = 'PM'
+        end if
+      else
+        h = h - 12
+        if ( h .lt. 12 ) then
+          ampm = 'PM'
+        else if ( h .eq. 12 ) then
+          if ( n .eq. 0 .and. s .eq. 0 ) then
+            ampm = 'Midnight'
+          else
+            ampm = 'AM'
+          end if
+        end if
+      end if
+
+      write ( *, 
+     &  '(i2,1x,a,1x,i4,2x,i2,a1,i2.2,a1,i2.2,a1,i3.3,1x,a)' ) 
+     &  d, month(m), y, h, ':', n, ':', s, '.', mm, ampm
+
+      return
+      end
+      subroutine update ( np, nd, pos, vel, f, acc, mass, dt )
+
+c*********************************************************************72
+c
+cc UPDATE performs the time integration, using a velocity Verlet algorithm.
+c
+c  Discussion:
+c
+c    The time integration is fully parallel.
+c
+c  Licensing:
+c
+c    This code is distributed under the GNU LGPL license. 
+c
+c  Modified:
+c
+c    13 November 2007
+c
+c  Author:
+c
+c    Original FORTRAN90 version by Bill Magro.
+c    FORTRAN77 version by John Burkardt.
+c
+c  Parameters:
+c
+c    Input, integer NP, the number of particles.
+c
+c    Input, integer ND, the number of spatial dimensions.
+c
+c    Input/output, double precision POS(ND,NP), the position of each particle.
+c
+c    Input/output, double precision VEL(ND,NP), the velocity of each particle.
+c
+c    Input, double precision MASS, the mass of each particle.
+c
+c    Input/output, double precision ACC(ND,NP), the acceleration of each
+c    particle.
+c
+      implicit none
+
+      integer np
+      integer nd
+
+      double precision acc(nd,np)
+      double precision dt
+      double precision f(nd,np)
+      integer i
+      integer j
+      double precision mass
+      double precision pos(nd,np)
+      double precision rmass
+      double precision vel(nd,np)
+
+      rmass = 1.0D+00 / mass
+
+c$omp parallel 
+c$omp& shared ( acc, dt, f, nd, np, pos, rmass, vel )
+c$omp& private ( i, j )
+
+c$omp do
+      do j = 1, np
+        do i = 1, nd
+
+          pos(i,j) = pos(i,j) 
+     &      + vel(i,j) * dt + 0.5D+00 * acc(i,j) * dt * dt
+
+          vel(i,j) = vel(i,j) 
+     &      + 0.5D+00 * dt * ( f(i,j) * rmass + acc(i,j) )
+
+          acc(i,j) = f(i,j) * rmass
+
+        end do
+      end do
+c$omp end do
+
+c$omp end parallel
+
+      return
+      end
diff --git a/src/external/tng_io/src/tests/md_openmp_util.c b/src/external/tng_io/src/tests/md_openmp_util.c
new file mode 100644 (file)
index 0000000..006d292
--- /dev/null
@@ -0,0 +1,762 @@
+#ifdef TNG_BUILD_OPENMP_EXAMPLES
+
+# include <stdlib.h>
+# include <stdio.h>
+# include <time.h>
+# include <math.h>
+# include <omp.h>
+# include "tng_io.h"
+
+int main ();
+void compute ( int np, int nd, float pos[], float vel[],
+    float mass, float f[], float *pot, float *kin );
+float dist ( int nd, float r1[], float r2[], float dr[] );
+void initialize ( int np, int nd, float box[], int *seed, float pos[],
+    float vel[], float acc[] );
+float r8_uniform_01 ( int *seed );
+void timestamp ( void );
+void update ( int np, int nd, float pos[], float vel[], float f[],
+    float acc[], float mass, float dt );
+
+/******************************************************************************/
+
+int main ()
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        MAIN is the main program for MD_OPENMP.
+
+    Discussion:
+
+        MD implements a simple molecular dynamics simulation.
+
+        The program uses Open MP directives to allow parallel computation.
+
+        The velocity Verlet time integration scheme is used.
+
+        The particles interact with a central pair potential.
+
+        Output of the program is saved in the TNG format, which is why this
+        code is included in the TNG API release. The high-level API of the
+        TNG API is used where appropriate.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        8 Jan 2013
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+        TNG trajectory output by Magnus Lundborg.
+
+    Parameters:
+
+        None
+*/
+{
+    float *acc;
+    float *box;
+    float *box_shape;
+    float dt = 0.0002;
+    float e0;
+    float *force;
+    int i;
+    float kinetic;
+    float mass = 1.0;
+    int nd = 3;
+    int np = 50;
+    float *pos;
+    float potential;
+    int proc_num;
+    int seed = 123456789;
+    int step;
+    int step_num = 50000;
+    int step_print;
+    int step_print_index;
+    int step_print_num;
+    int step_save;
+    float *vel;
+    float wtime;
+    tng_trajectory_t traj;
+    tng_molecule_t molecule;
+    tng_chain_t chain;
+    tng_residue_t residue;
+    tng_atom_t atom;
+
+    timestamp ( );
+
+    proc_num = omp_get_num_procs ( );
+
+    acc = ( float * ) malloc ( nd * np * sizeof ( float ) );
+    box = ( float * ) malloc ( nd * sizeof ( float ) );
+    box_shape = (float *) malloc (9 * sizeof (float));
+    force = ( float * ) malloc ( nd * np * sizeof ( float ) );
+    pos = ( float * ) malloc ( nd * np * sizeof ( float ) );
+    vel = ( float * ) malloc ( nd * np * sizeof ( float ) );
+
+    printf ( "\n" );
+    printf ( "MD_OPENMP\n" );
+    printf ( "  C/OpenMP version\n" );
+    printf ( "\n" );
+    printf ( "  A molecular dynamics program.\n" );
+
+    printf ( "\n" );
+    printf ( "  NP, the number of particles in the simulation is %d\n", np );
+    printf ( "  STEP_NUM, the number of time steps, is %d\n", step_num );
+    printf ( "  DT, the size of each time step, is %f\n", dt );
+
+    printf ( "\n" );
+    printf ( "  Number of processors available = %d\n", proc_num );
+    printf ( "  Number of threads =              %d\n", omp_get_max_threads ( ) );
+
+
+    printf("\n");
+    printf("  Initializing trajectory storage.\n");
+    /* Initialize the TNG trajectory */
+#ifdef TNG_EXAMPLE_FILES_DIR
+    tng_util_trajectory_open(TNG_EXAMPLE_FILES_DIR "tng_md_out.tng", 'w', &traj);
+#else
+    tng_util_trajectory_open("/tmp/tng_md_out.tng", 'w', &traj);
+#endif
+
+
+
+    /* Set molecules data */
+    /* N.B. This is still not done using utility functions. The low-level API
+     * is used. */
+    printf("  Creating molecules in trajectory.\n");
+    tng_molecule_add(traj, "water", &molecule);
+    tng_molecule_chain_add(traj, molecule, "W", &chain);
+    tng_chain_residue_add(traj, chain, "WAT", &residue);
+    if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
+    {
+        tng_util_trajectory_close(&traj);
+        printf("  Cannot create molecules.\n");
+        exit(1);
+    }
+    tng_molecule_cnt_set(traj, molecule, np);
+
+
+/*
+    Set the dimensions of the box.
+*/
+    for(i = 0; i < 9; i++)
+    {
+        box_shape[i] = 0.0;
+    }
+    for ( i = 0; i < nd; i++ )
+    {
+        box[i] = 10.0;
+        /* box_shape stores 9 values according to the TNG specs */
+        box_shape[i*nd + i] = box[i];
+    }
+
+
+    printf ( "\n" );
+    printf ( "  Initializing positions, velocities, and accelerations.\n" );
+/*
+    Set initial positions, velocities, and accelerations.
+*/
+    initialize ( np, nd, box, &seed, pos, vel, acc );
+/*
+    Compute the forces and energies.
+*/
+    printf ( "\n" );
+    printf ( "  Computing initial forces and energies.\n" );
+
+    compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
+
+    e0 = potential + kinetic;
+
+    /* Saving frequency */
+    step_save = 400;
+
+    step_print = 0;
+    step_print_index = 0;
+    step_print_num = 10;
+
+/*
+    This is the main time stepping loop:
+        Compute forces and energies,
+        Update positions, velocities, accelerations.
+*/
+    printf("  Every %d steps box shape, particle positions, velocities and forces are\n",
+           step_save);
+    printf("  saved to a TNG trajectory file.\n");
+    printf ( "\n" );
+    printf ( "  At certain step intervals, we report the potential and kinetic energies.\n" );
+    printf ( "  The sum of these energies should be a constant.\n" );
+    printf ( "  As an accuracy check, we also print the relative error\n" );
+    printf ( "  in the total energy.\n" );
+    printf ( "\n" );
+    printf ( "      Step      Potential       Kinetic        (P+K-E0)/E0\n" );
+    printf ( "                Energy P        Energy K       Relative Energy Error\n" );
+    printf ( "\n" );
+
+    step = 0;
+    printf ( "  %8d  %14f  %14f  %14e\n",
+        step, potential, kinetic, ( potential + kinetic - e0 ) / e0 );
+    step_print_index++;
+    step_print = ( step_print_index * step_num ) / step_print_num;
+
+    /* Set the output frequency of box shape, positions, velocities and forces */
+    if(tng_util_box_shape_write_frequency_set(traj, step_save) != TNG_SUCCESS)
+    {
+        printf("Error setting writing frequency data. %s: %d\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_util_pos_write_frequency_set(traj, step_save) != TNG_SUCCESS)
+    {
+        printf("Error setting writing frequency data. %s: %d\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_util_vel_write_frequency_set(traj, step_save) != TNG_SUCCESS)
+    {
+        printf("Error setting writing frequency data. %s: %d\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_util_force_write_frequency_set(traj, step_save) != TNG_SUCCESS)
+    {
+        printf("Error setting writing frequency data. %s: %d\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+
+    /* Write the first frame of box shape, positions, velocities and forces */
+    if(tng_util_box_shape_write(traj, 0, box_shape) != TNG_SUCCESS)
+    {
+        printf("Error writing box shape. %s: %d\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_util_pos_write(traj, 0, pos) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_util_vel_write(traj, 0, vel) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+    if(tng_util_force_write(traj, 0, force) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        exit(1);
+    }
+
+    wtime = omp_get_wtime ( );
+
+    for ( step = 1; step < step_num; step++ )
+    {
+        compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
+
+        if ( step == step_print )
+        {
+            printf ( "  %8d  %14f  %14f  %14e\n", step, potential, kinetic,
+             ( potential + kinetic - e0 ) / e0 );
+            step_print_index++;
+            step_print = ( step_print_index * step_num ) / step_print_num;
+        }
+        if(step % step_save == 0)
+        {
+            /* Write box shape, positions, velocities and forces */
+            if(tng_util_box_shape_write(traj, step, box_shape) != TNG_SUCCESS)
+            {
+                printf("Error writing box shape. %s: %d\n",
+                       __FILE__, __LINE__);
+                exit(1);
+            }
+            if(tng_util_pos_write(traj, step, pos) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                break;
+            }
+            if(tng_util_vel_write(traj, step, vel) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                break;
+            }
+            if(tng_util_force_write(traj, step, force) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                break;
+            }
+        }
+        update ( np, nd, pos, vel, force, acc, mass, dt );
+    }
+    wtime = omp_get_wtime ( ) - wtime;
+
+    printf ( "\n" );
+    printf ( "  Elapsed time for main computation:\n" );
+    printf ( "  %f seconds.\n", wtime );
+
+    free ( acc );
+    free ( box );
+    free ( box_shape );
+    free ( force );
+    free ( pos );
+    free ( vel );
+
+    /* Close the TNG output. */
+    tng_util_trajectory_close(&traj);
+
+    printf ( "\n" );
+    printf ( "MD_OPENMP\n" );
+    printf ( "  Normal end of execution.\n" );
+
+    printf ( "\n" );
+    timestamp ( );
+
+    return 0;
+}
+/******************************************************************************/
+
+void compute ( int np, int nd, float pos[], float vel[],
+    float mass, float f[], float *pot, float *kin )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        COMPUTE computes the forces and energies.
+
+    Discussion:
+
+        The computation of forces and energies is fully parallel.
+
+        The potential function V(X) is a harmonic well which smoothly
+        saturates to a maximum value at PI/2:
+
+            v(x) = ( sin ( min ( x, PI2 ) ) )**2
+
+        The derivative of the potential is:
+
+            dv(x) = 2.0 * sin ( min ( x, PI2 ) ) * cos ( min ( x, PI2 ) )
+                        = sin ( 2.0 * min ( x, PI2 ) )
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        21 November 2007
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int NP, the number of particles.
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input, float POS[ND*NP], the position of each particle.
+
+        Input, float VEL[ND*NP], the velocity of each particle.
+
+        Input, float MASS, the mass of each particle.
+
+        Output, float F[ND*NP], the forces.
+
+        Output, float *POT, the total potential energy.
+
+        Output, float *KIN, the total kinetic energy.
+*/
+{
+    float d;
+    float d2;
+    int i;
+    int j;
+    int k;
+    float ke;
+    float pe;
+    float PI2 = 3.141592653589793 / 2.0;
+    float rij[3];
+
+    pe = 0.0;
+    ke = 0.0;
+
+# pragma omp parallel \
+    shared ( f, nd, np, pos, vel ) \
+    private ( i, j, k, rij, d, d2 )
+
+
+# pragma omp for reduction ( + : pe, ke )
+    for ( k = 0; k < np; k++ )
+    {
+/*
+    Compute the potential energy and forces.
+*/
+        for ( i = 0; i < nd; i++ )
+        {
+            f[i+k*nd] = 0.0;
+        }
+
+        for ( j = 0; j < np; j++ )
+        {
+            if ( k != j )
+            {
+                d = dist ( nd, pos+k*nd, pos+j*nd, rij );
+/*
+    Attribute half of the potential energy to particle J.
+*/
+                if ( d < PI2 )
+                {
+                    d2 = d;
+                }
+                else
+                {
+                    d2 = PI2;
+                }
+
+                pe = pe + 0.5 * pow ( sin ( d2 ), 2 );
+
+                for ( i = 0; i < nd; i++ )
+                {
+                    f[i+k*nd] = f[i+k*nd] - rij[i] * sin ( 2.0 * d2 ) / d;
+                }
+            }
+        }
+/*
+    Compute the kinetic energy.
+*/
+        for ( i = 0; i < nd; i++ )
+        {
+            ke = ke + vel[i+k*nd] * vel[i+k*nd];
+        }
+    }
+
+    ke = ke * 0.5 * mass;
+
+    *pot = pe;
+    *kin = ke;
+
+    return;
+}
+/******************************************************************************/
+
+float dist ( int nd, float r1[], float r2[], float dr[] )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        DIST computes the displacement (and its norm) between two particles.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        21 November 2007
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input, float R1[ND], R2[ND], the positions of the particles.
+
+        Output, float DR[ND], the displacement vector.
+
+        Output, float D, the Euclidean norm of the displacement.
+*/
+{
+    float d;
+    int i;
+
+    d = 0.0;
+    for ( i = 0; i < nd; i++ )
+    {
+        dr[i] = r1[i] - r2[i];
+        d = d + dr[i] * dr[i];
+    }
+    d = sqrt ( d );
+
+    return d;
+}
+/******************************************************************************/
+
+void initialize ( int np, int nd, float box[], int *seed, float pos[],
+    float vel[], float acc[] )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        INITIALIZE initializes the positions, velocities, and accelerations.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        21 November 2007
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int NP, the number of particles.
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input, float BOX[ND], specifies the maximum position
+        of particles in each dimension.
+
+        Input, int *SEED, a seed for the random number generator.
+
+        Output, float POS[ND*NP], the position of each particle.
+
+        Output, float VEL[ND*NP], the velocity of each particle.
+
+        Output, float ACC[ND*NP], the acceleration of each particle.
+*/
+{
+    int i;
+    int j;
+/*
+    Give the particles random positions within the box.
+*/
+    for ( i = 0; i < nd; i++ )
+    {
+        for ( j = 0; j < np; j++ )
+        {
+            pos[i+j*nd] = box[i] * r8_uniform_01 ( seed );
+        }
+    }
+
+    for ( j = 0; j < np; j++ )
+    {
+        for ( i = 0; i < nd; i++ )
+        {
+            vel[i+j*nd] = 0.0;
+        }
+    }
+    for ( j = 0; j < np; j++ )
+    {
+        for ( i = 0; i < nd; i++ )
+        {
+            acc[i+j*nd] = 0.0;
+        }
+    }
+    return;
+}
+/******************************************************************************/
+
+float r8_uniform_01 ( int *seed )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        R8_UNIFORM_01 is a unit pseudorandom R8.
+
+    Discussion:
+
+        This routine implements the recursion
+
+            seed = 16807 * seed mod ( 2**31 - 1 )
+            unif = seed / ( 2**31 - 1 )
+
+        The integer arithmetic never requires more than 32 bits,
+        including a sign bit.
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        11 August 2004
+
+    Author:
+
+        John Burkardt
+
+    Reference:
+
+        Paul Bratley, Bennett Fox, Linus Schrage,
+        A Guide to Simulation,
+        Springer Verlag, pages 201-202, 1983.
+
+        Bennett Fox,
+        Algorithm 647:
+        Implementation and Relative Efficiency of Quasirandom
+        Sequence Generators,
+        ACM Transactions on Mathematical Software,
+        Volume 12, Number 4, pages 362-376, 1986.
+
+    Parameters:
+
+        Input/output, int *SEED, a seed for the random number generator.
+
+        Output, float R8_UNIFORM_01, a new pseudorandom variate, strictly between
+        0 and 1.
+*/
+{
+    int k;
+    float r;
+
+    k = *seed / 127773;
+
+    *seed = 16807 * ( *seed - k * 127773 ) - k * 2836;
+
+    if ( *seed < 0 )
+    {
+        *seed = *seed + 2147483647;
+    }
+
+    r = ( float ) ( *seed ) * 4.656612875E-10;
+
+    return r;
+}
+/******************************************************************************/
+
+void timestamp ( void )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        TIMESTAMP prints the current YMDHMS date as a time stamp.
+
+    Example:
+
+        31 May 2001 09:45:54 AM
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        24 September 2003
+
+    Author:
+
+        John Burkardt
+
+    Parameters:
+
+        None
+*/
+{
+# define TIME_SIZE 40
+
+    static char time_buffer[TIME_SIZE];
+    const struct tm *tm;
+    time_t now;
+
+    now = time ( NULL );
+    tm = localtime ( &now );
+
+    strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
+
+    printf ( "%s\n", time_buffer );
+
+    return;
+# undef TIME_SIZE
+}
+/******************************************************************************/
+
+void update ( int np, int nd, float pos[], float vel[], float f[],
+    float acc[], float mass, float dt )
+
+/******************************************************************************/
+/*
+    Purpose:
+
+        UPDATE updates positions, velocities and accelerations.
+
+    Discussion:
+
+        The time integration is fully parallel.
+
+        A velocity Verlet algorithm is used for the updating.
+
+        x(t+dt) = x(t) + v(t) * dt + 0.5 * a(t) * dt * dt
+        v(t+dt) = v(t) + 0.5 * ( a(t) + a(t+dt) ) * dt
+        a(t+dt) = f(t) / m
+
+    Licensing:
+
+        This code is distributed under the GNU LGPL license.
+
+    Modified:
+
+        17 April 2009
+
+    Author:
+
+        Original FORTRAN77 version by Bill Magro.
+        C version by John Burkardt.
+
+    Parameters:
+
+        Input, int NP, the number of particles.
+
+        Input, int ND, the number of spatial dimensions.
+
+        Input/output, float POS[ND*NP], the position of each particle.
+
+        Input/output, float VEL[ND*NP], the velocity of each particle.
+
+        Input, float F[ND*NP], the force on each particle.
+
+        Input/output, float ACC[ND*NP], the acceleration of each particle.
+
+        Input, float MASS, the mass of each particle.
+
+        Input, float DT, the time step.
+*/
+{
+    int i;
+    int j;
+    float rmass;
+
+    rmass = 1.0 / mass;
+
+# pragma omp parallel \
+    shared ( acc, dt, f, nd, np, pos, rmass, vel ) \
+    private ( i, j )
+
+# pragma omp for
+    for ( j = 0; j < np; j++ )
+    {
+        for ( i = 0; i < nd; i++ )
+        {
+            pos[i+j*nd] = pos[i+j*nd] + vel[i+j*nd] * dt + 0.5 * acc[i+j*nd] * dt * dt;
+            vel[i+j*nd] = vel[i+j*nd] + 0.5 * dt * ( f[i+j*nd] * rmass + acc[i+j*nd] );
+            acc[i+j*nd] = f[i+j*nd] * rmass;
+        }
+    }
+
+
+    return;
+}
+
+#endif
diff --git a/src/external/tng_io/src/tests/tng_io_read_pos.c b/src/external/tng_io/src/tests/tng_io_read_pos.c
new file mode 100644 (file)
index 0000000..079cad7
--- /dev/null
@@ -0,0 +1,153 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.0
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../../include/tng_io.h"
+
+int main(int argc, char **argv)
+{
+    tng_trajectory_t traj;
+    union data_values ***positions = 0; /* A 3-dimensional array to be populated */
+    int64_t n_particles, n_values_per_frame, n_frames, tot_n_frames;
+    char data_type;
+    int i, j;
+    int particle = 0;
+    /* Set a default frame range */
+    int first_frame = 0, last_frame = 50;
+    char atom_name[64], res_name[64], chain_name[64];
+
+    if(argc <= 1)
+    {
+        printf("No file specified\n");
+        printf("Usage:\n");
+        printf("tng_io_read_pos <tng_file> [particle number = %d] "
+               "[first_frame = %d] [last_frame = %d]\n",
+               particle, first_frame, last_frame);
+        exit(1);
+    }
+
+    /* A reference must be passed to allocate memory */
+    if(tng_trajectory_init(&traj) != TNG_SUCCESS)
+    {
+        tng_trajectory_destroy(&traj);
+        exit(1);
+    }
+    tng_input_file_set(traj, argv[1]);
+
+    /* Read the file headers */
+    tng_file_headers_read(traj, TNG_USE_HASH);
+
+    if(argc >= 3)
+    {
+        particle = strtol(argv[2], 0, 10);
+        if(argc >= 4)
+        {
+            first_frame = strtol(argv[3], 0, 10);
+            if(argc >= 5)
+            {
+                last_frame = strtol(argv[4], 0, 10);
+            }
+        }
+    }
+
+    if(tng_num_frames_get(traj, &tot_n_frames) != TNG_SUCCESS)
+    {
+        printf("Cannot determine the number of frames in the file\n");
+        tng_trajectory_destroy(&traj);
+        exit(1);
+    }
+
+    printf("%"PRId64" frames in file\n", tot_n_frames);
+
+    if(last_frame > tot_n_frames - 1)
+    {
+        last_frame = tot_n_frames - 1;
+    }
+
+    n_frames = last_frame - first_frame + 1;
+
+    if(tng_atom_name_of_particle_nr_get(traj, particle, atom_name,
+                                        sizeof(atom_name)) ==
+       TNG_SUCCESS &&
+       tng_residue_name_of_particle_nr_get(traj, particle, res_name,
+                                           sizeof(res_name)) ==
+       TNG_SUCCESS &&
+       tng_chain_name_of_particle_nr_get(traj, particle, chain_name,
+                                         sizeof(chain_name)) ==
+       TNG_SUCCESS)
+    {
+        printf("Particle: %s (%s: %s)\n", atom_name, chain_name, res_name);
+    }
+    else
+    {
+        printf("Particle name not found\n");
+    }
+
+    /* Get the positions of all particles in the requested frame range.
+       The positions are stored in the positions array.
+       N.B. No proper error checks. */
+    if(tng_particle_data_interval_get(traj, TNG_TRAJ_POSITIONS, first_frame,
+       last_frame, TNG_USE_HASH, &positions, &n_particles, &n_values_per_frame,
+       &data_type) == TNG_SUCCESS)
+    {
+        if(particle >= n_particles)
+        {
+            printf("Chosen particle out of range. Only %"PRId64" particles in trajectory.\n", n_particles);
+        }
+        else
+        {
+            /* Print the positions of the wanted particle (zero based) */
+            for(i=0; i<n_frames; i++)
+            {
+                printf("%d", first_frame + i);
+                for(j=0; j<n_values_per_frame; j++)
+                {
+                    switch(data_type)
+                    {
+                    case TNG_INT_DATA:
+                        printf("\t%"PRId64"", positions[i][particle][j].i);
+                        break;
+                    case TNG_FLOAT_DATA:
+                        printf("\t%f", positions[i][particle][j].f);
+                        break;
+                    case TNG_DOUBLE_DATA:
+                        printf("\t%f", positions[i][particle][j].d);
+                        break;
+                    default:
+                        break;
+                    }
+                    printf("\n");
+                }
+            }
+        }
+    }
+    else
+    {
+        printf("Cannot read positions\n");
+    }
+
+    /* Free memory */
+    if(positions)
+    {
+        tng_particle_data_values_free(traj, positions, n_frames, n_particles,
+                                      n_values_per_frame, data_type);
+    }
+    tng_trajectory_destroy(&traj);
+
+    return(0);
+}
diff --git a/src/external/tng_io/src/tests/tng_io_read_pos_util.c b/src/external/tng_io/src/tests/tng_io_read_pos_util.c
new file mode 100644 (file)
index 0000000..64df9df
--- /dev/null
@@ -0,0 +1,135 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *  The high-level API of the TNG API is used where appropriate.
+ *
+ *                      VERSION 1.0
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../../include/tng_io.h"
+
+int main(int argc, char **argv)
+{
+    tng_trajectory_t traj;
+    /* Assume that the data is stored as floats. The data is placed in 1-D
+     * arrays */
+    float *positions = 0, *box_shape = 0;
+    int64_t n_particles, n_frames, tot_n_frames, stride_length, i, j;
+    /* Set a default frame range */
+    int first_frame = 0, last_frame = 5000, n_strides;
+
+    if(argc <= 1)
+    {
+        printf("No file specified\n");
+        printf("Usage:\n");
+        printf("tng_io_read_pos_util <tng_file> "
+               "[first_frame = %d] [last_frame = %d]\n",
+               first_frame, last_frame);
+        exit(1);
+    }
+
+    /* A reference must be passed to allocate memory */
+    tng_util_trajectory_open(argv[1], 'r', &traj);
+
+    if(argc >= 3)
+    {
+        first_frame = strtol(argv[2], 0, 10);
+        if(argc >= 4)
+        {
+            last_frame = strtol(argv[3], 0, 10);
+        }
+    }
+
+    if(tng_num_frames_get(traj, &tot_n_frames) != TNG_SUCCESS)
+    {
+        printf("Cannot determine the number of frames in the file\n");
+        tng_util_trajectory_close(&traj);
+        exit(1);
+    }
+
+    if(tng_num_particles_get(traj, &n_particles) != TNG_SUCCESS)
+    {
+        printf("Cannot determine the number of particles in the file\n");
+        tng_util_trajectory_close(&traj);
+        exit(1);
+    }
+
+    printf("%"PRId64" frames in file\n", tot_n_frames);
+
+    if(last_frame > tot_n_frames - 1)
+    {
+        last_frame = tot_n_frames - 1;
+    }
+
+
+    n_frames = last_frame - first_frame + 1;
+
+    if(tng_util_box_shape_read(traj, &box_shape, &stride_length) ==
+       TNG_SUCCESS)
+    {
+        printf("Simulation box shape: ");
+        for(i=0; i < 9; i++)
+        {
+            printf("%f ", box_shape[i]);
+        }
+        printf("\n");
+    }
+    else
+    {
+        printf("Simulation box shape not set in the file (or could not be read)\n");
+    }
+
+    n_strides = (n_frames % stride_length) ? n_frames / stride_length + 1:
+                n_frames / stride_length;
+
+    /* Get the positions of all particles in the requested frame range.
+     * The positions are stored in the positions array.
+     * N.B. No proper error checks. */
+    if(tng_util_pos_read_range(traj, 0, last_frame, &positions, &stride_length)
+       == TNG_SUCCESS)
+    {
+        /* Print the positions of the wanted particle (zero based) */
+        for(i=0; i < n_strides; i++)
+        {
+            printf("\nFrame %"PRId64":\n", first_frame + i*stride_length);
+            for(j=0; j < n_particles; j++)
+            {
+                printf("Atom nr: %"PRId64"", j);
+                printf("\t%f", positions[i*n_particles*3+j*3]);
+                printf("\t%f", positions[i*n_particles*3+j*3+1]);
+                printf("\t%f", positions[i*n_particles*3+j*3+2]);
+                printf("\n");
+            }
+        }
+    }
+    else
+    {
+        printf("Cannot read positions\n");
+    }
+
+    /* Free memory */
+    if(positions)
+    {
+        free(positions);
+    }
+    if(box_shape)
+    {
+        free(box_shape);
+    }
+    tng_util_trajectory_close(&traj);
+
+    return(0);
+}
diff --git a/src/external/tng_io/src/tests/tng_io_testing.c b/src/external/tng_io/src/tests/tng_io_testing.c
new file mode 100644 (file)
index 0000000..8d8a68a
--- /dev/null
@@ -0,0 +1,705 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.0
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "../../include/tng_io.h"
+
+static tng_function_status tng_test_setup_molecules(tng_trajectory_t traj)
+{
+    tng_molecule_t molecule;
+    tng_chain_t chain;
+    tng_residue_t residue;
+    tng_atom_t atom;
+    int64_t cnt;
+
+    tng_molecule_add(traj, "water", &molecule);
+    tng_molecule_chain_add(traj, molecule, "W", &chain);
+    tng_chain_residue_add(traj, chain, "WAT", &residue);
+    if(tng_residue_atom_add(traj, residue, "O", "O", &atom) == TNG_CRITICAL)
+    {
+        return(TNG_CRITICAL);
+    }
+    if(tng_residue_atom_add(traj, residue, "HO1", "H", &atom) == TNG_CRITICAL)
+    {
+        return(TNG_CRITICAL);
+    }
+    if(tng_residue_atom_add(traj, residue, "HO2", "H", &atom) == TNG_CRITICAL)
+    {
+        return(TNG_CRITICAL);
+    }
+    tng_molecule_cnt_set(traj, molecule, 200);
+    tng_molecule_cnt_get(traj, molecule, &cnt);
+/*     printf("Created %"PRId64" %s molecules.\n", cnt, molecule->name); */
+
+/*     traj->molecule_cnt_list[traj->n_molecules-1] = 5;
+//     tng_molecule_name_set(traj, &traj->molecules[1], "ligand");
+//     tng_molecule_name_set(traj, &traj->molecules[2], "water");
+//     tng_molecule_name_set(traj, &traj->molecules[3], "dummy");
+//     traj->molecules[0].id = 0;
+//     traj->molecules[1].id = 1;
+//     traj->molecules[2].id = 2;
+//     traj->molecules[3].id = 3;
+
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom1", "type1") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom2", "type1") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom3", "type1") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom4", "type2") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom5", "type2") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom6", "type2") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[0], "atom7", "type3") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[1], "C1", "C") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[1], "O1", "O") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[1], "H11", "H") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[1], "H12", "H") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+//     if(tng_add_atom_to_molecule(traj, &traj->molecules[1], "H13", "H") == TNG_CRITICAL)
+//     {
+//         return(TNG_CRITICAL);
+//     }
+*/
+    return(TNG_SUCCESS);
+}
+
+static tng_function_status tng_test_read_and_write_file
+                (tng_trajectory_t traj)
+{
+    tng_function_status stat;
+
+    stat = tng_file_headers_read(traj, TNG_USE_HASH);
+    if(stat == TNG_CRITICAL)
+    {
+        return(stat);
+    }
+    stat = tng_file_headers_write(traj, TNG_USE_HASH);
+    if(stat == TNG_CRITICAL)
+    {
+        return(stat);
+    }
+
+    while(stat == TNG_SUCCESS)
+    {
+        stat = tng_frame_set_read_next(traj, TNG_USE_HASH);
+        if(stat != TNG_SUCCESS)
+        {
+            return(stat);
+        }
+        stat = tng_frame_set_write(traj, TNG_USE_HASH);
+    }
+
+    return(stat);
+}
+
+static tng_function_status tng_test_write_and_read_traj(tng_trajectory_t *traj)
+{
+    int i, j, k, nr, cnt;
+    float *data, *molpos, *charges;
+    int64_t mapping[300], n_particles, n_frames_per_frame_set, tot_n_mols;
+//     int64_t frame_nr;
+    double box_shape[9];
+    char atom_type[16], annotation[128];
+    tng_function_status stat = TNG_SUCCESS;
+
+    tng_medium_stride_length_set(*traj, 10);
+    tng_long_stride_length_set(*traj, 100);
+
+    /* Create molecules */
+    if(tng_test_setup_molecules(*traj) == TNG_CRITICAL)
+    {
+        return(TNG_CRITICAL);
+    }
+
+    /* Set the box shape */
+    box_shape[1] = box_shape[2] = box_shape[3] = box_shape[5] = box_shape[6] =
+    box_shape[7] = 0;
+    box_shape[0] = 150.0;
+    box_shape[4] = 145.5;
+    box_shape[8] = 155.5;
+    if(tng_data_block_add(*traj, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE", TNG_DOUBLE_DATA,
+                       TNG_NON_TRAJECTORY_BLOCK, 1, 9, 1, TNG_UNCOMPRESSED,
+                       box_shape) == TNG_CRITICAL)
+    {
+        tng_trajectory_destroy(traj);
+        printf("Cannot write trajectory box shape.\n");
+        exit(1);
+    }
+
+    /* Set partial charges (treat the water as TIP3P. */
+    tng_num_particles_get(*traj, &n_particles);
+    charges = malloc(sizeof(float) * n_particles);
+    for(i = 0; i < n_particles; i++)
+    {
+        stat = tng_atom_type_of_particle_nr_get(*traj, i, atom_type,
+                                                sizeof(atom_type));
+        if(stat == TNG_CRITICAL)
+        {
+            break;
+        }
+        if(atom_type[0] == 'O')
+        {
+            charges[i] = -0.834;
+        }
+        else if(atom_type[0] == 'H')
+        {
+            charges[i] = 0.417;
+        }
+    }
+    if(stat == TNG_CRITICAL)
+    {
+        free(charges);
+        printf("Failed setting partial charges.\n");
+        return(TNG_CRITICAL);
+    }
+
+    stat = tng_particle_data_block_add(*traj, TNG_TRAJ_PARTIAL_CHARGES, "PARTIAL CHARGES",
+                                       TNG_FLOAT_DATA, TNG_NON_TRAJECTORY_BLOCK,
+                                       1, 1, 1, 0, n_particles,
+                                       TNG_UNCOMPRESSED, charges);
+    free(charges);
+    if(stat != TNG_SUCCESS)
+    {
+        printf("Failed adding partial charges\n");
+        return(TNG_CRITICAL);
+    }
+
+
+    /* Generate a custom annotation data block */
+    strcpy(annotation, "This trajectory was generated from tng_io_testing. "
+                       "It is not a real MD trajectory.");
+    if(tng_data_block_add(*traj, 10100, "DETAILS", TNG_CHAR_DATA,
+                          TNG_NON_TRAJECTORY_BLOCK, 1, 1, 1, TNG_UNCOMPRESSED,
+                          annotation) != TNG_SUCCESS)
+    {
+        printf("Failed adding details annotation data block.\n");
+        return(TNG_CRITICAL);
+    }
+
+    /* Write file headers (includes non trajectory data blocks */
+    if(tng_file_headers_write(*traj, TNG_SKIP_HASH) == TNG_CRITICAL)
+    {
+        printf("Cannot write file headers.\n");
+    }
+
+
+    tng_num_frames_per_frame_set_get(*traj, &n_frames_per_frame_set);
+
+    data = malloc(sizeof(float) * n_particles *
+                  n_frames_per_frame_set * 3);
+    if(!data)
+    {
+        printf("Cannot allocate memory. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+    tng_num_molecules_get(*traj, &tot_n_mols);
+    molpos = malloc(sizeof(float) * tot_n_mols * 3);
+
+    /* Set initial coordinates */
+    for(i = 0; i < tot_n_mols; i++)
+    {
+        nr = i * 3;
+        /* Somewhat random coordinates (between 0 and 100),
+         * but not specifying a random seed */
+        molpos[nr] = 100.0 * rand() / (RAND_MAX + 1.0);
+        molpos[nr+1] = 100.0 * rand() / (RAND_MAX + 1.0);
+        molpos[nr+2] = 100.0 * rand() / (RAND_MAX + 1.0);
+    }
+
+    /* Generate 200 frame sets - each with 100 frames (by default) */
+    for(i = 0; i < 200; i++)
+    {
+        cnt = 0;
+        for(j = 0; j < n_frames_per_frame_set; j++)
+        {
+            for(k = 0; k < tot_n_mols; k++)
+            {
+                nr = k * 3;
+                /* Move -1 to 1 */
+                molpos[nr] += 2 * (rand() / (RAND_MAX + 1.0)) - 1;
+                molpos[nr+1] += 2 * (rand() / (RAND_MAX + 1.0)) - 1;
+                molpos[nr+2] += 2 * (rand() / (RAND_MAX + 1.0)) - 1;
+
+                data[cnt++] = molpos[nr];
+                data[cnt++] = molpos[nr + 1];
+                data[cnt++] = molpos[nr + 2];
+                data[cnt++] = molpos[nr] + 1;
+                data[cnt++] = molpos[nr + 1] + 1;
+                data[cnt++] = molpos[nr + 2] + 1;
+                data[cnt++] = molpos[nr] - 1;
+                data[cnt++] = molpos[nr + 1] - 1;
+                data[cnt++] = molpos[nr + 2] - 1;
+            }
+        }
+        if(tng_frame_set_new(*traj, i * n_frames_per_frame_set,
+            n_frames_per_frame_set) != TNG_SUCCESS)
+        {
+            printf("Error creating frame set %d. %s: %d\n",
+                   i, __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+
+        tng_frame_set_particle_mapping_free(*traj);
+
+        /* Setup particle mapping. Use 4 different mapping blocks with arbitrary
+         * mappings. */
+        for(k=0; k<150; k++)
+        {
+            mapping[k]=k;
+        }
+        if(tng_particle_mapping_add(*traj, 0, 150, mapping) != TNG_SUCCESS)
+        {
+            printf("Error creating particle mapping. %s: %d\n",
+                   __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+        for(k=0; k<150; k++)
+        {
+            mapping[k]=599-k;
+        }
+        if(tng_particle_mapping_add(*traj, 150, 150, mapping) != TNG_SUCCESS)
+        {
+            printf("Error creating particle mapping. %s: %d\n",
+                   __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+        for(k=0; k<150; k++)
+        {
+            mapping[k]=k+150;
+        }
+        if(tng_particle_mapping_add(*traj, 300, 150, mapping) != TNG_SUCCESS)
+        {
+            printf("Error creating particle mapping. %s: %d\n",
+                   __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+        for(k=0; k<150; k++)
+        {
+            mapping[k]=449-k;
+        }
+        if(tng_particle_mapping_add(*traj, 450, 150, mapping) != TNG_SUCCESS)
+        {
+            printf("Error creating particle mapping. %s: %d\n",
+                   __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+
+        /* Add the positions in a data block */
+        if(tng_particle_data_block_add(*traj, TNG_TRAJ_POSITIONS,
+                                       "POSITIONS",
+                                       TNG_FLOAT_DATA,
+                                       TNG_TRAJECTORY_BLOCK,
+                                       n_frames_per_frame_set, 3,
+                                       1, 0, n_particles,
+/*                                        TNG_UNCOMPRESSED, */
+                                       TNG_GZIP_COMPRESSION,
+                                       data) != TNG_SUCCESS)
+        {
+            printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+        /* Write the frame set */
+        if(tng_frame_set_write(*traj, TNG_SKIP_HASH) != TNG_SUCCESS)
+        {
+            printf("Error writing frame set. %s: %d\n", __FILE__, __LINE__);
+            free(molpos);
+            free(data);
+            return(TNG_CRITICAL);
+        }
+    }
+
+    /* Write two more frame sets one frame at a time */
+
+    /* Make a new frame set - if always using the same mapping blocks
+     * it is not necessary to explicitly add a new frame set - it will
+     * be added automatically when adding data for a frame */
+/*    if(tng_frame_set_new(*traj, i * n_frames_per_frame_set,
+        n_frames_per_frame_set) != TNG_SUCCESS)
+    {
+        printf("Error creating frame set %d. %s: %d\n",
+                i, __FILE__, __LINE__);
+        free(molpos);
+        free(data);
+        return(TNG_CRITICAL);
+    }
+
+    frame_nr = i * n_frames_per_frame_set;
+
+    for(k=0; k<300; k++)
+    {
+        mapping[k]=k;
+    }
+    *//* Just use two particle mapping blocks in this frame set *//*
+    if(tng_particle_mapping_add(*traj, 0, 300, mapping) != TNG_SUCCESS)
+    {
+        printf("Error creating particle mapping. %s: %d\n",
+                __FILE__, __LINE__);
+        free(molpos);
+        free(data);
+        return(TNG_CRITICAL);
+    }
+    for(k=0; k<300; k++)
+    {
+        mapping[k]=599-k;
+    }
+    if(tng_particle_mapping_add(*traj, 300, 300, mapping) != TNG_SUCCESS)
+    {
+        printf("Error creating particle mapping. %s: %d\n",
+                __FILE__, __LINE__);
+        free(molpos);
+        free(data);
+        return(TNG_CRITICAL);
+    }
+
+    *//* Add the data block to the current frame set *//*
+    if(tng_particle_data_block_add(*traj, TNG_TRAJ_POSITIONS,
+                                    "POSITIONS",
+                                    TNG_FLOAT_DATA,
+                                    TNG_TRAJECTORY_BLOCK,
+                                    n_frames_per_frame_set, 3,
+                                    1, 0, n_particles,
+                                    TNG_UNCOMPRESSED,
+                                    0) != TNG_SUCCESS)
+    {
+        printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+        free(molpos);
+        free(data);
+        return(TNG_CRITICAL);
+    }
+
+    *//* Write the frame set to disk *//*
+    if(tng_frame_set_write(*traj, TNG_SKIP_HASH) != TNG_SUCCESS)
+    {
+        printf("Error writing frame set. %s: %d\n", __FILE__, __LINE__);
+        free(molpos);
+        free(data);
+        return(TNG_CRITICAL);
+    }
+
+    *//* Write particle data to disk - one frame at a time *//*
+    for(i = 0; i < n_frames_per_frame_set * 2; i++)
+    {
+        for(j = 0; j < 2; j++)
+        {
+            cnt = 0;
+            for(k = 0; k < tot_n_mols/2; k++)
+            {
+                nr = k * 3;
+                *//* Move -1 to 1 *//*
+                molpos[nr] += 2 * (rand() / (RAND_MAX + 1.0)) - 1;
+                molpos[nr+1] += 2 * (rand() / (RAND_MAX + 1.0)) - 1;
+                molpos[nr+2] += 2 * (rand() / (RAND_MAX + 1.0)) - 1;
+
+                data[cnt++] = molpos[nr];
+                data[cnt++] = molpos[nr + 1];
+                data[cnt++] = molpos[nr + 2];
+                data[cnt++] = molpos[nr] + 1;
+                data[cnt++] = molpos[nr + 1] + 1;
+                data[cnt++] = molpos[nr + 2] + 1;
+                data[cnt++] = molpos[nr] - 1;
+                data[cnt++] = molpos[nr + 1] - 1;
+                data[cnt++] = molpos[nr + 2] - 1;
+            }
+            if(tng_frame_particle_data_write(*traj, frame_nr + i,
+                                          TNG_TRAJ_POSITIONS, j * 300, 300,
+                                          data, TNG_SKIP_HASH) != TNG_SUCCESS)
+            {
+                printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+                free(molpos);
+                free(data);
+                return(TNG_CRITICAL);
+            }
+        }
+    }
+*/
+    free(molpos);
+    free(data);
+
+    tng_trajectory_destroy(traj);
+    tng_trajectory_init(traj);
+#ifdef TNG_EXAMPLE_FILES_DIR
+    tng_input_file_set(*traj, TNG_EXAMPLE_FILES_DIR "tng_test.tng");
+#else
+    tng_input_file_set(*traj, "/tmp/tng_test.tng");
+#endif
+
+    stat = tng_file_headers_read(*traj, TNG_SKIP_HASH);
+
+    while(stat == TNG_SUCCESS)
+    {
+        stat = tng_frame_set_read_next(*traj, TNG_SKIP_HASH);
+    }
+
+    return(stat);
+}
+
+/* This test relies on knowing that the box shape is stored as double */
+tng_function_status tng_test_get_box_data(tng_trajectory_t traj)
+{
+    int64_t n_frames, n_values_per_frame;
+    union data_values **values = 0;
+    char type;
+
+    if(tng_data_get(traj, TNG_TRAJ_BOX_SHAPE, &values, &n_frames,
+                    &n_values_per_frame, &type) != TNG_SUCCESS)
+    {
+        printf("Failed getting box shape. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+/*
+//     int64_t i, j;
+//     printf("Box shape:");
+//     for(i=0; i<n_frames; i++)
+//     {
+//         for(j=0; j<n_values_per_frame; j++)
+//         {
+//             printf("\t%f", (values[i][j]).d);
+//         }
+//         printf("\n");
+//     }
+*/
+    tng_data_values_free(traj, values, n_frames, n_values_per_frame, type);
+
+    return(TNG_SUCCESS);
+}
+
+/* This test relies on knowing that the positions are stored as float
+ * and that the data is not sparse (i.e. as many frames in the data
+ * as in the frame set */
+tng_function_status tng_test_get_positions_data(tng_trajectory_t traj)
+{
+    int64_t n_frames, n_particles, n_values_per_frame;
+    union data_values ***values = 0;
+    char type;
+
+    if(tng_particle_data_get(traj, TNG_TRAJ_POSITIONS, &values, &n_frames,
+                             &n_particles, &n_values_per_frame, &type) !=
+       TNG_SUCCESS)
+    {
+        printf("Failed getting particle positions. %s: %d\n", __FILE__, __LINE__);
+        return(TNG_CRITICAL);
+    }
+
+/*
+//     int64_t i, j, k;
+//     struct tng_trajectory_frame_set *frame_set =
+//     &traj->current_trajectory_frame_set;
+//     for(i = 0; i<n_frames; i++)
+//     {
+//         printf("Frame %"PRId64"\n", frame_set->first_frame + i);
+//         for(j = 0; j<n_particles; j++)
+//         {
+//             printf("Particle %"PRId64":", j);
+//             for(k=0; k<n_values_per_frame; k++)
+//             {
+//                 printf("\t%f", (values[i][j][k]).f);
+//             }
+//             printf("\n");
+//         }
+//     }
+*/
+    tng_particle_data_values_free(traj, values, n_frames, n_particles,
+                                  n_values_per_frame, type);
+
+    values = 0;
+
+    tng_particle_data_interval_get(traj, TNG_TRAJ_POSITIONS, 11000, 11499,
+                                   TNG_SKIP_HASH, &values, &n_particles,
+                                   &n_values_per_frame, &type);
+
+    /* Here the particle positions can be printed */
+
+    tng_particle_data_values_free(traj, values, 500, n_particles,
+                                  n_values_per_frame, type);
+
+    return(TNG_SUCCESS);
+}
+
+int main()
+{
+    tng_trajectory_t traj;
+    tng_function_status stat;
+    char time_str[TNG_MAX_DATE_STR_LEN];
+
+    if(tng_trajectory_init(&traj) != TNG_SUCCESS)
+    {
+        tng_trajectory_destroy(&traj);
+        printf("Test Init trajectory:\t\t\t\tFailed. %s: %d.\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    printf("Test Init trajectory:\t\t\t\tSucceeded.\n");
+
+    tng_time_get_str(traj, time_str);
+
+    printf("Creation time: %s\n", time_str);
+
+#ifdef TNG_EXAMPLE_FILES_DIR
+    tng_input_file_set(traj, TNG_EXAMPLE_FILES_DIR "tng_example.tng");
+    tng_output_file_set(traj, TNG_EXAMPLE_FILES_DIR "tng_example_out.tng");
+#else
+    tng_input_file_set(traj, "tng_example.tng");
+    tng_output_file_set(traj, "/tmp/tng_example_out.tng");
+#endif
+
+
+    if(tng_test_read_and_write_file(traj) == TNG_CRITICAL)
+    {
+        printf("Test Read and write file:\t\t\tFailed. %s: %d\n",
+               __FILE__, __LINE__);
+    }
+    else
+    {
+        printf("Test Read and write file:\t\t\tSucceeded.\n");
+    }
+
+    if(tng_test_get_box_data(traj) != TNG_SUCCESS)
+    {
+        printf("Test Get data:\t\t\t\t\tFailed. %s: %d\n",
+               __FILE__, __LINE__);
+    }
+    else
+    {
+        printf("Test Get data:\t\t\t\t\tSucceeded.\n");
+    }
+
+    if(tng_trajectory_destroy(&traj) == TNG_CRITICAL ||
+       tng_trajectory_init(&traj) == TNG_CRITICAL)
+    {
+        printf("Test Destroy and init trajectory:\t\tFailed. %s: %d\n",
+               __FILE__, __LINE__);
+    }
+    else
+    {
+        printf("Test Destroy and init trajectory:\t\tSucceeded.\n");
+    }
+
+
+#ifdef TNG_EXAMPLE_FILES_DIR
+    tng_output_file_set(traj, TNG_EXAMPLE_FILES_DIR "tng_test.tng");
+#else
+    tng_output_file_set(traj, "/tmp/tng_test.tng");
+#endif
+
+    if(tng_test_write_and_read_traj(&traj) == TNG_CRITICAL)
+    {
+        printf("Test Write and read file:\t\t\tFailed. %s: %d\n",
+               __FILE__, __LINE__);
+    }
+    else
+    {
+        printf("Test Write and read file:\t\t\tSucceeded.\n");
+    }
+
+    if(tng_test_get_positions_data(traj) != TNG_SUCCESS)
+    {
+        printf("Test Get particle data:\t\t\t\tFailed. %s: %d\n",
+               __FILE__, __LINE__);
+    }
+    else
+    {
+        printf("Test Get particle data:\t\t\t\tSucceeded.\n");
+    }
+
+    if(tng_trajectory_destroy(&traj) == TNG_CRITICAL)
+    {
+        printf("Test Destroy trajectory:\t\t\tFailed. %s: %d.\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    else
+    {
+        printf("Test Destroy trajectory:\t\t\tSucceeded.\n");
+    }
+
+
+#ifdef TNG_EXAMPLE_FILES_DIR
+    stat = tng_util_trajectory_open(TNG_EXAMPLE_FILES_DIR "tng_test.tng", 'r', &traj);
+#else
+    stat = tng_util_trajectory_open("/tmp/tng_test.tng", 'r', &traj);
+#endif
+    if(stat != TNG_SUCCESS)
+    {
+        printf("Test Utility function open:\t\t\tFailed. %s: %d.\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    else
+    {
+        printf("Test Utility function open:\t\t\tSucceeded.\n");
+    }
+
+    stat = tng_util_trajectory_close(&traj);
+    if(stat != TNG_SUCCESS)
+    {
+        printf("Test Utility function close:\t\t\tFailed. %s: %d.\n",
+               __FILE__, __LINE__);
+        exit(1);
+    }
+    else
+    {
+        printf("Test Utility function close:\t\t\tSucceeded.\n");
+    }
+
+    printf("Tests finished\n");
+
+    exit(0);
+}
diff --git a/src/external/tng_io/src/tests/tng_parallel_read.c b/src/external/tng_io/src/tests/tng_parallel_read.c
new file mode 100644 (file)
index 0000000..95d1c9a
--- /dev/null
@@ -0,0 +1,185 @@
+#ifdef TNG_BUILD_OPENMP_EXAMPLES
+
+/* This code is part of the tng binary trajectory format.
+ *
+ *                      VERSION 1.0
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../../include/tng_io.h"
+
+
+/* N.B. this code is for testing parallel reading of trajectory frame sets. The
+ * performance is not improved very much and is to a large extent limited by
+ * disk i/o. It can however be used as inspiration for writing parallel code
+ * using the TNG library. The code is NOT fully tested and may behave strangely. */
+
+int main(int argc, char **argv)
+{
+    tng_trajectory_t traj, local_traj = 0;
+    union data_values ***local_positions = 0; // A 3-dimensional array to be populated
+    union data_values **particle_pos = 0;
+    int64_t n_particles, n_values_per_frame, n_frame_sets, n_frames;
+    int64_t n_frames_per_frame_set, tot_n_frames = 0;
+    char data_type;
+    int i, j, fail;
+    int64_t particle = 0, local_first_frame, local_last_frame;
+    char atom_name[64], res_name[64];
+    tng_trajectory_frame_set_t frame_set = 0;
+
+    if(argc <= 1)
+    {
+        printf("No file specified\n");
+        printf("Usage:\n");
+        printf("tng_parallel_read <tng_file> [particle number = %"PRId64"]\n",
+               particle);
+        exit(1);
+    }
+
+    // A reference must be passed to allocate memory
+    if(tng_trajectory_init(&traj) != TNG_SUCCESS)
+    {
+        tng_trajectory_destroy(&traj);
+        exit(1);
+    }
+    tng_input_file_set(traj, argv[1]);
+
+    tng_current_frame_set_get(traj, &frame_set);
+
+    // Read the file headers
+    tng_file_headers_read(traj, TNG_USE_HASH);
+
+    if(argc >= 3)
+    {
+        particle = strtoll(argv[2], 0, 10);
+    }
+
+    tng_num_frame_sets_get(traj, &n_frame_sets);
+    tng_num_frames_per_frame_set_get(traj, &n_frames_per_frame_set);
+
+    particle_pos = malloc(sizeof(union data_values *) * n_frame_sets *
+    n_frames_per_frame_set);
+    for(i = n_frame_sets * n_frames_per_frame_set; i--;)
+    {
+        /* Assume 3 values per frame even if it's not determined yet */
+        particle_pos[i] = malloc(sizeof(union data_values) * 3);
+    }
+
+    printf("%"PRId64" frame sets\n", n_frame_sets);
+
+    if(tng_atom_name_of_particle_nr_get(traj, particle, atom_name,
+                                        sizeof(atom_name)) ==
+       TNG_SUCCESS &&
+       tng_residue_name_of_particle_nr_get(traj, particle, res_name,
+                                           sizeof(res_name)) ==
+       TNG_SUCCESS)
+    {
+        printf("Particle: %s (%s)\n", atom_name, res_name);
+    }
+    else
+    {
+        printf("Particle name not found\n");
+    }
+
+    fail = 0;
+
+#pragma omp parallel \
+private (n_frames, n_particles, n_values_per_frame, \
+         local_first_frame, local_last_frame, j, fail) \
+firstprivate (local_traj, local_positions, frame_set)\
+shared(data_type, traj, n_frame_sets, particle_pos, particle, i, tot_n_frames)\
+default(none)
+{
+    /* Each tng_trajectory_t keeps its own file pointers and i/o positions.
+     * Therefore there must be a copy for each thread. */
+    tng_trajectory_init_from_src(traj, &local_traj);
+#pragma omp for
+    for(i = 0; i < n_frame_sets; i++)
+    {
+        if(tng_frame_set_nr_find(local_traj, i) != TNG_SUCCESS)
+        {
+            printf("FAILED finding frame set %d!\n", i);
+            tot_n_frames = 0;
+            fail = 1;
+        }
+        if(tng_particle_data_get(local_traj, TNG_TRAJ_POSITIONS, &local_positions,
+                                 &n_frames, &n_particles, &n_values_per_frame,
+                                 &data_type) != TNG_SUCCESS)
+        {
+            printf("FAILED getting particle data\n");
+            tot_n_frames = 0;
+            fail = 1;
+        }
+        if(!fail)
+        {
+            tng_current_frame_set_get(local_traj, &frame_set);
+            tng_frame_set_frame_range_get(local_traj, frame_set, &local_first_frame, &local_last_frame);
+    //         printf("Frame %"PRId64"-%"PRId64":\n", local_first_frame, local_last_frame);
+    //         printf("%"PRId64" %"PRId64" %"PRId64"\n", n_frames, n_particles, n_values_per_frame);
+            tot_n_frames += n_frames;
+            for(j = 0; j < n_frames; j++)
+            {
+                particle_pos[local_first_frame + j][0] = local_positions[j][particle][0];
+                particle_pos[local_first_frame + j][1] = local_positions[j][particle][1];
+                particle_pos[local_first_frame + j][2] = local_positions[j][particle][2];
+            }
+        }
+    }
+
+    // Free memory
+    if(local_positions)
+    {
+        tng_particle_data_values_free(local_traj, local_positions, n_frames, n_particles,
+                                      n_values_per_frame, data_type);
+    }
+    tng_trajectory_destroy(&local_traj);
+}
+    switch(data_type)
+    {
+    case TNG_INT_DATA:
+        for(j = 0; j < tot_n_frames; j++)
+        {
+            printf("\t%"PRId64"\t%"PRId64"\t%"PRId64"\n", particle_pos[j][0].i,
+                   particle_pos[j][1].i, particle_pos[j][2].i);
+        }
+        break;
+    case TNG_FLOAT_DATA:
+        for(j = 0; j < tot_n_frames; j++)
+        {
+            printf("\t%f\t%f\t%f\n", particle_pos[j][0].f,
+                   particle_pos[j][1].f, particle_pos[j][2].f);
+        }
+        break;
+    case TNG_DOUBLE_DATA:
+        for(j = 0; j < tot_n_frames; j++)
+        {
+            printf("\t%f\t%f\t%f\n", particle_pos[j][0].d,
+                   particle_pos[j][1].d, particle_pos[j][2].d);
+        }
+        break;
+    default:
+        break;
+    }
+
+    /* Free more memory */
+    for(i = n_frame_sets * n_frames_per_frame_set; i--;)
+    {
+        free(particle_pos[i]);
+    }
+    free(particle_pos);
+
+    tng_trajectory_destroy(&traj);
+
+    return(0);
+}
+
+#endif
index c743bb7ab00851f1000eb5313ecc63e820000271..efd2353ac667a2c4dd64b2d31bfd7d323152742f 100644 (file)
@@ -130,6 +130,7 @@ set_source_files_properties(selection/scanner.cpp PROPERTIES COMPILE_FLAGS "${_s
 
 target_link_libraries(libgromacs ${GMX_GPU_LIBRARIES}
                       ${GMX_EXTRA_LIBRARIES}
+                      ${GMX_TNG_LIBRARIES}
                       ${FFT_LIBRARIES} ${LINEAR_ALGEBRA_LIBRARIES}
                       ${XML_LIBRARIES} ${GSL_LIBRARIES}
                       ${THREAD_LIB} ${GMX_SHARED_LINKER_FLAGS})
index 876a6397bbd6e9e98a7938b812e4e53f7a17aabe..7975f19b77f4cc7ad7d408c393f24a8728ece3ad 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2013, by the GROMACS development team, led by
+# Copyright (c) 2013,2014, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -54,6 +54,15 @@ set(FILEIO_PUBLIC_HEADERS
     )
 gmx_install_headers(fileio ${FILEIO_PUBLIC_HEADERS})
 
+# These files include tng_io.h from the TNG library. That header needs to know
+# whether inttypes.h exists or not.
+include(CheckIncludeFiles)
+check_include_file(inttypes.h   HAVE_INTTYPES_H)
+if(HAVE_INTTYPES_H)
+  set_property(SOURCE tngio.cpp APPEND PROPERTY COMPILE_DEFINITIONS USE_STD_INTTYPES_H)
+  set_property(SOURCE tngio_for_tools.cpp APPEND PROPERTY COMPILE_DEFINITIONS USE_STD_INTTYPES_H)
+endif()
+
 if (BUILD_TESTING)
-#    add_subdirectory(tests)
+     add_subdirectory(tests)
 endif (BUILD_TESTING)
index 6bdd5a2f1a701c9c0db4065eb2dca73dfe390b5f..e567502c2717009af71367d1dd1bcfa43f946d76 100644 (file)
@@ -66,7 +66,7 @@
 
 enum
 {
-    eftASC, eftBIN, eftXDR, eftGEN, eftNR
+    eftASC, eftBIN, eftXDR, eftTNG, eftGEN, eftNR
 };
 
 /* To support multiple file types with one general (eg TRX) we have
@@ -77,16 +77,25 @@ static const int trxs[] =
 #ifdef USE_XDR
     efXTC, efTRR, efCPT,
 #endif
-    efTRJ, efGRO, efG96, efPDB, efG87
+    efTRJ, efGRO, efG96, efPDB, efG87, efTNG
 };
 #define NTRXS asize(trxs)
 
+static const int trcompressed[] =
+{
+#ifdef USE_XDR
+    efXTC,
+#endif
+    efTNG
+};
+#define NTRCOMPRESSED asize(trcompressed)
+
 static const int tros[] =
 {
 #ifdef USE_XDR
     efXTC, efTRR,
 #endif
-    efTRJ, efGRO, efG96, efPDB, efG87
+    efTRJ, efGRO, efG96, efPDB, efG87, efTNG
 };
 #define NTROS asize(tros)
 
@@ -95,7 +104,7 @@ static const int trns[] =
 #ifdef USE_XDR
     efTRR, efCPT,
 #endif
-    efTRJ
+    efTRJ, efTNG
 };
 #define NTRNS asize(trns)
 
@@ -148,15 +157,19 @@ static const t_deffile
 {
     { eftASC, ".mdp", "grompp", "-f", "grompp input file with MD parameters" },
     { eftGEN, ".???", "traj", "-f",
-      "Trajectory: xtc trr trj gro g96 pdb cpt", NTRXS, trxs },
+      "Trajectory: tng xtc trr trj gro g96 pdb cpt", NTRXS, trxs },
     { eftGEN, ".???", "trajout", "-f",
-      "Trajectory: xtc trr trj gro g96 pdb", NTROS, tros },
+      "Trajectory: tng xtc trr trj gro g96 pdb", NTROS, tros },
     { eftGEN, ".???", "traj", NULL,
-      "Full precision trajectory: trr trj cpt", NTRNS, trns },
+      "Full precision trajectory: tng trr trj cpt", NTRNS, trns },
     { eftXDR, ".trr", "traj", NULL, "Trajectory in portable xdr format" },
     { eftBIN, ".trj", "traj", NULL, "Trajectory file (architecture specific)" },
+    { eftGEN, ".???", "traj_comp", NULL,
+      "Compressed trajectory (tng format or portable xdr format): tng xtc", NTRCOMPRESSED, trcompressed},
     { eftXDR, ".xtc", "traj", NULL,
-      "Compressed trajectory (portable xdr format)" },
+      "Compressed trajectory (portable xdr format): xtc" },
+    { eftTNG, ".tng", "traj", NULL,
+      "Trajectory file (tng format)" },
     { eftASC, ".g87", "gtraj", NULL, "Gromos-87 ASCII trajectory format" },
     { eftXDR, ".edr", "ener",   NULL, "Energy file"},
     { eftGEN, ".???", "conf", "-c", "Structure file: gro g96 pdb tpr etc.",
@@ -306,6 +319,8 @@ const char *ftp2ftype(int ftp)
                 return "Binary";
             case eftXDR:
                 return "XDR portable";
+            case eftTNG:
+                return "TNG";
             case eftGEN:
                 return "";
             default:
index 003e269548edace431a03f3efe845e6dea6888b0..c291b225e4ccab6e59e527768410a908e3858018 100644 (file)
@@ -48,7 +48,7 @@ extern "C" {
 /* this enum should correspond to the array deffile in gmxlib/filenm.c */
 enum {
     efMDP,
-    efTRX, efTRO, efTRN, efTRR, efTRJ, efXTC, efG87,
+    efTRX, efTRO, efTRN, efTRR, efTRJ, efCOMPRESSED, efXTC, efTNG, efG87,
     efEDR,
     efSTX, efSTO, efGRO, efG96, efPDB, efBRK, efENT, efESP, efPQR, efXYZ,
     efCPT,
index f7958c0925c5d47262fe17d87e8051df9d330624..6bc342949ca6e9079d8ac0df33d0d9441433d5f5 100644 (file)
@@ -84,11 +84,11 @@ static tMPI_Thread_mutex_t open_file_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
 
 /* These simple lists define the I/O type for these files */
 static const int ftpXDR[] =
-{ efTPR, efTRR, efEDR, efXTC, efMTX, efCPT };
+{ efTPR, efTRR, efEDR, efXTC, efTNG, efMTX, efCPT };
 static const int ftpASC[] =
 { efTPA, efGRO, efPDB };
 static const int ftpBIN[] =
-{ efTPB, efTRJ };
+{ efTPB, efTRJ, efTNG };
 #ifdef HAVE_XML
 static const int ftpXML[] =
 {   efXML};
@@ -513,6 +513,10 @@ t_fileio *gmx_fio_open(const char *fn, const char *mode)
                     gmx_open(fn);
                 }
             }
+            if (fn2ftp(fn) == efTNG)
+            {
+                gmx_incons("gmx_fio_open may not be used to open TNG files");
+            }
             /* Open the file */
             fio->fp = ffopen(fn, newmode);
 
@@ -598,6 +602,10 @@ int gmx_fio_close(t_fileio *fio)
     /* We don't want two processes operating on the list at the same time */
     tMPI_Thread_mutex_lock(&open_file_mutex);
 
+    if (fio->iFTP == efTNG)
+    {
+        gmx_incons("gmx_fio_close should not be called on a TNG file");
+    }
     gmx_fio_lock(fio);
     /* first remove it from the list */
     gmx_fio_remove(fio);
index 61f06dcaa14bf024c9c88bc6fce4522b9620dc8b..eb08027c559977db439d9b355e0d58bbd33bc698 100644 (file)
 #include "mdoutf.h"
 
 #include "gromacs/legacyheaders/xvgr.h"
-#include "trnio.h"
-#include "xtcio.h"
 #include "gromacs/legacyheaders/mdrun.h"
 #include "gromacs/legacyheaders/smalloc.h"
+#include "gromacs/legacyheaders/mvdata.h"
+#include "gromacs/legacyheaders/domdec.h"
+#include "trnio.h"
+#include "xtcio.h"
+#include "tngio.h"
+#include "trajectory_writing.h"
+#include "checkpoint.h"
+
+struct gmx_mdoutf {
+    t_fileio         *fp_trn;
+    t_fileio         *fp_xtc;
+    tng_trajectory_t  tng;
+    tng_trajectory_t  tng_low_prec;
+    int               x_compression_precision; /* only used by XTC output */
+    ener_file_t       fp_ene;
+    const char       *fn_cpt;
+    gmx_bool          bKeepAndNumCPT;
+    int               eIntegrator;
+    gmx_bool          bExpanded;
+    int               elamstats;
+    int               simulation_part;
+    FILE             *fp_dhdl;
+    FILE             *fp_field;
+    int               natoms_global;
+    int               natoms_x_compressed;
+    gmx_groups_t     *groups; /* for compressed position writing */
+};
+
 
-gmx_mdoutf_t *init_mdoutf(int nfile, const t_filenm fnm[], int mdrun_flags,
-                          const t_commrec *cr, const t_inputrec *ir,
-                          gmx_mtop_t *top_global,
-                          const output_env_t oenv)
+gmx_mdoutf_t init_mdoutf(int nfile, const t_filenm fnm[], int mdrun_flags,
+                         const t_commrec *cr, const t_inputrec *ir,
+                         gmx_mtop_t *top_global,
+                         const output_env_t oenv)
 {
-    gmx_mdoutf_t *of;
+    gmx_mdoutf_t  of;
     char          filemode[3];
     gmx_bool      bAppendFiles;
     int           i;
 
     snew(of, 1);
 
-    of->fp_trn   = NULL;
-    of->fp_ene   = NULL;
-    of->fp_xtc   = NULL;
-    of->fp_dhdl  = NULL;
-    of->fp_field = NULL;
+    of->fp_trn       = NULL;
+    of->fp_ene       = NULL;
+    of->fp_xtc       = NULL;
+    of->tng          = NULL;
+    of->tng_low_prec = NULL;
+    of->fp_dhdl      = NULL;
+    of->fp_field     = NULL;
 
     of->eIntegrator     = ir->eI;
     of->bExpanded       = ir->bExpanded;
     of->elamstats       = ir->expandedvals->elamstats;
     of->simulation_part = ir->simulation_part;
+    of->x_compression_precision = ir->x_compression_precision;
 
     if (MASTER(cr))
     {
@@ -81,13 +110,45 @@ gmx_mdoutf_t *init_mdoutf(int nfile, const t_filenm fnm[], int mdrun_flags,
 #endif
             )
         {
-            of->fp_trn = open_trn(ftp2fn(efTRN, nfile, fnm), filemode);
+            const char *filename;
+            filename = ftp2fn(efTRN, nfile, fnm);
+            switch (fn2ftp(filename))
+            {
+                case efTRR:
+                case efTRN:
+                    of->fp_trn = open_trn(filename, filemode);
+                    break;
+                case efTNG:
+                    gmx_tng_open(filename, filemode[0], &of->tng);
+                    if (filemode[0] == 'w')
+                    {
+                        gmx_tng_prepare_md_writing(of->tng, top_global, ir);
+                    }
+                    break;
+                default:
+                    gmx_incons("Invalid full precision file format");
+            }
         }
         if (EI_DYNAMICS(ir->eI) &&
-            ir->nstxtcout > 0)
+            ir->nstxout_compressed > 0)
         {
-            of->fp_xtc   = open_xtc(ftp2fn(efXTC, nfile, fnm), filemode);
-            of->xtc_prec = ir->xtcprec;
+            const char *filename;
+            filename = ftp2fn(efCOMPRESSED, nfile, fnm);
+            switch (fn2ftp(filename))
+            {
+                case efXTC:
+                    of->fp_xtc                  = open_xtc(filename, filemode);
+                    break;
+                case efTNG:
+                    gmx_tng_open(filename, filemode[0], &of->tng_low_prec);
+                    if (filemode[0] == 'w')
+                    {
+                        gmx_tng_prepare_low_prec_writing(of->tng_low_prec, top_global, ir);
+                    }
+                    break;
+                default:
+                    gmx_incons("Invalid reduced precision file format");
+            }
         }
         if (EI_DYNAMICS(ir->eI) || EI_ENERGY_MINIMIZATION(ir->eI))
         {
@@ -131,12 +192,12 @@ gmx_mdoutf_t *init_mdoutf(int nfile, const t_filenm fnm[], int mdrun_flags,
            groups, and how to look up later which ones they are. */
         of->natoms_global = top_global->natoms;
         of->groups        = &top_global->groups;
-        of->natoms_xtc    = 0;
+        of->natoms_x_compressed = 0;
         for (i = 0; (i < top_global->natoms); i++)
         {
-            if (ggrpnr(of->groups, egcXTC, i) == 0)
+            if (ggrpnr(of->groups, egcCompressedX, i) == 0)
             {
-                of->natoms_xtc++;
+                of->natoms_x_compressed++;
             }
         }
     }
@@ -144,7 +205,232 @@ gmx_mdoutf_t *init_mdoutf(int nfile, const t_filenm fnm[], int mdrun_flags,
     return of;
 }
 
-void done_mdoutf(gmx_mdoutf_t *of)
+FILE *mdoutf_get_fp_field(gmx_mdoutf_t of)
+{
+    return of->fp_field;
+}
+
+ener_file_t mdoutf_get_fp_ene(gmx_mdoutf_t of)
+{
+    return of->fp_ene;
+}
+
+FILE *mdoutf_get_fp_dhdl(gmx_mdoutf_t of)
+{
+    return of->fp_dhdl;
+}
+
+static void moveit(t_commrec *cr, rvec xx[])
+{
+    if (!xx)
+    {
+        return;
+    }
+
+    move_rvecs(cr, FALSE, FALSE, xx, NULL, (cr->nnodes-cr->npmenodes)-1, NULL);
+}
+
+void mdoutf_write_to_trajectory_files(FILE *fplog, t_commrec *cr,
+                                      gmx_mdoutf_t of,
+                                      int mdof_flags,
+                                      gmx_mtop_t *top_global,
+                                      gmx_int64_t step, double t,
+                                      t_state *state_local, t_state *state_global,
+                                      rvec *f_local, rvec *f_global)
+{
+    rvec *local_v;
+    rvec *global_v;
+
+#define MX(xvf) moveit(cr, xvf)
+
+    /* MRS -- defining these variables is to manage the difference
+     * between half step and full step velocities, but there must be a better way . . . */
+
+    local_v  = state_local->v;
+    global_v = state_global->v;
+
+    if (DOMAINDECOMP(cr))
+    {
+        if (mdof_flags & MDOF_CPT)
+        {
+            dd_collect_state(cr->dd, state_local, state_global);
+        }
+        else
+        {
+            if (mdof_flags & (MDOF_X | MDOF_X_COMPRESSED))
+            {
+                dd_collect_vec(cr->dd, state_local, state_local->x,
+                               state_global->x);
+            }
+            if (mdof_flags & MDOF_V)
+            {
+                dd_collect_vec(cr->dd, state_local, local_v,
+                               global_v);
+            }
+        }
+        if (mdof_flags & MDOF_F)
+        {
+            dd_collect_vec(cr->dd, state_local, f_local, f_global);
+        }
+    }
+    else
+    {
+        if (mdof_flags & MDOF_CPT)
+        {
+            /* All pointers in state_local are equal to state_global,
+             * but we need to copy the non-pointer entries.
+             */
+            state_global->lambda = state_local->lambda;
+            state_global->veta   = state_local->veta;
+            state_global->vol0   = state_local->vol0;
+            copy_mat(state_local->box, state_global->box);
+            copy_mat(state_local->boxv, state_global->boxv);
+            copy_mat(state_local->svir_prev, state_global->svir_prev);
+            copy_mat(state_local->fvir_prev, state_global->fvir_prev);
+            copy_mat(state_local->pres_prev, state_global->pres_prev);
+        }
+        if (cr->nnodes > 1)
+        {
+            /* Particle decomposition, collect the data on the master node */
+            if (mdof_flags & MDOF_CPT)
+            {
+                if (state_local->flags & (1<<estX))
+                {
+                    MX(state_global->x);
+                }
+                if (state_local->flags & (1<<estV))
+                {
+                    MX(state_global->v);
+                }
+                if (state_local->flags & (1<<estSDX))
+                {
+                    MX(state_global->sd_X);
+                }
+                if (state_global->nrngi > 1)
+                {
+                    if (state_local->flags & (1<<estLD_RNG))
+                    {
+#ifdef GMX_MPI
+                        MPI_Gather(state_local->ld_rng,
+                                   state_local->nrng*sizeof(state_local->ld_rng[0]), MPI_BYTE,
+                                   state_global->ld_rng,
+                                   state_local->nrng*sizeof(state_local->ld_rng[0]), MPI_BYTE,
+                                   MASTERRANK(cr), cr->mpi_comm_mygroup);
+#endif
+                    }
+                    if (state_local->flags & (1<<estLD_RNGI))
+                    {
+#ifdef GMX_MPI
+                        MPI_Gather(state_local->ld_rngi,
+                                   sizeof(state_local->ld_rngi[0]), MPI_BYTE,
+                                   state_global->ld_rngi,
+                                   sizeof(state_local->ld_rngi[0]), MPI_BYTE,
+                                   MASTERRANK(cr), cr->mpi_comm_mygroup);
+#endif
+                    }
+                }
+            }
+            else
+            {
+                if (mdof_flags & (MDOF_X | MDOF_X_COMPRESSED))
+                {
+                    MX(state_global->x);
+                }
+                if (mdof_flags & MDOF_V)
+                {
+                    MX(global_v);
+                }
+            }
+            if (mdof_flags & MDOF_F)
+            {
+                MX(f_global);
+            }
+        }
+    }
+
+    if (MASTER(cr))
+    {
+        if (mdof_flags & MDOF_CPT)
+        {
+            fflush_tng(of->tng);
+            fflush_tng(of->tng_low_prec);
+            write_checkpoint(of->fn_cpt, of->bKeepAndNumCPT,
+                             fplog, cr, of->eIntegrator, of->simulation_part,
+                             of->bExpanded, of->elamstats, step, t, state_global);
+        }
+
+        if (mdof_flags & (MDOF_X | MDOF_V | MDOF_F))
+        {
+            if (of->fp_trn)
+            {
+                fwrite_trn(of->fp_trn, step, t, state_local->lambda[efptFEP],
+                           state_local->box, top_global->natoms,
+                           (mdof_flags & MDOF_X) ? state_global->x : NULL,
+                           (mdof_flags & MDOF_V) ? global_v : NULL,
+                           (mdof_flags & MDOF_F) ? f_global : NULL);
+                if (gmx_fio_flush(of->fp_trn) != 0)
+                {
+                    gmx_file("Cannot write trajectory; maybe you are out of disk space?");
+                }
+            }
+
+            gmx_fwrite_tng(of->tng, FALSE, step, t, state_local->lambda[efptFEP],
+                           (const rvec *) state_local->box,
+                           top_global->natoms,
+                           (mdof_flags & MDOF_X) ? (const rvec *) state_global->x : NULL,
+                           (mdof_flags & MDOF_V) ? (const rvec *) global_v : NULL,
+                           (mdof_flags & MDOF_F) ? (const rvec *) f_global : NULL);
+        }
+        if (mdof_flags & MDOF_X_COMPRESSED)
+        {
+            rvec *xxtc = NULL;
+
+            if (of->natoms_x_compressed == of->natoms_global)
+            {
+                /* We are writing the positions of all of the atoms to
+                   the compressed output */
+                xxtc = state_global->x;
+            }
+            else
+            {
+                /* We are writing the positions of only a subset of
+                   the atoms to the compressed output, so we have to
+                   make a copy of the subset of coordinates. */
+                int i, j;
+
+                snew(xxtc, of->natoms_x_compressed);
+                for (i = 0, j = 0; (i < of->natoms_x_compressed); i++)
+                {
+                    if (ggrpnr(of->groups, egcCompressedX, i) == 0)
+                    {
+                        copy_rvec(state_global->x[i], xxtc[j++]);
+                    }
+                }
+            }
+            if (write_xtc(of->fp_xtc, of->natoms_x_compressed, step, t,
+                          state_local->box, xxtc, of->x_compression_precision) == 0)
+            {
+                gmx_fatal(FARGS, "XTC error - maybe you are out of disk space?");
+            }
+            gmx_fwrite_tng(of->tng_low_prec,
+                           TRUE,
+                           step,
+                           t,
+                           state_local->lambda[efptFEP],
+                           (const rvec *) state_local->box,
+                           of->natoms_x_compressed,
+                           (const rvec *) xxtc,
+                           NULL,
+                           NULL);
+            if (of->natoms_x_compressed != of->natoms_global)
+            {
+                sfree(xxtc);
+            }
+        }
+    }
+}
+
+void done_mdoutf(gmx_mdoutf_t of)
 {
     if (of->fp_ene != NULL)
     {
@@ -166,6 +452,8 @@ void done_mdoutf(gmx_mdoutf_t *of)
     {
         gmx_fio_fclose(of->fp_field);
     }
+    gmx_tng_close(&of->tng);
+    gmx_tng_close(&of->tng_low_prec);
 
     sfree(of);
 }
index 3efba386bc8d9fefbfe31de99eb3c2cf773aa686..cb6b812720d86ecbe8db99ca2d7c8597e7f2eb92 100644 (file)
 #ifndef GMX_FILEIO_MDOUTF_H
 #define GMX_FILEIO_MDOUTF_H
 
-#include "filenm.h"
 #include <stdio.h>
 #include "../legacyheaders/types/simple.h"
+#include "../legacyheaders/types/topology.h"
+#include "../legacyheaders/types/inputrec.h"
+#include "../legacyheaders/types/oenv.h"
+#include "../legacyheaders/network.h"
+#include "filenm.h"
 #include "enxio.h"
-#include "gmxfio.h"
 
-typedef struct {
-    t_fileio     *fp_trn;
-    t_fileio     *fp_xtc;
-    int           xtc_prec;
-    ener_file_t   fp_ene;
-    const char   *fn_cpt;
-    gmx_bool      bKeepAndNumCPT;
-    int           eIntegrator;
-    gmx_bool      bExpanded;
-    int           elamstats;
-    int           simulation_part;
-    FILE         *fp_dhdl;
-    FILE         *fp_field;
-    int           natoms_global;
-    int           natoms_xtc;
-    gmx_groups_t *groups; /* for XTC writing */
-} gmx_mdoutf_t;
+typedef struct gmx_mdoutf *gmx_mdoutf_t;
 
-gmx_mdoutf_t *init_mdoutf(int nfile, const t_filenm fnm[],
-                          int mdrun_flags,
-                          const t_commrec *cr, const t_inputrec *ir,
-                          gmx_mtop_t *top_global,
-                          const output_env_t oenv);
-/* Returns a pointer to a data structure with all output file pointers
+/*! \brief Allocate and initialize object to manager trajectory writing output
+ *
+ * Returns a pointer to a data structure with all output file pointers
  * and names required by mdrun.
  */
+gmx_mdoutf_t init_mdoutf(int                nfile,
+                         const t_filenm     fnm[],
+                         int                mdrun_flags,
+                         const t_commrec   *cr,
+                         const t_inputrec  *ir,
+                         gmx_mtop_t        *mtop,
+                         const output_env_t oenv);
+
+/*! \brief Getter for file pointer */
+FILE *mdoutf_get_fp_field(gmx_mdoutf_t of);
+
+/*! \brief Getter for file pointer */
+ener_file_t mdoutf_get_fp_ene(gmx_mdoutf_t of);
 
-void done_mdoutf(gmx_mdoutf_t *of);
-/* Close all open output files and free the of pointer */
+/*! \brief Getter for file pointer */
+FILE *mdoutf_get_fp_dhdl(gmx_mdoutf_t of);
+
+/*! \brief Close all open output files and free the of pointer */
+void done_mdoutf(gmx_mdoutf_t of);
+
+/*! \brief Routine that writes trajectory-like frames.
+ *
+ * Writes data to trn, xtc and/or checkpoint. What is written is
+ * determined by the mdof_flags defined below. Data is collected to
+ * the master node only when necessary.
+ */
+void mdoutf_write_to_trajectory_files(FILE *fplog, t_commrec *cr,
+                                      gmx_mdoutf_t of,
+                                      int mdof_flags,
+                                      gmx_mtop_t *top_global,
+                                      gmx_int64_t step, double t,
+                                      t_state *state_local, t_state *state_global,
+                                      rvec *f_local, rvec *f_global);
 
-#define MDOF_X   (1<<0)
-#define MDOF_V   (1<<1)
-#define MDOF_F   (1<<2)
-#define MDOF_XTC (1<<3)
-#define MDOF_CPT (1<<4)
+#define MDOF_X            (1<<0)
+#define MDOF_V            (1<<1)
+#define MDOF_F            (1<<2)
+#define MDOF_X_COMPRESSED (1<<3)
+#define MDOF_CPT          (1<<4)
 
 #endif /* GMX_FILEIO_MDOUTF_H */
diff --git a/src/gromacs/fileio/tests/CMakeLists.txt b/src/gromacs/fileio/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b0f6273
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2013,2014, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+if(GMX_USE_TNG)
+    gmx_add_unit_test(FileIOTests fileio-test
+        tngio.cpp)
+endif()
diff --git a/src/gromacs/fileio/tests/spc2-traj.tng b/src/gromacs/fileio/tests/spc2-traj.tng
new file mode 100644 (file)
index 0000000..9f7f22f
Binary files /dev/null and b/src/gromacs/fileio/tests/spc2-traj.tng differ
diff --git a/src/gromacs/fileio/tests/tngio.cpp b/src/gromacs/fileio/tests/tngio.cpp
new file mode 100644 (file)
index 0000000..a7ced2f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for file I/O routines
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_fileio
+ */
+#include <gtest/gtest.h>
+#include <string>
+
+#include "../tngio.h"
+#include "../tngio_for_tools.h"
+#include "testutils/testfilemanager.h"
+#include "gromacs/utility/path.h"
+
+namespace
+{
+
+class TngTest : public ::testing::Test
+{
+    public:
+        TngTest()
+        {
+        }
+        gmx::test::TestFileManager      fileManager_;
+};
+
+TEST_F(TngTest, CanOpenTngFile)
+{
+    tng_trajectory_t tng;
+    gmx_tng_open(fileManager_.getInputFilePath("spc2-traj.tng").c_str(),
+                 'r',
+                 &tng);
+    gmx_tng_close(&tng);
+}
+
+TEST_F(TngTest, CloseBeforeOpenIsNotFatal)
+{
+    tng_trajectory_t tng = NULL;
+    gmx_tng_close(&tng);
+}
+
+} // namespace
diff --git a/src/gromacs/fileio/tngio.cpp b/src/gromacs/fileio/tngio.cpp
new file mode 100644 (file)
index 0000000..44f6875
--- /dev/null
@@ -0,0 +1,828 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "tngio.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef GMX_USE_TNG
+#include "../../external/tng_io/include/tng_io.h"
+#endif
+
+#include "gromacs/legacyheaders/copyrite.h"
+#include "gromacs/legacyheaders/gmx_fatal.h"
+#include "gromacs/legacyheaders/main.h"
+#include "gromacs/legacyheaders/physics.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/programinfo.h"
+#include "gromacs/math/utilities.h"
+#include "gmxfio.h"
+
+static const char *modeToVerb(char mode)
+{
+    switch (mode)
+    {
+        case 'r':
+            return "reading";
+            break;
+        case 'w':
+            return "writing";
+            break;
+        case 'a':
+            return "appending";
+            break;
+        default:
+            gmx_fatal(FARGS, "Invalid file opening mode %c", mode);
+            return "";
+    }
+}
+
+void gmx_tng_open(const char       *filename,
+                  char              mode,
+                  tng_trajectory_t *tng)
+{
+#ifdef GMX_USE_TNG
+    /* First check whether we have to make a backup,
+     * only for writing, not for read or append.
+     */
+    if (mode == 'w')
+    {
+#ifndef GMX_FAHCORE
+        /* only make backups for normal gromacs */
+        make_backup(filename);
+#endif
+    }
+
+    /* tng must not be pointing at already allocated memory.
+     * Memory will be allocated by tng_util_trajectory_open() and must
+     * later on be freed by tng_util_trajectory_close(). */
+    if (TNG_SUCCESS != tng_util_trajectory_open(filename, mode, tng))
+    {
+        /* TNG does return more than one degree of error, but there is
+           no use case for GROMACS handling the non-fatal errors
+           gracefully. */
+        gmx_fatal(FARGS,
+                  "%s while opening %s for %s",
+                  gmx_strerror("file"),
+                  filename,
+                  modeToVerb(mode));
+    }
+
+    if (mode == 'w' || mode == 'a')
+    {
+        /* FIXME in TNG: When adding data to the header, subsequent blocks might get
+         * overwritten. This could be solved by moving the first trajectory
+         * frame set(s) to the end of the file. Could that cause other problems,
+         * e.g. when continuing a simulation? */
+        char hostname[256];
+        gmx_gethostname(hostname, 256);
+        if (mode == 'w')
+        {
+            tng_first_computer_name_set(*tng, hostname);
+        }
+/* TODO: This should be implemented when the above fixme is done (adding data to
+ * the header). */
+//         else
+//         {
+//             tng_last_computer_name_set(*tng, hostname);
+//         }
+
+        char        programInfo[256];
+        const char *precisionString = "";
+#ifdef GMX_DOUBLE
+        precisionString = " (double precision)";
+#endif
+        sprintf(programInfo, "%.100s, %.128s%.24s", gmx::ProgramInfo::getInstance().displayName().c_str(),
+                GromacsVersion(), precisionString);
+        if (mode == 'w')
+        {
+            tng_first_program_name_set(*tng, programInfo);
+        }
+/* TODO: This should be implemented when the above fixme is done (adding data to
+ * the header). */
+//         else
+//         {
+//             tng_last_program_name_set(*tng, programInfo);
+//         }
+
+#ifdef HAVE_UNISTD_H
+        char username[256];
+        getlogin_r(username, 256);
+        if (mode == 'w')
+        {
+            tng_first_user_name_set(*tng, username);
+        }
+/* TODO: This should be implemented when the above fixme is done (adding data to
+ * the header). */
+//         else
+//         {
+//             tng_last_user_name_set(*tng, username);
+//         }
+#endif
+    }
+#else
+    gmx_file("GROMACS was compiled without TNG support, cannot handle this file type");
+    GMX_UNUSED_VALUE(filename);
+    GMX_UNUSED_VALUE(mode);
+    GMX_UNUSED_VALUE(tng);
+#endif
+}
+
+void gmx_tng_close(tng_trajectory_t *tng)
+{
+    /* We have to check that tng is set because
+     * tng_util_trajectory_close wants to return a NULL in it, and
+     * gives a fatal error if it is NULL. */
+#ifdef GMX_USE_TNG
+    if (tng)
+    {
+        tng_util_trajectory_close(tng);
+    }
+#else
+    GMX_UNUSED_VALUE(tng);
+#endif
+}
+
+#ifdef GMX_USE_TNG
+static void addTngMoleculeFromTopology(tng_trajectory_t     tng,
+                                       const char          *moleculeName,
+                                       const t_atoms       *atoms,
+                                       gmx_int64_t          numMolecules,
+                                       tng_molecule_t      *tngMol)
+{
+    if (tng_molecule_add(tng, moleculeName, tngMol) != TNG_SUCCESS)
+    {
+        gmx_file("Cannot add molecule to TNG molecular system.");
+    }
+
+    /* FIXME: The TNG atoms should contain mass and atomB info (for free
+     * energy calculations), i.e. in when it's available in TNG (2.0). */
+    for (int atomIt = 0; atomIt < atoms->nr; atomIt++)
+    {
+        const t_atom *at = &atoms->atom[atomIt];
+        /* FIXME: Currently the TNG API can only add atoms belonging to a
+         * residue and chain. Wait for TNG 2.0*/
+        if (atoms->nres > 0)
+        {
+            const t_resinfo *resInfo        = &atoms->resinfo[at->resind];
+            char             chainName[2]   = {resInfo->chainid, 0};
+            tng_chain_t      tngChain       = NULL;
+            tng_residue_t    tngRes         = NULL;
+            tng_atom_t       tngAtom        = NULL;
+
+            if (tng_molecule_chain_find (tng, *tngMol, chainName,
+                                         (gmx_int64_t)-1, &tngChain) !=
+                TNG_SUCCESS)
+            {
+                tng_molecule_chain_add (tng, *tngMol, chainName,
+                                        &tngChain);
+            }
+
+            /* FIXME: When TNG supports both residue index and residue
+             * number the latter should be used. Wait for TNG 2.0*/
+            if (tng_chain_residue_find(tng, tngChain, *resInfo->name,
+                                       at->resind + 1, &tngRes)
+                != TNG_SUCCESS)
+            {
+                tng_chain_residue_add(tng, tngChain, *resInfo->name, &tngRes);
+            }
+            tng_residue_atom_add(tng, tngRes, *(atoms->atomname[atomIt]), *(atoms->atomtype[atomIt]), &tngAtom);
+        }
+    }
+    tng_molecule_cnt_set(tng, *tngMol, numMolecules);
+}
+
+void gmx_tng_add_mtop(tng_trajectory_t  tng,
+                      const gmx_mtop_t *mtop)
+{
+    int                  i, j;
+    const t_ilist       *ilist;
+    tng_bond_t           tngBond;
+
+    if (!mtop)
+    {
+        /* No topology information available to add. */
+        return;
+    }
+
+    for (int molIt = 0; molIt < mtop->nmolblock; molIt++)
+    {
+        tng_molecule_t       tngMol  = NULL;
+        const gmx_moltype_t *molType =
+            &mtop->moltype[mtop->molblock[molIt].type];
+
+        /* Add a molecule to the TNG trajectory with the same name as the
+         * current molecule. */
+        addTngMoleculeFromTopology(tng,
+                                   *(molType->name),
+                                   &molType->atoms,
+                                   mtop->molblock[molIt].nmol,
+                                   &tngMol);
+
+        /* Bonds have to be deduced from interactions (constraints etc). Different
+         * interactions have different sets of parameters. */
+        /* Constraints are specified using two atoms */
+        for (i = 0; i < F_NRE; i++)
+        {
+            if (IS_CHEMBOND(i))
+            {
+                ilist = &molType->ilist[i];
+                if (ilist)
+                {
+                    j = 1;
+                    while (j < ilist->nr)
+                    {
+                        tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
+                        j += 3;
+                    }
+                }
+            }
+        }
+        /* Settle is described using three atoms */
+        ilist = &molType->ilist[F_SETTLE];
+        if (ilist)
+        {
+            j = 1;
+            while (j < ilist->nr)
+            {
+                tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
+                tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+2], &tngBond);
+                j += 4;
+            }
+        }
+    }
+}
+
+/*! \libinternal \brief Compute greatest common divisor of n1 and n2
+ * if they are positive.
+ *
+ * If only one of n1 and n2 is positive, then return it.
+ * If neither n1 or n2 is positive, then return -1. */
+static int
+greatest_common_divisor_if_positive(int n1, int n2)
+{
+    if (0 >= n1)
+    {
+        return (0 >= n2) ? -1 : n2;
+    }
+    if (0 >= n2)
+    {
+        return n1;
+    }
+
+    /* We have a non-trivial greatest common divisor to compute. */
+    return gmx_greatest_common_divisor(n1, n2);
+}
+
+/* By default try to write 100 frames (of actual output) in each frame set.
+ * This number is the number of outputs of the most frequently written data
+ * type per frame set.
+ * TODO for 5.1: Verify that 100 frames per frame set is efficient for most
+ * setups regarding compression efficiency and compression time. Make this
+ * a hidden command-line option? */
+const int defaultFramesPerFrameSet = 100;
+
+/*! \libinternal \brief  Set the number of frames per frame
+ * set according to output intervals.
+ * The default is that 100 frames are written of the data
+ * that is written most often. */
+static void tng_set_frames_per_frame_set(tng_trajectory_t  tng,
+                                         const gmx_bool    bUseLossyCompression,
+                                         const t_inputrec *ir)
+{
+    int     gcd = -1;
+
+    /* Set the number of frames per frame set to contain at least
+     * defaultFramesPerFrameSet of the lowest common denominator of
+     * the writing interval of positions and velocities. */
+    /* FIXME after 5.0: consider nstenergy also? */
+    if (bUseLossyCompression)
+    {
+        gcd = ir->nstxout_compressed;
+    }
+    else
+    {
+        gcd = greatest_common_divisor_if_positive(ir->nstxout, ir->nstvout);
+        gcd = greatest_common_divisor_if_positive(gcd, ir->nstfout);
+    }
+    if (0 >= gcd)
+    {
+        return;
+    }
+
+    tng_num_frames_per_frame_set_set(tng, gcd * defaultFramesPerFrameSet);
+}
+
+/*! \libinternal \brief Set the data-writing intervals, and number of
+ * frames per frame set */
+static void set_writing_intervals(tng_trajectory_t  tng,
+                                  const gmx_bool    bUseLossyCompression,
+                                  const t_inputrec *ir)
+{
+    /* Define pointers to specific writing functions depending on if we
+     * write float or double data */
+    typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
+                                                                     const gmx_int64_t,
+                                                                     const gmx_int64_t,
+                                                                     const gmx_int64_t,
+                                                                     const char*,
+                                                                     const char,
+                                                                     const char);
+#ifdef GMX_DOUBLE
+    set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
+#else
+    set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
+#endif
+    int  xout, vout, fout;
+//     int  gcd = -1, lowest = -1;
+    char compression;
+
+    tng_set_frames_per_frame_set(tng, bUseLossyCompression, ir);
+
+    if (bUseLossyCompression)
+    {
+        xout        = ir->nstxout_compressed;
+        vout        = 0;
+        fout        = 0;
+        compression = TNG_TNG_COMPRESSION;
+    }
+    else
+    {
+        xout        = ir->nstxout;
+        vout        = ir->nstvout;
+        fout        = ir->nstfout;
+        compression = TNG_GZIP_COMPRESSION;
+    }
+    if (xout)
+    {
+        set_writing_interval(tng, xout, 3, TNG_TRAJ_POSITIONS,
+                             "POSITIONS", TNG_PARTICLE_BLOCK_DATA,
+                             compression);
+        /* The design of TNG makes it awkward to try to write a box
+         * with multiple periodicities, which might be co-prime. Since
+         * the use cases for the box with a frame consisting only of
+         * velocities seem low, for now we associate box writing with
+         * position writing. */
+        set_writing_interval(tng, xout, 9, TNG_TRAJ_BOX_SHAPE,
+                             "BOX SHAPE", TNG_NON_PARTICLE_BLOCK_DATA,
+                             TNG_GZIP_COMPRESSION);
+        /* TODO: if/when we write energies to TNG also, reconsider how
+         * and when box information is written, because GROMACS
+         * behaviour pre-5.0 was to write the box with every
+         * trajectory frame and every energy frame, and probably
+         * people depend on this. */
+
+        /* TODO: If we need to write lambda values at steps when
+         * positions (or other data) are not also being written, then
+         * code in mdoutf.c will need to match however that is
+         * organized here. */
+        set_writing_interval(tng, xout, 1, TNG_GMX_LAMBDA,
+                             "LAMBDAS", TNG_NON_PARTICLE_BLOCK_DATA,
+                             TNG_GZIP_COMPRESSION);
+
+        /* FIXME: gcd and lowest currently not used. */
+//         gcd = greatest_common_divisor_if_positive(gcd, xout);
+//         if (lowest < 0 || xout < lowest)
+//         {
+//             lowest = xout;
+//         }
+    }
+    if (vout)
+    {
+        set_writing_interval(tng, ir->nstvout, 3, TNG_TRAJ_VELOCITIES,
+                             "VELOCITIES", TNG_PARTICLE_BLOCK_DATA,
+                             compression);
+
+        /* FIXME: gcd and lowest currently not used. */
+//         gcd = greatest_common_divisor_if_positive(gcd, vout);
+//         if (lowest < 0 || vout < lowest)
+//         {
+//             lowest = vout;
+//         }
+    }
+    if (fout)
+    {
+        set_writing_interval(tng, ir->nstfout, 3, TNG_TRAJ_FORCES,
+                             "FORCES", TNG_PARTICLE_BLOCK_DATA,
+                             TNG_GZIP_COMPRESSION);
+
+        /* FIXME: gcd and lowest currently not used. */
+//         gcd = greatest_common_divisor_if_positive(gcd, fout);
+//         if (lowest < 0 || fout < lowest)
+//         {
+//             lowest = fout;
+//         }
+    }
+    /* FIXME: See above. gcd interval for lambdas is disabled. */
+//     if (gcd > 0)
+//     {
+//         /* Lambdas written at an interval of the lowest common denominator
+//          * of other output */
+//         set_writing_interval(tng, gcd, 1, TNG_GMX_LAMBDA,
+//                                  "LAMBDAS", TNG_NON_PARTICLE_BLOCK_DATA,
+//                                  TNG_GZIP_COMPRESSION);
+//
+//         if (gcd < lowest / 10)
+//         {
+//             gmx_warning("The lowest common denominator of trajectory output is "
+//                         "every %d step(s), whereas the shortest output interval "
+//                         "is every %d steps.", gcd, lowest);
+//         }
+//     }
+}
+#endif
+
+void gmx_tng_prepare_md_writing(tng_trajectory_t  tng,
+                                const gmx_mtop_t *mtop,
+                                const t_inputrec *ir)
+{
+#ifdef GMX_USE_TNG
+    gmx_tng_add_mtop(tng, mtop);
+    set_writing_intervals(tng, FALSE, ir);
+    tng_time_per_frame_set(tng, ir->delta_t * PICO);
+#else
+    GMX_UNUSED_VALUE(tng);
+    GMX_UNUSED_VALUE(mtop);
+    GMX_UNUSED_VALUE(ir);
+#endif
+}
+
+#ifdef GMX_USE_TNG
+/* Create a TNG molecule representing the selection groups
+ * to write */
+static void add_selection_groups(tng_trajectory_t  tng,
+                                 const gmx_mtop_t *mtop)
+{
+    const gmx_moltype_t     *molType;
+    const t_atoms           *atoms;
+    const t_atom            *at;
+    const t_resinfo         *resInfo;
+    const t_ilist           *ilist;
+    int                      nAtoms      = 0, i = 0, j, molIt, atomIt, nameIndex;
+    int                      atom_offset = 0;
+    tng_molecule_t           mol, iterMol;
+    tng_chain_t              chain;
+    tng_residue_t            res;
+    tng_atom_t               atom;
+    tng_bond_t               tngBond;
+    gmx_int64_t              nMols;
+    char                    *groupName;
+
+    /* The name of the TNG molecule containing the selection group is the
+     * same as the name of the selection group. */
+    nameIndex = *mtop->groups.grps[egcCompressedX].nm_ind;
+    groupName = *mtop->groups.grpname[nameIndex];
+
+    tng_molecule_alloc(tng, &mol);
+    tng_molecule_name_set(tng, mol, groupName);
+    tng_molecule_chain_add(tng, mol, "", &chain);
+    for (molIt = 0; molIt < mtop->nmoltype; molIt++)
+    {
+        molType = &mtop->moltype[mtop->molblock[molIt].type];
+
+        atoms = &molType->atoms;
+
+        for (j = 0; j < mtop->molblock[molIt].nmol; j++)
+        {
+            bool bAtomsAdded = FALSE;
+            for (atomIt = 0; atomIt < atoms->nr; atomIt++, i++)
+            {
+                char *res_name;
+                int   res_id;
+
+                if (ggrpnr(&mtop->groups, egcCompressedX, i) != 0)
+                {
+                    continue;
+                }
+                at = &atoms->atom[atomIt];
+                if (atoms->nres > 0)
+                {
+                    resInfo = &atoms->resinfo[at->resind];
+                    /* FIXME: When TNG supports both residue index and residue
+                     * number the latter should be used. */
+                    res_name = *resInfo->name;
+                    res_id   = at->resind + 1;
+                }
+                else
+                {
+                    res_name = (char *)"";
+                    res_id   = 0;
+                }
+                if (tng_chain_residue_find(tng, chain, res_name, res_id, &res)
+                    != TNG_SUCCESS)
+                {
+                    /* Since there is ONE chain for selection groups do not keep the
+                     * original residue IDs - otherwise there might be conflicts. */
+                    tng_chain_residue_add(tng, chain, res_name, &res);
+                }
+                tng_residue_atom_w_id_add(tng, res, *(atoms->atomname[atomIt]),
+                                          *(atoms->atomtype[atomIt]),
+                                          atom_offset + atomIt, &atom);
+                nAtoms++;
+                bAtomsAdded = TRUE;
+            }
+            /* Add bonds. */
+            if (bAtomsAdded)
+            {
+                for (int k = 0; k < F_NRE; k++)
+                {
+                    if (IS_CHEMBOND(k))
+                    {
+                        ilist = &molType->ilist[k];
+                        if (ilist)
+                        {
+                            int l = 1;
+                            while (l < ilist->nr)
+                            {
+                                int atom1, atom2;
+                                atom1 = ilist->iatoms[l] + atom_offset;
+                                atom2 = ilist->iatoms[l+1] + atom_offset;
+                                if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0 &&
+                                    ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
+                                {
+                                    tng_molecule_bond_add(tng, mol, ilist->iatoms[l],
+                                                          ilist->iatoms[l+1], &tngBond);
+                                }
+                                l += 3;
+                            }
+                        }
+                    }
+                }
+                /* Settle is described using three atoms */
+                ilist = &molType->ilist[F_SETTLE];
+                if (ilist)
+                {
+                    int l = 1;
+                    while (l < ilist->nr)
+                    {
+                        int atom1, atom2, atom3;
+                        atom1 = ilist->iatoms[l] + atom_offset;
+                        atom2 = ilist->iatoms[l+1] + atom_offset;
+                        atom3 = ilist->iatoms[l+2] + atom_offset;
+                        if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0)
+                        {
+                            if (ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
+                            {
+                                tng_molecule_bond_add(tng, mol, atom1,
+                                                      atom2, &tngBond);
+                            }
+                            if (ggrpnr(&mtop->groups, egcCompressedX, atom3) == 0)
+                            {
+                                tng_molecule_bond_add(tng, mol, atom1,
+                                                      atom3, &tngBond);
+                            }
+                        }
+                        l += 4;
+                    }
+                }
+            }
+            atom_offset += atoms->nr;
+        }
+    }
+    if (nAtoms != i)
+    {
+        tng_molecule_existing_add(tng, &mol);
+        tng_molecule_cnt_set(tng, mol, 1);
+        tng_num_molecule_types_get(tng, &nMols);
+        for (gmx_int64_t k = 0; k < nMols; k++)
+        {
+            tng_molecule_of_index_get(tng, k, &iterMol);
+            if (iterMol == mol)
+            {
+                continue;
+            }
+            tng_molecule_cnt_set(tng, iterMol, 0);
+        }
+    }
+    else
+    {
+        tng_molecule_free(tng, &mol);
+    }
+}
+#endif
+
+void gmx_tng_set_compression_precision(tng_trajectory_t tng,
+                                       real             prec)
+{
+#ifdef GMX_USE_TNG
+    tng_compression_precision_set(tng, 1.0/prec);
+#else
+    GMX_UNUSED_VALUE(tng);
+    GMX_UNUSED_VALUE(prec);
+#endif
+}
+
+void gmx_tng_prepare_low_prec_writing(tng_trajectory_t  tng,
+                                      const gmx_mtop_t *mtop,
+                                      const t_inputrec *ir)
+{
+#ifdef GMX_USE_TNG
+    gmx_tng_add_mtop(tng, mtop);
+    add_selection_groups(tng, mtop);
+    set_writing_intervals(tng, TRUE, ir);
+    tng_time_per_frame_set(tng, ir->delta_t * PICO);
+    gmx_tng_set_compression_precision(tng, ir->x_compression_precision);
+#else
+    GMX_UNUSED_VALUE(tng);
+    GMX_UNUSED_VALUE(mtop);
+    GMX_UNUSED_VALUE(ir);
+#endif
+}
+
+void gmx_fwrite_tng(tng_trajectory_t tng,
+                    const gmx_bool   bUseLossyCompression,
+                    int              step,
+                    real             elapsedPicoSeconds,
+                    real             lambda,
+                    const rvec      *box,
+                    int              nAtoms,
+                    const rvec      *x,
+                    const rvec      *v,
+                    const rvec      *f)
+{
+#ifdef GMX_USE_TNG
+    typedef tng_function_status (*write_data_func_pointer)(tng_trajectory_t,
+                                                           const gmx_int64_t,
+                                                           const double,
+                                                           const real*,
+                                                           const gmx_int64_t,
+                                                           const gmx_int64_t,
+                                                           const char*,
+                                                           const char,
+                                                           const char);
+#ifdef GMX_DOUBLE
+    static write_data_func_pointer           write_data           = tng_util_generic_with_time_double_write;
+#else
+    static write_data_func_pointer           write_data           = tng_util_generic_with_time_write;
+#endif
+    double                  elapsedSeconds = elapsedPicoSeconds * PICO;
+    gmx_int64_t             nParticles;
+    char                    compression;
+
+
+    if (!tng)
+    {
+        /* This function might get called when the type of the
+           compressed trajectory is actually XTC. So we exit and move
+           on. */
+        return;
+    }
+
+    tng_num_particles_get(tng, &nParticles);
+    if (nAtoms != (int)nParticles)
+    {
+        tng_implicit_num_particles_set(tng, nAtoms);
+    }
+
+    if (bUseLossyCompression)
+    {
+        compression = TNG_TNG_COMPRESSION;
+    }
+    else
+    {
+        compression = TNG_GZIP_COMPRESSION;
+    }
+
+    /* The writing is done using write_data, which writes float or double
+     * depending on the GROMACS compilation. */
+    if (x)
+    {
+        GMX_ASSERT(box, "Need a non-NULL box if positions are written");
+
+        if (write_data(tng, step, elapsedSeconds,
+                       reinterpret_cast<const real *>(x),
+                       3, TNG_TRAJ_POSITIONS, "POSITIONS",
+                       TNG_PARTICLE_BLOCK_DATA,
+                       compression) != TNG_SUCCESS)
+        {
+            gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
+        }
+        /* TNG-MF1 compression only compresses positions and velocities. Use lossless
+         * compression for box shape regardless of output mode */
+        if (write_data(tng, step, elapsedSeconds,
+                       reinterpret_cast<const real *>(box),
+                       9, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
+                       TNG_NON_PARTICLE_BLOCK_DATA,
+                       TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
+        {
+            gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
+        }
+    }
+
+    if (v)
+    {
+        if (write_data(tng, step, elapsedSeconds,
+                       reinterpret_cast<const real *>(v),
+                       3, TNG_TRAJ_VELOCITIES, "VELOCITIES",
+                       TNG_PARTICLE_BLOCK_DATA,
+                       compression) != TNG_SUCCESS)
+        {
+            gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
+        }
+    }
+
+    if (f)
+    {
+        /* TNG-MF1 compression only compresses positions and velocities. Use lossless
+         * compression for forces regardless of output mode */
+        if (write_data(tng, step, elapsedSeconds,
+                       reinterpret_cast<const real *>(f),
+                       3, TNG_TRAJ_FORCES, "FORCES",
+                       TNG_PARTICLE_BLOCK_DATA,
+                       TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
+        {
+            gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
+        }
+    }
+
+    /* TNG-MF1 compression only compresses positions and velocities. Use lossless
+     * compression for lambdas regardless of output mode */
+    if (write_data(tng, step, elapsedSeconds,
+                   reinterpret_cast<const real *>(&lambda),
+                   1, TNG_GMX_LAMBDA, "LAMBDAS",
+                   TNG_NON_PARTICLE_BLOCK_DATA,
+                   TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
+    {
+        gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
+    }
+#else
+    GMX_UNUSED_VALUE(tng);
+    GMX_UNUSED_VALUE(bUseLossyCompression);
+    GMX_UNUSED_VALUE(step);
+    GMX_UNUSED_VALUE(elapsedPicoSeconds);
+    GMX_UNUSED_VALUE(lambda);
+    GMX_UNUSED_VALUE(box);
+    GMX_UNUSED_VALUE(nAtoms);
+    GMX_UNUSED_VALUE(x);
+    GMX_UNUSED_VALUE(v);
+    GMX_UNUSED_VALUE(f);
+#endif
+}
+
+void fflush_tng(tng_trajectory_t tng)
+{
+#ifdef GMX_USE_TNG
+    if (!tng)
+    {
+        return;
+    }
+    tng_frame_set_premature_write(tng, TNG_USE_HASH);
+#else
+    GMX_UNUSED_VALUE(tng);
+#endif
+}
+
+float gmx_tng_get_time_of_final_frame(tng_trajectory_t tng)
+{
+#ifdef GMX_USE_TNG
+    gmx_int64_t nFrames;
+    double      time;
+    float       fTime;
+
+    tng_num_frames_get(tng, &nFrames);
+    tng_util_time_of_frame_get(tng, nFrames - 1, &time);
+
+    fTime = time / PICO;
+    return fTime;
+#else
+    GMX_UNUSED_VALUE(tng);
+    return -1.0;
+#endif
+}
diff --git a/src/gromacs/fileio/tngio.h b/src/gromacs/fileio/tngio.h
new file mode 100644 (file)
index 0000000..e899a23
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_TNGIO_H
+#define GMX_FILEIO_TNGIO_H
+
+#include "gromacs/legacyheaders/typedefs.h"
+#include "../../external/tng_io/include/tng_io_fwd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+}
+#endif
+
+/*! \brief Open a TNG trajectory file
+ *
+ * \param filename   Name of file to open
+ * \param mode       Can be set to 'r', 'w' or 'a' for reading, writing or appending respectively.
+ * \param tng_data_p Pointer to an allocated tng_trajectory_t into which a handle to a TNG trajectory will be stored.
+ *
+ * Handles all I/O errors internally via fatal error
+ */
+void gmx_tng_open(const char       *filename,
+                  char              mode,
+                  tng_trajectory_t *tng_data_p);
+
+/*! \brief Finish writing a TNG trajectory file */
+void gmx_tng_close(tng_trajectory_t *tng);
+
+/*!\brief Add molecular topology information to TNG output (if
+ * available)
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param mtop  Pointer to a topology (can be NULL)
+ */
+void gmx_tng_add_mtop(tng_trajectory_t  tng,
+                      const gmx_mtop_t *mtop);
+
+/*! \brief Do all TNG preparation for full-precision whole-system
+ * trajectory writing during MD simulations.
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param mtop  Global topology
+ * \param ir    Input settings (for writing frequencies)
+ */
+void gmx_tng_prepare_md_writing(tng_trajectory_t  tng,
+                                const gmx_mtop_t *mtop,
+                                const t_inputrec *ir);
+
+/*! \brief Set the default compression precision for TNG writing
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param prec  GROMACS-style precision setting (i.e. 1000 for 3 digits of precision) */
+void gmx_tng_set_compression_precision(tng_trajectory_t tng,
+                                       real             prec);
+
+/*! \brief Do all TNG preparation for low-precision selection-based
+ * trajectory writing during MD simulations.
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param mtop  Global topology
+ * \param ir    Input settings (for writing frequencies)
+ */
+void gmx_tng_prepare_low_prec_writing(tng_trajectory_t  tng,
+                                      const gmx_mtop_t *mtop,
+                                      const t_inputrec *ir);
+
+/*! \brief Write a frame to a TNG file
+ *
+ * \param tng                  Valid handle to a TNG trajectory
+ * \param bUseLossyCompression Whether to use lossy compression
+ * \param step                 MD step number
+ * \param elapsedPicoSeconds   Elapsed MD time
+ * \param lambda               Free-energy lambda value
+ * \param box                  Simulation box
+ * \param nAtoms               Number of atoms (i.e. vector lengths)
+ * \param x                    Vector of position coordinates
+ * \param v                    Vector of elocities
+ * \param f                    Vector of forces
+ *
+ * The pointers tng, x, v, f may be NULL, which triggers not writing
+ * (that component). box can only be NULL if x is also NULL. */
+void gmx_fwrite_tng(tng_trajectory_t tng,
+                    const gmx_bool   bUseLossyCompression,
+                    int              step,
+                    real             elapsedPicoSeconds,
+                    real             lambda,
+                    const rvec      *box,
+                    int              nAtoms,
+                    const rvec      *x,
+                    const rvec      *v,
+                    const rvec      *f);
+
+/*! \brief Write the current frame set to disk. Perform compression
+ * etc.
+ *
+ * \param tng Valid handle to a TNG trajectory
+ */
+void fflush_tng(tng_trajectory_t tng);
+
+/*! \brief Get the time (in picoseconds) of the final frame in the
+ * trajectory.
+ *
+ * \param tng Valid handle to a TNG trajectory
+ */
+float gmx_tng_get_time_of_final_frame(tng_trajectory_t tng);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GMX_FILEIO_TNGIO_H */
diff --git a/src/gromacs/fileio/tngio_for_tools.cpp b/src/gromacs/fileio/tngio_for_tools.cpp
new file mode 100644 (file)
index 0000000..fc00718
--- /dev/null
@@ -0,0 +1,925 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "tngio_for_tools.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <vector>
+
+#include "tngio.h"
+#include "trx.h"
+
+#ifdef GMX_USE_TNG
+#include "../../external/tng_io/include/tng_io.h"
+#endif
+
+#include "gromacs/utility/common.h"
+#include "gromacs/legacyheaders/types/atoms.h"
+#include "gromacs/legacyheaders/smalloc.h"
+#include "gromacs/legacyheaders/physics.h"
+#include "gromacs/legacyheaders/gmx_fatal.h"
+#include "gromacs/tools/dump.h"
+
+void gmx_prepare_tng_writing(const char              *filename,
+                             char                     mode,
+                             tng_trajectory_t        *input,
+                             tng_trajectory_t        *output,
+                             int                      nAtoms,
+                             const gmx_mtop_t        *mtop,
+                             const atom_id           *index,
+                             const char              *indexGroupName)
+{
+#ifdef GMX_USE_TNG
+    /* FIXME after 5.0: Currently only standard block types are read */
+    const int           defaultNumIds              = 5;
+    static gmx_int64_t  fallbackIds[defaultNumIds] =
+    {
+        TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
+        TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
+        TNG_GMX_LAMBDA
+    };
+    static char         fallbackNames[defaultNumIds][32] =
+    {
+        "BOX SHAPE", "POSITIONS", "VELOCITIES",
+        "FORCES", "LAMBDAS"
+    };
+
+
+    gmx_tng_open(filename, mode, output);
+
+    /* Do we have an input file in TNG format? If so, then there's
+       more data we can copy over, rather than having to improvise. */
+    if (*input)
+    {
+        /* Set parameters (compression, time per frame, molecule
+         * information, number of frames per frame set and writing
+         * intervals of positions, box shape and lambdas) of the
+         * output tng container based on their respective values int
+         * the input tng container */
+        double      time, compression_precision;
+        gmx_int64_t n_frames_per_frame_set, interval = -1;
+
+        tng_compression_precision_get(*input, &compression_precision);
+        tng_compression_precision_set(*output, compression_precision);
+        // TODO make this configurable in a future version
+        char compression_type = TNG_TNG_COMPRESSION;
+
+        tng_molecule_system_copy(*input, *output);
+
+        tng_time_per_frame_get(*input, &time);
+        tng_time_per_frame_set(*output, time);
+
+        tng_num_frames_per_frame_set_get(*input, &n_frames_per_frame_set);
+        tng_num_frames_per_frame_set_set(*output, n_frames_per_frame_set);
+
+        for (int i = 0; i < defaultNumIds; i++)
+        {
+            if (tng_data_get_stride_length(*input, fallbackIds[i], -1, &interval)
+                == TNG_SUCCESS)
+            {
+                switch (fallbackIds[i])
+                {
+                    case TNG_TRAJ_POSITIONS:
+                    case TNG_TRAJ_VELOCITIES:
+                        tng_util_generic_write_interval_set(*output, interval, 3, fallbackIds[i],
+                                                            fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
+                                                            compression_type);
+                        break;
+                    case TNG_TRAJ_FORCES:
+                        tng_util_generic_write_interval_set(*output, interval, 3, fallbackIds[i],
+                                                            fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
+                                                            TNG_GZIP_COMPRESSION);
+                        break;
+                    case TNG_TRAJ_BOX_SHAPE:
+                        tng_util_generic_write_interval_set(*output, interval, 9, fallbackIds[i],
+                                                            fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
+                                                            TNG_GZIP_COMPRESSION);
+                        break;
+                    case TNG_GMX_LAMBDA:
+                        tng_util_generic_write_interval_set(*output, interval, 1, fallbackIds[i],
+                                                            fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
+                                                            TNG_GZIP_COMPRESSION);
+                    default:
+                        continue;
+                }
+            }
+        }
+
+    }
+    else
+    {
+        /* TODO after trjconv is modularized: fix this so the user can
+           change precision when they are doing an operation where
+           this makes sense, and not otherwise.
+
+           char compression = bUseLossyCompression ? TNG_TNG_COMPRESSION : TNG_GZIP_COMPRESSION;
+           gmx_tng_set_compression_precision(*output, ndec2prec(nDecimalsOfPrecision));
+         */
+        gmx_tng_add_mtop(*output, mtop);
+        tng_num_frames_per_frame_set_set(*output, 1);
+    }
+
+    if (index && nAtoms > 0)
+    {
+        gmx_tng_setup_atom_subgroup(*output, nAtoms, index, indexGroupName);
+    }
+
+    /* If for some reason there are more requested atoms than there are atoms in the
+     * molecular system create a number of implicit atoms (without atom data) to
+     * compensate for that. */
+    if (nAtoms >= 0)
+    {
+        tng_implicit_num_particles_set(*output, nAtoms);
+    }
+#else
+    GMX_UNUSED_VALUE(filename);
+    GMX_UNUSED_VALUE(mode);
+    GMX_UNUSED_VALUE(input);
+    GMX_UNUSED_VALUE(output);
+    GMX_UNUSED_VALUE(nAtoms);
+#endif
+}
+
+void gmx_write_tng_from_trxframe(tng_trajectory_t        output,
+                                 t_trxframe             *frame,
+                                 int                     natoms)
+{
+#ifdef GMX_USE_TNG
+    if (frame->step > 0)
+    {
+        double timePerFrame = frame->time * PICO / frame->step;
+        tng_time_per_frame_set(output, timePerFrame);
+    }
+    if (natoms < 0)
+    {
+        natoms = frame->natoms;
+    }
+    gmx_fwrite_tng(output,
+                   TRUE,
+                   frame->step,
+                   frame->time,
+                   0,
+                   (const rvec *) frame->box,
+                   natoms,
+                   (const rvec *) frame->x,
+                   (const rvec *) frame->v,
+                   (const rvec *) frame->f);
+#else
+    GMX_UNUSED_VALUE(output);
+    GMX_UNUSED_VALUE(frame);
+#endif
+}
+
+#ifdef GMX_USE_TNG
+static void
+convert_array_to_real_array(void       *from,
+                            real       *to,
+                            const float fact,
+                            const int   nAtoms,
+                            const int   nValues,
+                            const char  datatype)
+{
+    int i, j;
+
+    switch (datatype)
+    {
+        case TNG_FLOAT_DATA:
+            if (sizeof(real) == sizeof(float))
+            {
+                if (fact == 1)
+                {
+                    memcpy(to, from, nValues * sizeof(real) * nAtoms);
+                }
+                else
+                {
+                    for (i = 0; i < nAtoms; i++)
+                    {
+                        for (j = 0; j < nValues; j++)
+                        {
+                            to[i*nValues+j] = (real)((float *)from)[i*nValues+j] * fact;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (i = 0; i < nAtoms; i++)
+                {
+                    for (j = 0; j < nValues; j++)
+                    {
+                        to[i*nValues+j] = (real)((float *)from)[i*nValues+j] * fact;
+                    }
+                }
+            }
+            break;
+        case TNG_INT_DATA:
+            for (i = 0; i < nAtoms; i++)
+            {
+                for (j = 0; j < nValues; j++)
+                {
+                    to[i*nValues+j] = (real)((gmx_int64_t *)from)[i*nValues+j] * fact;
+                }
+            }
+            break;
+        case TNG_DOUBLE_DATA:
+            if (sizeof(real) == sizeof(double))
+            {
+                if (fact == 1)
+                {
+                    memcpy(to, from, nValues * sizeof(real) * nAtoms);
+                }
+                else
+                {
+                    for (i = 0; i < nAtoms; i++)
+                    {
+                        for (j = 0; j < nValues; j++)
+                        {
+                            to[i*nValues+j] = (real)((double *)from)[i*nValues+j] * fact;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (i = 0; i < nAtoms; i++)
+                {
+                    for (j = 0; j < nValues; j++)
+                    {
+                        to[i*nValues+j] = (real)((double *)from)[i*nValues+j] * fact;
+                    }
+                }
+            }
+            break;
+        default:
+            gmx_incons("Illegal datatype when converting values to a real array!");
+            return;
+    }
+    return;
+}
+
+static real getDistanceScaleFactor(tng_trajectory_t in)
+{
+    gmx_int64_t exp = -1;
+    real        distanceScaleFactor;
+
+    // TODO Hopefully, TNG 2.0 will do this kind of thing for us
+    tng_distance_unit_exponential_get(in, &exp);
+
+    // GROMACS expects distances in nm
+    switch (exp)
+    {
+        case 9:
+            distanceScaleFactor = NANO/NANO;
+            break;
+        case 10:
+            distanceScaleFactor = NANO/ANGSTROM;
+            break;
+        default:
+            distanceScaleFactor = pow(10.0, exp + 9.0);
+    }
+
+    return distanceScaleFactor;
+}
+#endif
+
+void gmx_tng_setup_atom_subgroup(tng_trajectory_t tng,
+                                 const int        nind,
+                                 const atom_id   *ind,
+                                 const char      *name)
+{
+#ifdef GMX_USE_TNG
+    gmx_int64_t              nAtoms, cnt, nMols;
+    tng_molecule_t           mol, iterMol;
+    tng_chain_t              chain;
+    tng_residue_t            res;
+    tng_atom_t               atom;
+    tng_function_status      stat;
+
+    tng_num_particles_get(tng, &nAtoms);
+
+    if (nAtoms == nind)
+    {
+        return;
+    }
+
+    stat = tng_molecule_find(tng, name, -1, &mol);
+    if (stat == TNG_SUCCESS)
+    {
+        tng_molecule_num_atoms_get(tng, mol, &nAtoms);
+        tng_molecule_cnt_get(tng, mol, &cnt);
+        if (nAtoms == nind)
+        {
+            stat = TNG_SUCCESS;
+        }
+        else
+        {
+            stat = TNG_FAILURE;
+        }
+    }
+    if (stat == TNG_FAILURE)
+    {
+        /* The indexed atoms are added to one separate molecule. */
+        tng_molecule_alloc(tng, &mol);
+        tng_molecule_name_set(tng, mol, name);
+        tng_molecule_chain_add(tng, mol, "", &chain);
+
+        for (int i = 0; i < nind; i++)
+        {
+            char        temp_name[256], temp_type[256];
+
+            /* Try to retrieve the residue name of the atom */
+            stat = tng_residue_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
+            if (stat != TNG_SUCCESS)
+            {
+                temp_name[0] = '\0';
+            }
+            /* Check if the molecule of the selection already contains this residue */
+            if (tng_chain_residue_find(tng, chain, temp_name, -1, &res)
+                != TNG_SUCCESS)
+            {
+                tng_chain_residue_add(tng, chain, temp_name, &res);
+            }
+            /* Try to find the original name and type of the atom */
+            stat = tng_atom_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
+            if (stat != TNG_SUCCESS)
+            {
+                temp_name[0] = '\0';
+            }
+            stat = tng_atom_type_of_particle_nr_get(tng, ind[i], temp_type, 256);
+            if (stat != TNG_SUCCESS)
+            {
+                temp_type[0] = '\0';
+            }
+            tng_residue_atom_w_id_add(tng, res, temp_name, temp_type, ind[i], &atom);
+        }
+        tng_molecule_existing_add(tng, &mol);
+    }
+    /* Set the count of the molecule containing the selected atoms to 1 and all
+     * other molecules to 0 */
+    tng_molecule_cnt_set(tng, mol, 1);
+    tng_num_molecule_types_get(tng, &nMols);
+    for (gmx_int64_t k = 0; k < nMols; k++)
+    {
+        tng_molecule_of_index_get(tng, k, &iterMol);
+        if (iterMol == mol)
+        {
+            continue;
+        }
+        tng_molecule_cnt_set(tng, iterMol, 0);
+    }
+#else
+    GMX_UNUSED_VALUE(tng);
+    GMX_UNUSED_VALUE(nind);
+    GMX_UNUSED_VALUE(ind);
+    GMX_UNUSED_VALUE(name);
+#endif
+}
+
+/* TODO: If/when TNG acquires the ability to copy data blocks without
+ * uncompressing them, then this implemenation should be reconsidered.
+ * Ideally, gmx trjconv -f a.tng -o b.tng -b 10 -e 20 would be fast
+ * and lose no information. */
+gmx_bool gmx_read_next_tng_frame(tng_trajectory_t            input,
+                                 t_trxframe                 *fr,
+                                 gmx_int64_t                *requestedIds,
+                                 int                         numRequestedIds)
+{
+#ifdef GMX_USE_TNG
+    gmx_bool                bOK = TRUE;
+    tng_function_status     stat;
+    gmx_int64_t             numberOfAtoms = -1, frameNumber = -1;
+    gmx_int64_t             nBlocks, blockId, *blockIds = NULL;
+    char                    datatype      = -1, codecId;
+    void                   *values        = NULL;
+    double                  frameTime     = -1.0;
+    int                     size, blockDependency;
+    float                   prec;
+    const int               defaultNumIds = 5;
+    static gmx_int64_t      fallbackRequestedIds[defaultNumIds] =
+    {
+        TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
+        TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
+        TNG_GMX_LAMBDA
+    };
+
+
+    fr->bStep     = FALSE;
+    fr->bTime     = FALSE;
+    fr->bLambda   = FALSE;
+    fr->bAtoms    = FALSE;
+    fr->bPrec     = FALSE;
+    fr->bX        = FALSE;
+    fr->bV        = FALSE;
+    fr->bF        = FALSE;
+    fr->bBox      = FALSE;
+
+    /* If no specific IDs were requested read all block types that can
+     * currently be interpreted */
+    if (!requestedIds || numRequestedIds == 0)
+    {
+        numRequestedIds = defaultNumIds;
+        requestedIds    = fallbackRequestedIds;
+    }
+
+    stat = tng_num_particles_get(input, &numberOfAtoms);
+    if (stat != TNG_SUCCESS)
+    {
+        gmx_file("Cannot determine number of atoms from TNG file.");
+    }
+    fr->natoms = numberOfAtoms;
+
+    if (!gmx_get_tng_data_block_types_of_next_frame(input,
+                                                    fr->step,
+                                                    numRequestedIds,
+                                                    requestedIds,
+                                                    &frameNumber,
+                                                    &nBlocks,
+                                                    &blockIds))
+    {
+        return FALSE;
+    }
+
+    if (nBlocks == 0)
+    {
+        return FALSE;
+    }
+
+    for (gmx_int64_t i = 0; i < nBlocks; i++)
+    {
+        blockId = blockIds[i];
+        tng_data_block_dependency_get(input, blockId, &blockDependency);
+        if (blockDependency & TNG_PARTICLE_DEPENDENT)
+        {
+            stat = tng_util_particle_data_next_frame_read(input,
+                                                          blockId,
+                                                          &values,
+                                                          &datatype,
+                                                          &frameNumber,
+                                                          &frameTime);
+        }
+        else
+        {
+            stat = tng_util_non_particle_data_next_frame_read(input,
+                                                              blockId,
+                                                              &values,
+                                                              &datatype,
+                                                              &frameNumber,
+                                                              &frameTime);
+        }
+        if (stat == TNG_CRITICAL)
+        {
+            gmx_file("Cannot read positions from TNG file.");
+            return FALSE;
+        }
+        else if (stat == TNG_FAILURE)
+        {
+            continue;
+        }
+        switch (blockId)
+        {
+            case TNG_TRAJ_BOX_SHAPE:
+                switch (datatype)
+                {
+                    case TNG_INT_DATA:
+                        size = sizeof(gmx_int64_t);
+                        break;
+                    case TNG_FLOAT_DATA:
+                        size = sizeof(float);
+                        break;
+                    case TNG_DOUBLE_DATA:
+                        size = sizeof(double);
+                        break;
+                    default:
+                        size = 0; /* Just to make the compiler happy. */
+                        gmx_incons("Illegal datatype of box shape values!");
+                }
+                for (int i = 0; i < DIM; i++)
+                {
+                    convert_array_to_real_array((char *)(values) + size * i * DIM,
+                                                    (real *) fr->box[i],
+                                                    getDistanceScaleFactor(input),
+                                                    1,
+                                                    DIM,
+                                                    datatype);
+                }
+                fr->bBox = TRUE;
+                break;
+            case TNG_TRAJ_POSITIONS:
+                srenew(fr->x, fr->natoms);
+                convert_array_to_real_array(values,
+                                                (real *) fr->x,
+                                                getDistanceScaleFactor(input),
+                                                fr->natoms,
+                                                DIM,
+                                                datatype);
+                fr->bX = TRUE;
+                tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
+                /* This must be updated if/when more lossy compression methods are added */
+                if (codecId == TNG_TNG_COMPRESSION)
+                {
+                    fr->prec  = prec;
+                    fr->bPrec = TRUE;
+                }
+                break;
+            case TNG_TRAJ_VELOCITIES:
+                srenew(fr->v, fr->natoms);
+                convert_array_to_real_array(values,
+                                                (real *) fr->v,
+                                                getDistanceScaleFactor(input),
+                                                fr->natoms,
+                                                DIM,
+                                                datatype);
+                fr->bV = TRUE;
+                tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
+                /* This must be updated if/when more lossy compression methods are added */
+                if (codecId == TNG_TNG_COMPRESSION)
+                {
+                    fr->prec  = prec;
+                    fr->bPrec = TRUE;
+                }
+                break;
+            case TNG_TRAJ_FORCES:
+                srenew(fr->f, fr->natoms);
+                convert_array_to_real_array(values,
+                                                (real *) fr->f,
+                                                getDistanceScaleFactor(input),
+                                                fr->natoms,
+                                                DIM,
+                                                datatype);
+                fr->bF = TRUE;
+                break;
+            case TNG_GMX_LAMBDA:
+                switch (datatype)
+                {
+                    case TNG_FLOAT_DATA:
+                        fr->lambda = (*(float *)values);
+                        break;
+                    case TNG_DOUBLE_DATA:
+                        fr->lambda = (*(double *)values);
+                        break;
+                    default:
+                        gmx_incons("Illegal datatype lambda value!");
+                }
+                fr->bLambda = TRUE;
+                break;
+            default:
+                gmx_warning("Illegal block type! Currently GROMACS tools can only handle certain data types. Skipping block.");
+        }
+        /* values does not have to be freed before reading next frame. It will
+         * be reallocated if it is not NULL. */
+    }
+
+    fr->step  = (int) frameNumber;
+    fr->bStep = TRUE;
+    // Convert the time to ps
+    fr->time  = frameTime / PICO;
+    fr->bTime = TRUE;
+
+    /* values must be freed before leaving this function */
+    sfree(values);
+
+    return bOK;
+#else
+    GMX_UNUSED_VALUE(input);
+    GMX_UNUSED_VALUE(fr);
+    GMX_UNUSED_VALUE(requestedIds);
+    return FALSE;
+#endif
+}
+
+void gmx_print_tng_molecule_system(tng_trajectory_t input,
+                                   FILE            *stream)
+{
+#ifdef GMX_USE_TNG
+    gmx_int64_t        nMolecules, nChains, nResidues, nAtoms, *molCntList;
+    tng_molecule_t     molecule;
+    tng_chain_t        chain;
+    tng_residue_t      residue;
+    tng_atom_t         atom;
+    char               str[256], varNAtoms;
+
+    tng_num_molecule_types_get(input, &nMolecules);
+    tng_molecule_cnt_list_get(input, &molCntList);
+    /* Can the number of particles change in the trajectory or is it constant? */
+    tng_num_particles_variable_get(input, &varNAtoms);
+
+    for (gmx_int64_t i = 0; i < nMolecules; i++)
+    {
+        tng_molecule_of_index_get(input, i, &molecule);
+        tng_molecule_name_get(input, molecule, str, 256);
+        if (varNAtoms == TNG_CONSTANT_N_ATOMS)
+        {
+            if ((int)molCntList[i] == 0)
+            {
+                continue;
+            }
+            fprintf(stream, "Molecule: %s, count: %d\n", str, (int)molCntList[i]);
+        }
+        else
+        {
+            fprintf(stream, "Molecule: %s\n", str);
+        }
+        tng_molecule_num_chains_get(input, molecule, &nChains);
+        if (nChains > 0)
+        {
+            for (gmx_int64_t j = 0; j < nChains; j++)
+            {
+                tng_molecule_chain_of_index_get(input, molecule, j, &chain);
+                tng_chain_name_get(input, chain, str, 256);
+                fprintf(stream, "\tChain: %s\n", str);
+                tng_chain_num_residues_get(input, chain, &nResidues);
+                for (gmx_int64_t k = 0; k < nResidues; k++)
+                {
+                    tng_chain_residue_of_index_get(input, chain, k, &residue);
+                    tng_residue_name_get(input, residue, str, 256);
+                    fprintf(stream, "\t\tResidue: %s\n", str);
+                    tng_residue_num_atoms_get(input, residue, &nAtoms);
+                    for (gmx_int64_t l = 0; l < nAtoms; l++)
+                    {
+                        tng_residue_atom_of_index_get(input, residue, l, &atom);
+                        tng_atom_name_get(input, atom, str, 256);
+                        fprintf(stream, "\t\t\tAtom: %s", str);
+                        tng_atom_type_get(input, atom, str, 256);
+                        fprintf(stream, " (%s)\n", str);
+                    }
+                }
+            }
+        }
+        /* It is possible to have a molecule without chains, in which case
+         * residues in the molecule can be iterated through without going
+         * through chains. */
+        else
+        {
+            tng_molecule_num_residues_get(input, molecule, &nResidues);
+            if (nResidues > 0)
+            {
+                for (gmx_int64_t k = 0; k < nResidues; k++)
+                {
+                    tng_molecule_residue_of_index_get(input, molecule, k, &residue);
+                    tng_residue_name_get(input, residue, str, 256);
+                    fprintf(stream, "\t\tResidue: %s\n", str);
+                    tng_residue_num_atoms_get(input, residue, &nAtoms);
+                    for (gmx_int64_t l = 0; l < nAtoms; l++)
+                    {
+                        tng_residue_atom_of_index_get(input, residue, l, &atom);
+                        tng_atom_name_get(input, atom, str, 256);
+                        fprintf(stream, "\t\t\tAtom: %s", str);
+                        tng_atom_type_get(input, atom, str, 256);
+                        fprintf(stream, " (%s)\n", str);
+                    }
+                }
+            }
+            else
+            {
+                tng_molecule_num_atoms_get(input, molecule, &nAtoms);
+                for (gmx_int64_t l = 0; l < nAtoms; l++)
+                {
+                    tng_molecule_atom_of_index_get(input, molecule, l, &atom);
+                    tng_atom_name_get(input, atom, str, 256);
+                    fprintf(stream, "\t\t\tAtom: %s", str);
+                    tng_atom_type_get(input, atom, str, 256);
+                    fprintf(stream, " (%s)\n", str);
+                }
+            }
+        }
+    }
+#else
+    GMX_UNUSED_VALUE(input);
+    GMX_UNUSED_VALUE(stream);
+#endif
+}
+
+gmx_bool gmx_get_tng_data_block_types_of_next_frame(tng_trajectory_t     input,
+                                                    int                  frame,
+                                                    int                  nRequestedIds,
+                                                    gmx_int64_t         *requestedIds,
+                                                    gmx_int64_t         *nextFrame,
+                                                    gmx_int64_t         *nBlocks,
+                                                    gmx_int64_t        **blockIds)
+{
+#ifdef GMX_USE_TNG
+    tng_function_status stat;
+
+    stat = tng_util_trajectory_next_frame_present_data_blocks_find(input, frame,
+                                                                   nRequestedIds, requestedIds,
+                                                                   nextFrame,
+                                                                   nBlocks, blockIds);
+
+    if (stat == TNG_CRITICAL)
+    {
+        gmx_file("Cannot read TNG file. Cannot find data blocks of next frame.");
+    }
+    else if (stat == TNG_FAILURE)
+    {
+        return FALSE;
+    }
+    return TRUE;
+#else
+    GMX_UNUSED_VALUE(input);
+    GMX_UNUSED_VALUE(frame);
+    GMX_UNUSED_VALUE(nRequestedIds);
+    GMX_UNUSED_VALUE(requestedIds);
+    GMX_UNUSED_VALUE(nextFrame);
+    GMX_UNUSED_VALUE(nBlocks);
+    GMX_UNUSED_VALUE(blockIds);
+    return FALSE;
+#endif
+}
+
+gmx_bool gmx_get_tng_data_next_frame_of_block_type(tng_trajectory_t     input,
+                                                   gmx_int64_t          blockId,
+                                                   real               **values,
+                                                   gmx_int64_t         *frameNumber,
+                                                   double              *frameTime,
+                                                   gmx_int64_t         *nValuesPerFrame,
+                                                   gmx_int64_t         *nAtoms,
+                                                   real                *prec,
+                                                   char                *name,
+                                                   int                  maxLen,
+                                                   gmx_bool            *bOK)
+{
+#ifdef GMX_USE_TNG
+    tng_function_status stat;
+    char                datatype = -1, codecId;
+    int                 blockDependency;
+    void               *data = 0;
+    float               localPrec;
+
+    stat = tng_data_block_name_get(input, blockId, name, maxLen);
+    if (stat != TNG_SUCCESS)
+    {
+        gmx_file("Cannot read next frame of TNG file");
+    }
+    stat = tng_data_block_dependency_get(input, blockId, &blockDependency);
+    if (stat != TNG_SUCCESS)
+    {
+        gmx_file("Cannot read next frame of TNG file");
+    }
+    if (blockDependency & TNG_PARTICLE_DEPENDENT)
+    {
+        tng_num_particles_get(input, nAtoms);
+        stat = tng_util_particle_data_next_frame_read(input,
+                                                      blockId,
+                                                      &data,
+                                                      &datatype,
+                                                      frameNumber,
+                                                      frameTime);
+    }
+    else
+    {
+        *nAtoms = 1; /* There are not actually any atoms, but it is used for
+                        allocating memory */
+        stat    = tng_util_non_particle_data_next_frame_read(input,
+                                                             blockId,
+                                                             &data,
+                                                             &datatype,
+                                                             frameNumber,
+                                                             frameTime);
+    }
+    if (stat == TNG_CRITICAL)
+    {
+        gmx_file("Cannot read next frame of TNG file");
+    }
+    if (stat == TNG_FAILURE)
+    {
+        *bOK = TRUE;
+        return FALSE;
+    }
+
+    stat = tng_data_block_num_values_per_frame_get(input, blockId, nValuesPerFrame);
+    if (stat != TNG_SUCCESS)
+    {
+        gmx_file("Cannot read next frame of TNG file");
+    }
+    snew(*values, sizeof(real) * *nValuesPerFrame * *nAtoms);
+    convert_array_to_real_array(data,
+                                    *values,
+                                    getDistanceScaleFactor(input),
+                                    *nAtoms,
+                                    *nValuesPerFrame,
+                                    datatype);
+
+    tng_util_frame_current_compression_get(input, blockId, &codecId, &localPrec);
+
+    /* This must be updated if/when more lossy compression methods are added */
+    if (codecId != TNG_TNG_COMPRESSION)
+    {
+        *prec = -1.0;
+    }
+    else
+    {
+        *prec = localPrec;
+    }
+
+    *bOK = TRUE;
+    return TRUE;
+#else
+    GMX_UNUSED_VALUE(input);
+    GMX_UNUSED_VALUE(blockId);
+    GMX_UNUSED_VALUE(values);
+    GMX_UNUSED_VALUE(frameNumber);
+    GMX_UNUSED_VALUE(frameTime);
+    GMX_UNUSED_VALUE(nValuesPerFrame);
+    GMX_UNUSED_VALUE(nAtoms);
+    GMX_UNUSED_VALUE(prec);
+    GMX_UNUSED_VALUE(name);
+    GMX_UNUSED_VALUE(maxLen);
+    GMX_UNUSED_VALUE(bOK);
+    return FALSE;
+#endif
+}
+
+void list_tng_for_gmx_dump(const char *fn, gmx_bool bXVG)
+{
+#ifdef GMX_USE_TNG
+    tng_trajectory_t     tng;
+    gmx_int64_t          nframe = 0;
+    gmx_int64_t          i, *block_ids = NULL, step, ndatablocks;
+    gmx_int64_t          pos_block_id = TNG_TRAJ_POSITIONS;
+    gmx_bool             bOK;
+
+    gmx_tng_open(fn, 'r', &tng);
+    gmx_print_tng_molecule_system(tng, stdout);
+
+    bOK    = gmx_get_tng_data_block_types_of_next_frame(tng, -1,
+                                                        bXVG ? 1 : 0,
+                                                        bXVG ? &pos_block_id : NULL,
+                                                        &step, &ndatablocks,
+                                                        &block_ids);
+    do
+    {
+        for (i = 0; i < ndatablocks; i++)
+        {
+            double               frame_time;
+            real                 prec, *values = NULL;
+            gmx_int64_t          n_values_per_frame, n_atoms;
+            char                 block_name[STRLEN];
+
+            gmx_get_tng_data_next_frame_of_block_type(tng, block_ids[i], &values,
+                                                      &step, &frame_time,
+                                                      &n_values_per_frame, &n_atoms,
+                                                      &prec,
+                                                      block_name, STRLEN, &bOK);
+            if (!bOK)
+            {
+                /* Can't write any output because we don't know what
+                   arrays are valid. */
+                fprintf(stderr, "\nWARNING: Incomplete frame at time %g, will not write output\n", frame_time);
+                list_tng_inner(fn, (0 == i), bXVG, values, step, frame_time,
+                               n_values_per_frame, n_atoms, prec, nframe, block_name);
+            }
+        }
+        nframe++;
+    }
+    while (gmx_get_tng_data_block_types_of_next_frame(tng, step,
+                                                      bXVG ? 1 : 0,
+                                                      bXVG ? &pos_block_id : NULL,
+                                                      &step,
+                                                      &ndatablocks,
+                                                      &block_ids));
+
+    if (block_ids)
+    {
+        sfree(block_ids);
+    }
+
+    gmx_tng_close(&tng);
+#else
+    GMX_UNUSED_VALUE(fn);
+    GMX_UNUSED_VALUE(bXVG);
+#endif
+}
diff --git a/src/gromacs/fileio/tngio_for_tools.h b/src/gromacs/fileio/tngio_for_tools.h
new file mode 100644 (file)
index 0000000..c8a3099
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_TNGIO_FOR_TOOLS_H
+#define GMX_FILEIO_TNGIO_FOR_TOOLS_H
+
+#include "gromacs/legacyheaders/typedefs.h"
+#include "../../external/tng_io/include/tng_io_fwd.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+}
+#endif
+
+/*! \brief Prepare to write TNG output from trajectory conversion tools */
+void gmx_prepare_tng_writing(const char              *filename,
+                             char                     mode,
+                             tng_trajectory_t        *in,
+                             tng_trajectory_t        *out,
+                             int                      nAtoms,
+                             const gmx_mtop_t        *mtop,
+                             const atom_id           *index,
+                             const char              *indexGroupName);
+
+/*! \brief Write a trxframe to a TNG file
+ *
+ * \param natoms Number of atoms to actually write
+ *
+ * The natoms field in frame is the number of atoms in the system. The
+ * parameter natoms supports writing an index-group subset of the
+ * atoms.
+ */
+void gmx_write_tng_from_trxframe(tng_trajectory_t        output,
+                                 t_trxframe             *frame,
+                                 int                     natoms);
+
+/*! \brief Creates a molecule containing only the indexed atoms and sets
+ * the number of all other molecules to 0. Works similar to a
+ * selection group. */
+void gmx_tng_setup_atom_subgroup(tng_trajectory_t tng,
+                                 const int        nind,
+                                 const atom_id   *ind,
+                                 const char      *name);
+
+/*! \brief Read the first/next TNG frame. */
+gmx_bool gmx_read_next_tng_frame(tng_trajectory_t            input,
+                                 t_trxframe                 *fr,
+                                 gmx_int64_t                *requestedIds,
+                                 int                         numRequestedIds);
+
+/*! \brief Print the molecule system to stream */
+void gmx_print_tng_molecule_system(tng_trajectory_t input,
+                                   FILE            *stream);
+
+/*! \brief Get a list of block IDs present in the next frame with data. */
+gmx_bool gmx_get_tng_data_block_types_of_next_frame(tng_trajectory_t     input,
+                                                    int                  frame,
+                                                    int                  nRequestedIds,
+                                                    gmx_int64_t         *requestedIds,
+                                                    gmx_int64_t         *nextFrame,
+                                                    gmx_int64_t         *nBlocks,
+                                                    gmx_int64_t        **blockIds);
+
+/*! \brief Get data of the next frame with data from the data block
+ * with the specified block ID. */
+gmx_bool gmx_get_tng_data_next_frame_of_block_type(tng_trajectory_t     input,
+                                                   gmx_int64_t          blockId,
+                                                   real               **values,
+                                                   gmx_int64_t         *frameNumber,
+                                                   double              *frameTime,
+                                                   gmx_int64_t         *nValuesPerFrame,
+                                                   gmx_int64_t         *nAtoms,
+                                                   real                *prec,
+                                                   char                *name,
+                                                   int                  maxLen,
+                                                   gmx_bool            *bOK);
+
+/*! \brief Implements TNG file reading for gmxdump. */
+void list_tng_for_gmx_dump(const char *fn,
+                           gmx_bool bXVG);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 95b649c0816bd2ef2d49ab042752ab80b28b0b61..02414e9b9f04a469e319eda9897bfe33be9565ea 100644 (file)
@@ -877,7 +877,7 @@ static void do_inputrec(t_fileio *fio, t_inputrec *ir, gmx_bool bRead,
     gmx_fio_do_int(fio, ir->nstvout);
     gmx_fio_do_int(fio, ir->nstfout);
     gmx_fio_do_int(fio, ir->nstenergy);
-    gmx_fio_do_int(fio, ir->nstxtcout);
+    gmx_fio_do_int(fio, ir->nstxout_compressed);
     if (file_version >= 59)
     {
         gmx_fio_do_double(fio, ir->init_t);
@@ -890,7 +890,7 @@ static void do_inputrec(t_fileio *fio, t_inputrec *ir, gmx_bool bRead,
         gmx_fio_do_real(fio, rdum);
         ir->delta_t = rdum;
     }
-    gmx_fio_do_real(fio, ir->xtcprec);
+    gmx_fio_do_real(fio, ir->x_compression_precision);
     if (file_version < 19)
     {
         gmx_fio_do_int(fio, idum);
index df0cf844c5db1edbc1537d307fbf8aa0f035581f..813c5b80369f2de45ee2f62d60787b583d9d51e7 100644 (file)
 #include "gromacs/timing/wallcycle.h"
 
 void
-do_trajectory_writing(FILE           *fplog,
-                      t_commrec      *cr,
-                      int             nfile,
-                      const t_filenm  fnm[],
-                      gmx_int64_t     step,
-                      gmx_int64_t     step_rel,
-                      double          t,
-                      t_inputrec     *ir,
-                      t_state        *state,
-                      t_state        *state_global,
-                      gmx_mtop_t     *top_global,
-                      t_forcerec     *fr,
-                      gmx_update_t    upd,
-                      gmx_mdoutf_t   *outf,
-                      t_mdebin       *mdebin,
-                      gmx_ekindata_t *ekind,
-                      rvec           *f,
-                      rvec           *f_global,
-                      gmx_wallcycle_t wcycle,
-                      gmx_rng_t       mcrng,
-                      int            *nchkpt,
-                      gmx_bool        bCPT,
-                      gmx_bool        bRerunMD,
-                      gmx_bool        bLastStep,
-                      gmx_bool        bDoConfOut,
-                      gmx_bool        bSumEkinhOld
-                      )
+do_md_trajectory_writing(FILE           *fplog,
+                         t_commrec      *cr,
+                         int             nfile,
+                         const t_filenm  fnm[],
+                         gmx_int64_t     step,
+                         gmx_int64_t     step_rel,
+                         double          t,
+                         t_inputrec     *ir,
+                         t_state        *state,
+                         t_state        *state_global,
+                         gmx_mtop_t     *top_global,
+                         t_forcerec     *fr,
+                         gmx_update_t    upd,
+                         gmx_mdoutf_t    outf,
+                         t_mdebin       *mdebin,
+                         gmx_ekindata_t *ekind,
+                         rvec           *f,
+                         rvec           *f_global,
+                         gmx_wallcycle_t wcycle,
+                         gmx_rng_t       mcrng,
+                         int            *nchkpt,
+                         gmx_bool        bCPT,
+                         gmx_bool        bRerunMD,
+                         gmx_bool        bLastStep,
+                         gmx_bool        bDoConfOut,
+                         gmx_bool        bSumEkinhOld
+                         )
 {
     int   mdof_flags;
 
@@ -92,9 +92,9 @@ do_trajectory_writing(FILE           *fplog,
     {
         mdof_flags |= MDOF_F;
     }
-    if (do_per_step(step, ir->nstxtcout))
+    if (do_per_step(step, ir->nstxout_compressed))
     {
-        mdof_flags |= MDOF_XTC;
+        mdof_flags |= MDOF_X_COMPRESSED;
     }
     if (bCPT)
     {
@@ -153,8 +153,8 @@ do_trajectory_writing(FILE           *fplog,
                 update_energyhistory(&state_global->enerhist, mdebin);
             }
         }
-        write_traj(fplog, cr, outf, mdof_flags,
-                   step, t, state, state_global, f, f_global);
+        mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global,
+                                         step, t, state, state_global, f, f_global);
         if (bCPT)
         {
             (*nchkpt)++;
@@ -165,7 +165,7 @@ do_trajectory_writing(FILE           *fplog,
             bDoConfOut && MASTER(cr) &&
             !bRerunMD)
         {
-            /* x and v have been collected in write_traj,
+            /* x and v have been collected in mdoutf_write_to_trajectory_files,
              * because a checkpoint file will always be written
              * at the last step.
              */
index 76b6c2a5f537a40eb6d623ac2bf2a74461e3feb5..1bfc38d9ba9372e787482808f07353b37ed468a7 100644 (file)
 #include "../legacyheaders/update.h"
 #include "../legacyheaders/mdebin.h"
 
+/*! \brief Wrapper routine for writing trajectories during mdrun
+ *
+ * This routine does communication (e.g. collecting distributed coordinates)
+ */
 void
-do_trajectory_writing(FILE           *fplog,
-                      t_commrec      *cr,
-                      int             nfile,
-                      const t_filenm  fnm[],
-                      gmx_int64_t     step,
-                      gmx_int64_t     step_rel,
-                      double          t,
-                      t_inputrec     *ir,
-                      t_state        *state,
-                      t_state        *state_global,
-                      gmx_mtop_t     *top_global,
-                      t_forcerec     *fr,
-                      gmx_update_t    upd,
-                      gmx_mdoutf_t   *outf,
-                      t_mdebin       *mdebin,
-                      gmx_ekindata_t *ekind,
-                      rvec           *f,
-                      rvec           *f_global,
-                      gmx_wallcycle_t wcycle,
-                      gmx_rng_t       mcrng,
-                      int            *nchkpt,
-                      gmx_bool        bCPT,
-                      gmx_bool        bRerunMD,
-                      gmx_bool        bLastStep,
-                      gmx_bool        bDoConfOut,
-                      gmx_bool        bSumEkinhOld
-                      );
-/* Wrapper routine for trajectory writing */
+do_md_trajectory_writing(FILE           *fplog,
+                         t_commrec      *cr,
+                         int             nfile,
+                         const t_filenm  fnm[],
+                         gmx_int64_t     step,
+                         gmx_int64_t     step_rel,
+                         double          t,
+                         t_inputrec     *ir,
+                         t_state        *state,
+                         t_state        *state_global,
+                         gmx_mtop_t     *top_global,
+                         t_forcerec     *fr,
+                         gmx_update_t    upd,
+                         gmx_mdoutf_t    outf,
+                         t_mdebin       *mdebin,
+                         gmx_ekindata_t *ekind,
+                         rvec           *f,
+                         rvec           *f_global,
+                         gmx_wallcycle_t wcycle,
+                         gmx_rng_t       mcrng,
+                         int            *nchkpt,
+                         gmx_bool        bCPT,
+                         gmx_bool        bRerunMD,
+                         gmx_bool        bLastStep,
+                         gmx_bool        bDoConfOut,
+                         gmx_bool        bSumEkinhOld
+                         );
 
-void write_traj(FILE *fplog, t_commrec *cr,
-                gmx_mdoutf_t *of,
-                int mdof_flags,
-                gmx_int64_t step, double t,
-                t_state *state_local, t_state *state_global,
-                rvec *f_local, rvec *f_global);
-/* Routine that writes frames to trn, xtc and/or checkpoint.
- * What is written is determined by the mdof_flags defined above.
- * Data is collected to the master node only when necessary.
- */
 
 #endif /* GMX_FILEIO_TRAJECTORY_WRITING_H */
diff --git a/src/gromacs/fileio/trajectory_writing_low_level.c b/src/gromacs/fileio/trajectory_writing_low_level.c
deleted file mode 100644 (file)
index 894737a..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 2013,2014, by the GROMACS development team, led by
- * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
- * and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at http://www.gromacs.org.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-
-#include "trajectory_writing.h"
-#include "gromacs/legacyheaders/mvdata.h"
-#include "gromacs/legacyheaders/domdec.h"
-#include "checkpoint.h"
-#include "trnio.h"
-#include "xtcio.h"
-#include "gromacs/legacyheaders/smalloc.h"
-
-static void moveit(t_commrec *cr, rvec xx[])
-{
-    if (!xx)
-    {
-        return;
-    }
-
-    move_rvecs(cr, FALSE, FALSE, xx, NULL, (cr->nnodes-cr->npmenodes)-1, NULL);
-}
-
-void write_traj(FILE *fplog, t_commrec *cr,
-                gmx_mdoutf_t *of,
-                int mdof_flags,
-                gmx_int64_t step, double t,
-                t_state *state_local, t_state *state_global,
-                rvec *f_local, rvec *f_global)
-{
-    rvec *local_v;
-    rvec *global_v;
-
-#define MX(xvf) moveit(cr, xvf)
-
-    /* MRS -- defining these variables is to manage the difference
-     * between half step and full step velocities, but there must be a better way . . . */
-
-    local_v  = state_local->v;
-    global_v = state_global->v;
-
-    if (DOMAINDECOMP(cr))
-    {
-        if (mdof_flags & MDOF_CPT)
-        {
-            dd_collect_state(cr->dd, state_local, state_global);
-        }
-        else
-        {
-            if (mdof_flags & (MDOF_X | MDOF_XTC))
-            {
-                dd_collect_vec(cr->dd, state_local, state_local->x,
-                               state_global->x);
-            }
-            if (mdof_flags & MDOF_V)
-            {
-                dd_collect_vec(cr->dd, state_local, local_v,
-                               global_v);
-            }
-        }
-        if (mdof_flags & MDOF_F)
-        {
-            dd_collect_vec(cr->dd, state_local, f_local, f_global);
-        }
-    }
-    else
-    {
-        if (mdof_flags & MDOF_CPT)
-        {
-            /* All pointers in state_local are equal to state_global,
-             * but we need to copy the non-pointer entries.
-             */
-            state_global->lambda = state_local->lambda;
-            state_global->veta   = state_local->veta;
-            state_global->vol0   = state_local->vol0;
-            copy_mat(state_local->box, state_global->box);
-            copy_mat(state_local->boxv, state_global->boxv);
-            copy_mat(state_local->svir_prev, state_global->svir_prev);
-            copy_mat(state_local->fvir_prev, state_global->fvir_prev);
-            copy_mat(state_local->pres_prev, state_global->pres_prev);
-        }
-        if (cr->nnodes > 1)
-        {
-            /* Particle decomposition, collect the data on the master node */
-            if (mdof_flags & MDOF_CPT)
-            {
-                if (state_local->flags & (1<<estX))
-                {
-                    MX(state_global->x);
-                }
-                if (state_local->flags & (1<<estV))
-                {
-                    MX(state_global->v);
-                }
-                if (state_local->flags & (1<<estSDX))
-                {
-                    MX(state_global->sd_X);
-                }
-                if (state_global->nrngi > 1)
-                {
-                    if (state_local->flags & (1<<estLD_RNG))
-                    {
-#ifdef GMX_MPI
-                        MPI_Gather(state_local->ld_rng,
-                                   state_local->nrng*sizeof(state_local->ld_rng[0]), MPI_BYTE,
-                                   state_global->ld_rng,
-                                   state_local->nrng*sizeof(state_local->ld_rng[0]), MPI_BYTE,
-                                   MASTERRANK(cr), cr->mpi_comm_mygroup);
-#endif
-                    }
-                    if (state_local->flags & (1<<estLD_RNGI))
-                    {
-#ifdef GMX_MPI
-                        MPI_Gather(state_local->ld_rngi,
-                                   sizeof(state_local->ld_rngi[0]), MPI_BYTE,
-                                   state_global->ld_rngi,
-                                   sizeof(state_local->ld_rngi[0]), MPI_BYTE,
-                                   MASTERRANK(cr), cr->mpi_comm_mygroup);
-#endif
-                    }
-                }
-            }
-            else
-            {
-                if (mdof_flags & (MDOF_X | MDOF_XTC))
-                {
-                    MX(state_global->x);
-                }
-                if (mdof_flags & MDOF_V)
-                {
-                    MX(global_v);
-                }
-            }
-            if (mdof_flags & MDOF_F)
-            {
-                MX(f_global);
-            }
-        }
-    }
-
-    if (MASTER(cr))
-    {
-        if (mdof_flags & MDOF_CPT)
-        {
-            write_checkpoint(of->fn_cpt, of->bKeepAndNumCPT,
-                             fplog, cr, of->eIntegrator, of->simulation_part,
-                             of->bExpanded, of->elamstats, step, t, state_global);
-        }
-
-        if (mdof_flags & (MDOF_X | MDOF_V | MDOF_F))
-        {
-            fwrite_trn(of->fp_trn, step, t, state_local->lambda[efptFEP],
-                       state_local->box, of->natoms_global,
-                       (mdof_flags & MDOF_X) ? state_global->x : NULL,
-                       (mdof_flags & MDOF_V) ? global_v : NULL,
-                       (mdof_flags & MDOF_F) ? f_global : NULL);
-            if (gmx_fio_flush(of->fp_trn) != 0)
-            {
-                gmx_file("Cannot write trajectory; maybe you are out of disk space?");
-            }
-        }
-        if (mdof_flags & MDOF_XTC)
-        {
-            rvec *xxtc = NULL;
-
-            if (of->natoms_xtc == of->natoms_global)
-            {
-                /* We are writing all the atoms to XTC output */
-                xxtc = state_global->x;
-            }
-            else
-            {
-                /* We are writing only a subset of the atoms to XTC
-                   output, so we have to make a copy of the subset of
-                   coordinates. */
-                int i, j;
-
-                snew(xxtc, of->natoms_xtc);
-                for (i = 0, j = 0; ((i < of->natoms_global) &&
-                                    (j < of->natoms_xtc)); i++)
-                {
-                    if (ggrpnr(of->groups, egcXTC, i) == 0)
-                    {
-                        copy_rvec(state_global->x[i], xxtc[j++]);
-                    }
-                }
-            }
-            if (write_xtc(of->fp_xtc, of->natoms_xtc, step, t,
-                          state_local->box, xxtc, of->xtc_prec) == 0)
-            {
-                gmx_fatal(FARGS, "XTC error - maybe you are out of disk space?");
-            }
-            if (of->natoms_xtc != of->natoms_global)
-            {
-                sfree(xxtc);
-            }
-        }
-    }
-}
index 7367fa92aa907b4fc24239c9082c743a8937c86a..74b4831b8bfd05b737a433c60123708f731d6e0a 100644 (file)
@@ -54,6 +54,8 @@
 #include "trxio.h"
 #include "tpxio.h"
 #include "trnio.h"
+#include "tngio.h"
+#include "tngio_for_tools.h"
 #include "names.h"
 #include "vec.h"
 #include "futil.h"
@@ -77,15 +79,16 @@ typedef enum {
 
 struct t_trxstatus
 {
-    int             __frame;
-    t_trxframe     *xframe;
-    int             nxframe;
-    t_fileio       *fio;
-    eFileFormat     eFF;
-    int             NATOMS;
-    double          DT, BOX[3];
-    gmx_bool        bReadBox;
-    char           *persistent_line; /* Persistent line for reading g96 trajectories */
+    int                     __frame;
+    t_trxframe             *xframe;
+    int                     nxframe;
+    t_fileio               *fio;
+    tng_trajectory_t        tng;
+    eFileFormat             eFF;
+    int                     NATOMS;
+    double                  DT, BOX[3];
+    gmx_bool                bReadBox;
+    char                   *persistent_line; /* Persistent line for reading g96 trajectories */
 };
 
 /* utility functions */
@@ -162,6 +165,7 @@ static void status_init(t_trxstatus *status)
     status->fio             = NULL;
     status->__frame         = -1;
     status->persistent_line = NULL;
+    status->tng             = NULL;
 }
 
 
@@ -219,46 +223,53 @@ int prec2ndec(real prec)
     return (int)(log(prec)/log(10.0)+0.5);
 }
 
+real ndec2prec(int ndec)
+{
+    return pow(10.0, ndec);
+}
 
 t_fileio *trx_get_fileio(t_trxstatus *status)
 {
     return status->fio;
 }
 
-
+tng_trajectory_t trx_get_tng(t_trxstatus *status)
+{
+    return status->tng;
+}
 
 void clear_trxframe(t_trxframe *fr, gmx_bool bFirst)
 {
-    fr->not_ok  = 0;
-    fr->bTitle  = FALSE;
-    fr->bStep   = FALSE;
-    fr->bTime   = FALSE;
-    fr->bLambda = FALSE;
+    fr->not_ok    = 0;
+    fr->bTitle    = FALSE;
+    fr->bStep     = FALSE;
+    fr->bTime     = FALSE;
+    fr->bLambda   = FALSE;
     fr->bFepState = FALSE;
-    fr->bAtoms  = FALSE;
-    fr->bPrec   = FALSE;
-    fr->bX      = FALSE;
-    fr->bV      = FALSE;
-    fr->bF      = FALSE;
-    fr->bBox    = FALSE;
+    fr->bAtoms    = FALSE;
+    fr->bPrec     = FALSE;
+    fr->bX        = FALSE;
+    fr->bV        = FALSE;
+    fr->bF        = FALSE;
+    fr->bBox      = FALSE;
     if (bFirst)
     {
-        fr->flags   = 0;
-        fr->bDouble = FALSE;
-        fr->natoms  = -1;
-        fr->t0      = 0;
-        fr->tpf     = 0;
-        fr->tppf    = 0;
-        fr->title   = NULL;
-        fr->step    = 0;
-        fr->time    = 0;
-        fr->lambda  = 0;
+        fr->flags     = 0;
+        fr->bDouble   = FALSE;
+        fr->natoms    = -1;
+        fr->t0        = 0;
+        fr->tpf       = 0;
+        fr->tppf      = 0;
+        fr->title     = NULL;
+        fr->step      = 0;
+        fr->time      = 0;
+        fr->lambda    = 0;
         fr->fep_state = 0;
-        fr->atoms   = NULL;
-        fr->prec    = 0;
-        fr->x       = NULL;
-        fr->v       = NULL;
-        fr->f       = NULL;
+        fr->atoms     = NULL;
+        fr->prec      = 0;
+        fr->x         = NULL;
+        fr->v         = NULL;
+        fr->f         = NULL;
         clear_mat(fr->box);
         fr->bPBC   = FALSE;
         fr->ePBC   = -1;
@@ -276,7 +287,7 @@ int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
 {
     char  title[STRLEN];
     rvec *xout = NULL, *vout = NULL, *fout = NULL;
-    int   i;
+    int   i, ftp = -1;
     real  prec;
 
     if (fr->bPrec)
@@ -288,24 +299,39 @@ int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
         prec = 1000.0;
     }
 
-    switch (gmx_fio_getftp(status->fio))
+    if (status->tng)
+    {
+        ftp = efTNG;
+    }
+    else if (status->fio)
+    {
+        ftp = gmx_fio_getftp(status->fio);
+    }
+    else
+    {
+        gmx_incons("No input file available");
+    }
+
+    switch (ftp)
     {
         case efTRJ:
         case efTRR:
+        case efTNG:
             break;
         default:
             if (!fr->bX)
             {
                 gmx_fatal(FARGS, "Need coordinates to write a %s trajectory",
-                          ftp2ext(gmx_fio_getftp(status->fio)));
+                          ftp2ext(ftp));
             }
             break;
     }
 
-    switch (gmx_fio_getftp(status->fio))
+    switch (ftp)
     {
         case efTRJ:
         case efTRR:
+        case efTNG:
             if (fr->bV)
             {
                 snew(vout, nind);
@@ -338,8 +364,11 @@ int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
             break;
     }
 
-    switch (gmx_fio_getftp(status->fio))
+    switch (ftp)
     {
+        case efTNG:
+            gmx_write_tng_from_trxframe(status->tng, fr, nind);
+            break;
         case efXTC:
             write_xtc(status->fio, nind, fr->step, fr->time, fr->box, xout, prec);
             break;
@@ -355,10 +384,10 @@ int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
             if (!fr->bAtoms)
             {
                 gmx_fatal(FARGS, "Can not write a %s file without atom names",
-                          ftp2ext(gmx_fio_getftp(status->fio)));
+                          ftp2ext(ftp));
             }
             sprintf(title, "frame t= %.3f", fr->time);
-            if (gmx_fio_getftp(status->fio) == efGRO)
+            if (ftp == efGRO)
             {
                 write_hconf_indexed_p(gmx_fio_getfp(status->fio), title, fr->atoms, nind, ind,
                                       prec2ndec(prec),
@@ -378,15 +407,16 @@ int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
             break;
         default:
             gmx_fatal(FARGS, "Sorry, write_trxframe_indexed can not write %s",
-                      ftp2ext(gmx_fio_getftp(status->fio)));
+                      ftp2ext(ftp));
             break;
     }
 
-    switch (gmx_fio_getftp(status->fio))
+    switch (ftp)
     {
         case efTRN:
         case efTRJ:
         case efTRR:
+        case efTNG:
             if (vout)
             {
                 sfree(vout);
@@ -407,6 +437,60 @@ int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
     return 0;
 }
 
+void trjtools_gmx_prepare_tng_writing(const char       *filename,
+                                      char              filemode,
+                                      t_trxstatus      *in,
+                                      t_trxstatus     **out,
+                                      const char       *infile,
+                                      const int         natoms,
+                                      const gmx_mtop_t *mtop,
+                                      const atom_id    *index,
+                                      const char       *index_group_name)
+{
+    if (filemode != 'w' && filemode != 'a')
+    {
+        gmx_incons("Sorry, can only prepare for TNG output.");
+    }
+
+    if (*out == NULL)
+    {
+        snew((*out), 1);
+    }
+    status_init(*out);
+
+    if (in != NULL)
+    {
+        gmx_prepare_tng_writing(filename,
+                                filemode,
+                                &in->tng,
+                                &(*out)->tng,
+                                natoms,
+                                mtop,
+                                index,
+                                index_group_name);
+    }
+    else if (efTNG == fn2ftp(infile))
+    {
+        tng_trajectory_t tng_in;
+        gmx_tng_open(infile, 'r', &tng_in);
+
+        gmx_prepare_tng_writing(filename,
+                                filemode,
+                                &tng_in,
+                                &(*out)->tng,
+                                natoms,
+                                mtop,
+                                index,
+                                index_group_name);
+    }
+}
+
+void write_tng_frame(t_trxstatus *status,
+                     t_trxframe  *frame)
+{
+    gmx_write_tng_from_trxframe(status->tng, frame, -1);
+}
+
 int write_trxframe(t_trxstatus *status, t_trxframe *fr, gmx_conect gc)
 {
     char title[STRLEN];
@@ -421,6 +505,14 @@ int write_trxframe(t_trxstatus *status, t_trxframe *fr, gmx_conect gc)
         prec = 1000.0;
     }
 
+    if (status->tng)
+    {
+        gmx_tng_set_compression_precision(status->tng, prec);
+        write_tng_frame(status, fr);
+
+        return 0;
+    }
+
     switch (gmx_fio_getftp(status->fio))
     {
         case efTRJ:
@@ -507,7 +599,11 @@ int write_trx(t_trxstatus *status, int nind, const atom_id *ind, t_atoms *atoms,
 
 void close_trx(t_trxstatus *status)
 {
-    gmx_fio_close(status->fio);
+    gmx_tng_close(&status->tng);
+    if (status->fio)
+    {
+        gmx_fio_close(status->fio);
+    }
     sfree(status);
 }
 
@@ -870,6 +966,7 @@ gmx_bool read_next_frame(const output_env_t oenv, t_trxstatus *status, t_trxfram
     int      ct;
     gmx_bool bOK, bRet, bMissingData = FALSE, bSkip = FALSE;
     int      dummy = 0;
+    int      ftp;
 
     bRet = FALSE;
     pt   = fr->time;
@@ -880,7 +977,16 @@ gmx_bool read_next_frame(const output_env_t oenv, t_trxstatus *status, t_trxfram
         fr->tppf = fr->tpf;
         fr->tpf  = fr->time;
 
-        switch (gmx_fio_getftp(status->fio))
+        if (status->tng)
+        {
+            /* Special treatment for TNG files */
+            ftp = efTNG;
+        }
+        else
+        {
+            ftp = gmx_fio_getftp(status->fio);
+        }
+        switch (ftp)
         {
             case efTRJ:
             case efTRR:
@@ -932,6 +1038,9 @@ gmx_bool read_next_frame(const output_env_t oenv, t_trxstatus *status, t_trxfram
                     fr->not_ok = DATA_NOT_OK;
                 }
                 break;
+            case efTNG:
+                bRet = gmx_read_next_tng_frame(status->tng, fr, NULL, 0);
+                break;
             case efPDB:
                 bRet = pdb_next_x(status, gmx_fio_getfp(status->fio), fr);
                 break;
@@ -991,9 +1100,11 @@ gmx_bool read_next_frame(const output_env_t oenv, t_trxstatus *status, t_trxfram
 int read_first_frame(const output_env_t oenv, t_trxstatus **status,
                      const char *fn, t_trxframe *fr, int flags)
 {
-    t_fileio *fio;
-    gmx_bool  bFirst, bOK;
-    int       dummy = 0;
+    t_fileio      *fio;
+    gmx_bool       bFirst, bOK;
+    int            dummy = 0;
+    int            ftp   = fn2ftp(fn);
+    gmx_int64_t   *tng_ids;
 
     clear_trxframe(fr, TRUE);
     fr->flags = flags;
@@ -1006,8 +1117,16 @@ int read_first_frame(const output_env_t oenv, t_trxstatus **status,
     (*status)->nxframe = 1;
     initcount(*status);
 
-    fio = (*status)->fio = gmx_fio_open(fn, "r");
-    switch (gmx_fio_getftp(fio))
+    if (efTNG == ftp)
+    {
+        /* Special treatment for TNG files */
+        gmx_tng_open(fn, 'r', &(*status)->tng);
+    }
+    else
+    {
+        fio = (*status)->fio = gmx_fio_open(fn, "r");
+    }
+    switch (ftp)
     {
         case efTRJ:
         case efTRR:
@@ -1071,6 +1190,20 @@ int read_first_frame(const output_env_t oenv, t_trxstatus **status,
             }
             bFirst = FALSE;
             break;
+        case efTNG:
+            fr->step = -1;
+            if (!gmx_read_next_tng_frame((*status)->tng, fr, NULL, 0))
+            {
+                fr->not_ok = DATA_NOT_OK;
+                fr->natoms = 0;
+                printincomp(*status, fr);
+            }
+            else
+            {
+                printcount(*status, oenv, fr->time, FALSE);
+            }
+            bFirst = FALSE;
+            break;
         case efPDB:
             pdb_first_x(*status, gmx_fio_getfp(fio), fr);
             if (fr->natoms)
@@ -1161,7 +1294,12 @@ gmx_bool read_next_x(const output_env_t oenv, t_trxstatus *status, real *t,
 
 void close_trj(t_trxstatus *status)
 {
-    gmx_fio_close(status->fio);
+    gmx_tng_close(&status->tng);
+    if (status->fio)
+    {
+        gmx_fio_close(status->fio);
+    }
+
     /* The memory in status->xframe is lost here,
      * but the read_first_x/read_next_x functions are deprecated anyhow.
      * read_first_frame/read_next_frame and close_trx should be used.
index b5ba5bb8b5d7f4059adc5d319d71216b43f9bc00..28cc436aef2aba7821549dfd307da7927e706fc7 100644 (file)
@@ -44,6 +44,7 @@
 #include "pdbio.h"
 #include "../legacyheaders/oenv.h"
 #include "gmxfio.h"
+#include "../../external/tng_io/include/tng_io_fwd.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -64,6 +65,10 @@ typedef struct t_trxstatus t_trxstatus;
 int prec2ndec(real prec);
 /* Convert precision in 1/(nm) to number of decimal places */
 
+/*! \brief Convert number of decimal places to trajectory precision in
+ * 1/(nm) */
+real ndec2prec(int ndec);
+
 void clear_trxframe(t_trxframe *fr, gmx_bool bFirst);
 /* Set all content gmx_booleans to FALSE.
  * When bFirst = TRUE, set natoms=-1, all pointers to NULL
@@ -98,6 +103,36 @@ int write_trx(t_trxstatus *status, int nind, const atom_id *ind, t_atoms *atoms,
  * atoms can be NULL for file types which don't need atom names.
  */
 
+void trjtools_gmx_prepare_tng_writing(const char       *filename,
+                                      char              filemode,
+                                      t_trxstatus      *in,
+                                      t_trxstatus     **out,
+                                      const char       *infile,
+                                      const int         natoms,
+                                      const gmx_mtop_t *mtop,
+                                      const atom_id    *index,
+                                      const char       *index_group_name);
+/* Sets up *out for writing TNG. If *in != NULL and contains a TNG trajectory
+ * some data, e.g. molecule system, will be copied over from *in to *out.
+ * If *in == NULL a file name (infile) of a TNG file can be provided instead
+ * and used for copying data to *out.
+ * If there is no TNG input natoms is used to create "implicit atoms" (no atom
+ * or molecular data present). If natoms == -1 the number of atoms are
+ * not known (or there is already a TNG molecule system to copy, in which case
+ * natoms is not required anyhow). If an group of indexed atoms are written
+ * natoms must be the length of index. index_group_name is the name of the
+ * index group.
+ */
+
+/*! \brief Write a trxframe to the TNG file in status.
+ *
+ * This function is needed because both t_trxstatus and
+ * tng_trajectory_t are encapsulated, so client trajectory-writing
+ * code with a t_trxstatus can't just call the TNG writing
+ * function. */
+void write_tng_frame(t_trxstatus *status,
+                     t_trxframe  *fr);
+
 void close_trx(t_trxstatus *status);
 /* Close trj file as opened with read_first_x, read_frist_frame
  * or open_trx. Identical to close_trj.
@@ -106,9 +141,11 @@ void close_trx(t_trxstatus *status);
 t_trxstatus *open_trx(const char *outfile, const char *filemode);
 /* Open a TRX file and return an allocated status pointer */
 
-/* get a fileio from a trxstatus */
 t_fileio *trx_get_fileio(t_trxstatus *status);
+/* get a fileio from a trxstatus */
 
+tng_trajectory_t trx_get_tng(t_trxstatus *status);
+/* get a tng trajectory container from a trxstatus */
 
 gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble);
 /* Returns TRUE when (a - b) MOD c = 0, using a margin which is slightly
index 039e1020a8c7a1c1a476ef15f606e2f52467685c..d7a866597227015529072bb5c7cf59ba2ae29abb 100644 (file)
@@ -209,6 +209,14 @@ int write_xtc(t_fileio *fio,
     gmx_bool bDum;
     int      bOK;
 
+    if (!fio)
+    {
+        /* This means the fio object is not being used, e.g. because
+           we are actually writing TNG output. We still have to return
+           a pseudo-success value, to keep some callers happy. */
+        return 1;
+    }
+
     xd = gmx_fio_getxdr(fio);
     /* write magic number and xtc identidier */
     if (xtc_header(xd, &magic_number, &natoms, &step, &time, FALSE, &bDum) == 0)
index ae5a1ec58e17260230aec020f2f8947b6d384bd5..72ac220a185dd9f69832c7848593af6e89911df6 100644 (file)
@@ -123,7 +123,7 @@ static void clust_size(const char *ndx, const char *trx, const char *xpm,
         read_tpxheader(tpr, &tpxh, TRUE, &version, &generation);
         if (tpxh.natoms != natoms)
         {
-            gmx_fatal(FARGS, "tpr (%d atoms) and xtc (%d atoms) do not match!",
+            gmx_fatal(FARGS, "tpr (%d atoms) and trajectory (%d atoms) do not match!",
                       tpxh.natoms, natoms);
         }
         ePBC = read_tpx(tpr, NULL, NULL, &natoms, NULL, NULL, NULL, mtop);
index c092a7a26499b9dcfee48bc364912958834ccdaf..39caf539ebcb88086464df28b542f9d09cbb85ca 100644 (file)
@@ -55,7 +55,6 @@
 #include "gromacs/fileio/tpxio.h"
 #include "gromacs/fileio/trxio.h"
 #include "rmpbc.h"
-#include "gromacs/fileio/xtcio.h"
 #include "gmx_ana.h"
 
 
index c25cf1d93c5916d0b68125dcb8bc640d22bf6d00..78a64e4cf0f82ff766b874466978dd2ef0719dde 100644 (file)
@@ -68,7 +68,7 @@ int gmx_spatial(int argc, char *argv[])
         "For a system of 32,000 atoms and a 50 ns trajectory, the SDF can be generated ",
         "in about 30 minutes, with most of the time dedicated to the two runs through ",
         "[TT]trjconv[tt] that are required to center everything properly. ",
-        "This also takes a whole bunch of space (3 copies of the [TT].xtc[tt] file). ",
+        "This also takes a whole bunch of space (3 copies of the trajectory file). ",
         "Still, the pictures are pretty and very informative when the fitted selection is properly made. ",
         "3-4 atoms in a widely mobile group (like a free amino acid in solution) works ",
         "well, or select the protein backbone in a stable folded structure to get the SDF ",
@@ -77,9 +77,9 @@ int gmx_spatial(int argc, char *argv[])
         "Cartesian coordinate. To do that, simply omit the preliminary [gmx-trjconv] steps. \n",
         "USAGE: \n",
         "1. Use [gmx-make_ndx] to create a group containing the atoms around which you want the SDF \n",
-        "2. [TT]gmx trjconv -s a.tpr -f a.xtc -o b.xtc -boxcenter tric -ur compact -pbc none[tt] \n",
-        "3. [TT]gmx trjconv -s a.tpr -f b.xtc -o c.xtc -fit rot+trans[tt] \n",
-        "4. run [THISMODULE] on the [TT].xtc[tt] output of step #3. \n",
+        "2. [TT]gmx trjconv -s a.tpr -f a.tng -o b.tng -boxcenter tric -ur compact -pbc none[tt] \n",
+        "3. [TT]gmx trjconv -s a.tpr -f b.tng -o c.tng -fit rot+trans[tt] \n",
+        "4. run [THISMODULE] on the [TT]c.tng[tt] output of step #3. \n",
         "5. Load [TT]grid.cube[tt] into VMD and view as an isosurface. \n",
         "[BB]Note[bb] that systems such as micelles will require [TT]gmx trjconv -pbc cluster[tt] between steps 1 and 2\n",
         "WARNINGS:[BR]",
index 990dd7ee94ad62beb3c18297823ea29d9aebef8b..be102c28b394bf2bcecb818b6fa2c7647b1016ac 100644 (file)
@@ -48,6 +48,8 @@
 #include "gromacs/fileio/tpxio.h"
 #include "gromacs/fileio/trxio.h"
 #include "gromacs/fileio/trnio.h"
+#include "gromacs/fileio/tngio.h"
+#include "gromacs/fileio/tngio_for_tools.h"
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/fileio/futil.h"
 #include "gromacs/fileio/pdbio.h"
@@ -476,13 +478,12 @@ int gmx_trjcat(int argc, char *argv[])
     };
 #define npargs asize(pa)
     int          ftpin, i, frame, frame_out, step = 0, trjout = 0;
-    t_trxstatus *status;
+    t_trxstatus *status, *trxout = NULL;
     rvec        *x, *v;
     real         t_corr;
     t_trxframe   fr, frout;
     char       **fnms, **fnms_out, *in_file, *out_file;
     int          n_append;
-    t_trxstatus *trxout = NULL;
     gmx_bool     bNewFile, bIndex, bWrite;
     int          earliersteps, nfile_in, nfile_out, *cont_type, last_ok_step;
     real        *readtime, *timest, *settime;
@@ -492,7 +493,7 @@ int gmx_trjcat(int argc, char *argv[])
     atom_id     *index = NULL, imax;
     char        *grpname;
     real       **val = NULL, *t = NULL, dt_remd;
-    int          n, nset;
+    int          n, nset, ftpout = -1, prevEndStep = 0, filetype;
     gmx_bool     bOK;
     gmx_off_t    fpos;
     output_env_t oenv;
@@ -563,6 +564,16 @@ int gmx_trjcat(int argc, char *argv[])
         gmx_fatal(FARGS, "You have specified %d files and %d entries in the demux table", nfile_in, nset);
     }
 
+    ftpin = fn2ftp(fnms[0]);
+
+    for (i = 1; i < nfile_in; i++)
+    {
+        if (ftpin != fn2ftp(fnms[i]))
+        {
+            gmx_fatal(FARGS, "All input files must be of the same format");
+        }
+    }
+
     nfile_out = opt2fns(&fnms_out, "-o", NFILE, fnm);
     if (!nfile_out)
     {
@@ -606,6 +617,7 @@ int gmx_trjcat(int argc, char *argv[])
          * This has to be done after sorting etc.
          */
         out_file = fnms_out[0];
+        ftpout   = fn2ftp(out_file);
         n_append = -1;
         for (i = 0; ((i < nfile_in) && (n_append == -1)); i++)
         {
@@ -638,7 +650,23 @@ int gmx_trjcat(int argc, char *argv[])
 
         if (n_append == -1)
         {
-            trxout = open_trx(out_file, "w");
+            if (ftpout == efTNG)
+            {
+                if (bIndex)
+                {
+                    trjtools_gmx_prepare_tng_writing(out_file, 'w', NULL, &trxout,
+                                                     fnms[0], isize, NULL, index, grpname);
+                }
+                else
+                {
+                    trjtools_gmx_prepare_tng_writing(out_file, 'w', NULL, &trxout,
+                                                     fnms[0], -1, NULL, NULL, NULL);
+                }
+            }
+            else
+            {
+                trxout = open_trx(out_file, "w");
+            }
             memset(&frout, 0, sizeof(frout));
         }
         else
@@ -658,10 +686,11 @@ int gmx_trjcat(int argc, char *argv[])
                         "If the trajectories have an overlap and have not been written binary \n"
                         "reproducible this will produce an incorrect trajectory!\n\n");
 
+                filetype = gmx_fio_getftp(stfio);
                 /* Fails if last frame is incomplete
                  * We can't do anything about it without overwriting
                  * */
-                if (gmx_fio_getftp(stfio) == efXTC)
+                if (filetype == efXTC)
                 {
                     lasttime =
                         xdr_xtc_get_last_frame_time(gmx_fio_getfp(stfio),
@@ -673,6 +702,16 @@ int gmx_trjcat(int argc, char *argv[])
                         gmx_fatal(FARGS, "Error reading last frame. Maybe seek not supported." );
                     }
                 }
+                else if (filetype == efTNG)
+                {
+                    tng_trajectory_t tng = trx_get_tng(status);
+                    if (!tng)
+                    {
+                        gmx_fatal(FARGS, "Error opening TNG file.");
+                    }
+                    lasttime = gmx_tng_get_time_of_final_frame(tng);
+                    fr.time  = lasttime;
+                }
                 else
                 {
                     while (read_next_frame(oenv, status, &fr))
@@ -742,6 +781,13 @@ int gmx_trjcat(int argc, char *argv[])
             /* set the next time from the last frame in previous file */
             if (i > 0)
             {
+                /* When writing TNG the step determine which frame to write. Use an
+                 * offset to be able to increase steps properly when changing files. */
+                if (ftpout == efTNG)
+                {
+                    prevEndStep = frout.step;
+                }
+
                 if (frame_out >= 0)
                 {
                     if (cont_type[i] == TIME_CONTINUE)
@@ -818,6 +864,10 @@ int gmx_trjcat(int argc, char *argv[])
                 frout = fr;
                 /* set the new time by adding the correct calculated above */
                 frout.time += t_corr;
+                if (ftpout == efTNG)
+                {
+                    frout.step += prevEndStep;
+                }
                 /* quit if we have reached the end of what should be written */
                 if ((end > 0) && (frout.time > end+GMX_REAL_EPS))
                 {
index 1f6a961cb2f1d42ce07e47f58e8acca663794b90..db0e1fa94a32ef5fd5a78eb54c3041145ab1c1a4 100644 (file)
@@ -50,6 +50,7 @@
 #include "gromacs/fileio/tpxio.h"
 #include "gromacs/fileio/trxio.h"
 #include "gromacs/fileio/trnio.h"
+#include "gromacs/fileio/tngio_for_tools.h"
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/fileio/futil.h"
 #include "gromacs/fileio/pdbio.h"
@@ -548,6 +549,45 @@ void do_trunc(const char *fn, real t0)
 }
 #endif
 
+/*! \brief Read a full molecular topology if useful and available.
+ *
+ * If the input trajectory file is not in TNG format, and the output
+ * file is in TNG format, then we want to try to read a full topology
+ * (if available), so that we can write molecule information to the
+ * output file. The full topology provides better molecule information
+ * than is available from the normal t_topology data used by GROMACS
+ * tools.
+ *
+ * Also, the t_topology is only read under (different) particular
+ * conditions. If both apply, then a .tpr file might be read
+ * twice. Trying to fix this redundancy while trjconv is still an
+ * all-purpose tool does not seem worthwhile.
+ *
+ * Because of the way gmx_prepare_tng_writing is implemented, the case
+ * where the input TNG file has no molecule information will never
+ * lead to an output TNG file having molecule information. Since
+ * molecule information will generally be present if the input TNG
+ * file was written by a GROMACS tool, this seems like reasonable
+ * behaviour. */
+static gmx_mtop_t *read_mtop_for_tng(const char *tps_file,
+                                     const char *input_file,
+                                     const char *output_file)
+{
+    gmx_mtop_t *mtop = NULL;
+
+    if (fn2bTPX(tps_file) &&
+        efTNG != fn2ftp(input_file) &&
+        efTNG == fn2ftp(output_file))
+    {
+        int temp_natoms = -1;
+        snew(mtop, 1);
+        read_tpx(tps_file, NULL, NULL, &temp_natoms,
+                 NULL, NULL, NULL, mtop);
+    }
+
+    return mtop;
+}
+
 int gmx_trjconv(int argc, char *argv[])
 {
     const char *desc[] = {
@@ -825,7 +865,7 @@ int gmx_trjconv(int argc, char *argv[])
 
     FILE            *out    = NULL;
     t_trxstatus     *trxout = NULL;
-    t_trxstatus     *status;
+    t_trxstatus     *trxin;
     int              ftp, ftpin = 0, file_nr;
     t_trxframe       fr, frout;
     int              flags;
@@ -835,6 +875,7 @@ int gmx_trjconv(int argc, char *argv[])
     int              m, i, d, frame, outframe, natoms, nout, ncent, nre, newstep = 0, model_nr;
 #define SKIP 10
     t_topology       top;
+    gmx_mtop_t      *mtop  = NULL;
     gmx_conect       gc    = NULL;
     int              ePBC  = -1;
     t_atoms         *atoms = NULL, useatoms;
@@ -1051,6 +1092,8 @@ int gmx_trjconv(int argc, char *argv[])
         {
         }
 
+        mtop = read_mtop_for_tng(top_file, in_file, out_file);
+
         /* Determine whether to read a topology */
         bTPS = (ftp2bSet(efTPS, NFILE, fnm) ||
                 bRmPBC || bReset || bPBCcomMol || bCluster ||
@@ -1146,12 +1189,12 @@ int gmx_trjconv(int argc, char *argv[])
         else
         {
             /* no index file, so read natoms from TRX */
-            if (!read_first_frame(oenv, &status, in_file, &fr, TRX_DONT_SKIP))
+            if (!read_first_frame(oenv, &trxin, in_file, &fr, TRX_DONT_SKIP))
             {
                 gmx_fatal(FARGS, "Could not read a frame from %s", in_file);
             }
             natoms = fr.natoms;
-            close_trj(status);
+            close_trj(trxin);
             sfree(fr.x);
             snew(index, natoms);
             for (i = 0; i < natoms; i++)
@@ -1238,7 +1281,7 @@ int gmx_trjconv(int argc, char *argv[])
         }
 
         /* open trx file for reading */
-        bHaveFirstFrame = read_first_frame(oenv, &status, in_file, &fr, flags);
+        bHaveFirstFrame = read_first_frame(oenv, &trxin, in_file, &fr, flags);
         if (fr.bPrec)
         {
             fprintf(stderr, "\nPrecision of %s is %g (nm)\n", in_file, 1/fr.prec);
@@ -1270,6 +1313,23 @@ int gmx_trjconv(int argc, char *argv[])
                 tzero = fr.time;
             }
 
+            bCopy = FALSE;
+            if (bIndex)
+            {
+                /* check if index is meaningful */
+                for (i = 0; i < nout; i++)
+                {
+                    if (index[i] >= natoms)
+                    {
+                        gmx_fatal(FARGS,
+                                  "Index[%d] %d is larger than the number of atoms in the\n"
+                                  "trajectory file (%d). There is a mismatch in the contents\n"
+                                  "of your -f, -s and/or -n files.", i, index[i]+1, natoms);
+                    }
+                    bCopy = bCopy || (i != index[i]);
+                }
+            }
+
             /* open output for writing */
             if ((bAppend) && (gmx_fexist(out_file)))
             {
@@ -1282,6 +1342,17 @@ int gmx_trjconv(int argc, char *argv[])
             }
             switch (ftp)
             {
+                case efTNG:
+                    trjtools_gmx_prepare_tng_writing(out_file,
+                                                     filemode[0],
+                                                     trxin,
+                                                     &trxout,
+                                                     NULL,
+                                                     nout,
+                                                     mtop,
+                                                     index,
+                                                     grpnm);
+                    break;
                 case efXTC:
                 case efG87:
                 case efTRR:
@@ -1300,24 +1371,10 @@ int gmx_trjconv(int argc, char *argv[])
                         out = ffopen(out_file, filemode);
                     }
                     break;
+                default:
+                    gmx_incons("Illegal output file format");
             }
 
-            bCopy = FALSE;
-            if (bIndex)
-            {
-                /* check if index is meaningful */
-                for (i = 0; i < nout; i++)
-                {
-                    if (index[i] >= natoms)
-                    {
-                        gmx_fatal(FARGS,
-                                  "Index[%d] %d is larger than the number of atoms in the\n"
-                                  "trajectory file (%d). There is a mismatch in the contents\n"
-                                  "of your -f, -s and/or -n files.", i, index[i]+1, natoms);
-                    }
-                    bCopy = bCopy || (i != index[i]);
-                }
-            }
             if (bCopy)
             {
                 snew(xmem, nout);
@@ -1694,6 +1751,10 @@ int gmx_trjconv(int argc, char *argv[])
 
                         switch (ftp)
                         {
+                            case efTNG:
+                                write_tng_frame(trxout, &frout);
+                                // TODO when trjconv behaves better: work how to read and write lambda
+                                break;
                             case efTRJ:
                             case efTRR:
                             case efG87:
@@ -1834,7 +1895,7 @@ int gmx_trjconv(int argc, char *argv[])
                     }
                 }
                 frame++;
-                bHaveNextFrame = read_next_frame(oenv, status, &fr);
+                bHaveNextFrame = read_next_frame(oenv, trxin, &fr);
             }
             while (!(bTDump && bDumpFrame) && bHaveNextFrame);
         }
@@ -1846,7 +1907,7 @@ int gmx_trjconv(int argc, char *argv[])
         }
         fprintf(stderr, "\n");
 
-        close_trj(status);
+        close_trj(trxin);
         sfree(outf_base);
 
         if (bRmPBC)
@@ -1874,6 +1935,8 @@ int gmx_trjconv(int argc, char *argv[])
         }
     }
 
+    sfree(mtop);
+
     do_view(oenv, out_file, NULL);
 
     return 0;
index b2ec6baeba7f13ed527b61ad3aed33fea417c680..bda2ae89df34a73acb8e0d9f490283720ce4e7c8 100644 (file)
@@ -2093,7 +2093,7 @@ int gmx_tune_pme(int argc, char *argv[])
         /* mdrun: */
         { efTPX, NULL,      NULL,       ffREAD },
         { efTRN, "-o",      NULL,       ffWRITE },
-        { efXTC, "-x",      NULL,       ffOPTWR },
+        { efCOMPRESSED, "-x", NULL,     ffOPTWR },
         { efCPT, "-cpi",    NULL,       ffOPTRD },
         { efCPT, "-cpo",    NULL,       ffOPTWR },
         { efSTO, "-c",      "confout",  ffWRITE },
index 29e90b6b229d8d0c6a4b87480f03aec38faea74e..ef945bef37671763a1fd2bad6399921513206053 100644 (file)
 #include "gromacs/gmxana/gmx_ana.h"
 #include "testutils/integrationtests.h"
 #include "testutils/cmdlinetest.h"
+#include "gromacs/utility/arrayref.h"
 
 namespace
 {
 
-//! Test fixture for gmx traj
-typedef gmx::test::IntegrationTestFixture GmxTrajTest;
-
-class GmxTrajTestNoFatalError : public GmxTrajTest
+class GmxTraj : public gmx::test::IntegrationTestFixture,
+                public ::testing::WithParamInterface<const char *>
 {
     public:
-        GmxTrajTestNoFatalError() : groFileName(fileManager_.getInputFilePath("spc2.gro")),
-                                    xvgFileName(fileManager_.getTemporaryFilePath("spc2.xvg"))
+        GmxTraj() : groFileName(fileManager_.getInputFilePath("spc2.gro")),
+                    xvgFileName(fileManager_.getTemporaryFilePath("spc2.xvg"))
         {
         }
 
@@ -65,7 +64,7 @@ class GmxTrajTestNoFatalError : public GmxTrajTest
             caller.addOption("-s",  groFileName);
             caller.addOption("-ox", xvgFileName);
 
-            std::string inputTrajectoryFileName = fileManager_.getInputFilePath(fileName).c_str();
+            std::string inputTrajectoryFileName = fileManager_.getInputFilePath(fileName);
             caller.addOption("-f", inputTrajectoryFileName);
 
             redirectStringToStdin("0\n");
@@ -77,48 +76,88 @@ class GmxTrajTestNoFatalError : public GmxTrajTest
         std::string xvgFileName;
 };
 
-/* There are fancy ways to use "value-parameterized tests" to reduce
- * the duplication below, but for the handful of file formats we care
- * about, the code would probably get longer (and need fancy template
- * stuff, too).
- */
-
 /* TODO These tests are actually not very effective, because gmx-traj
  * can only return 0 or exit via gmx_fatal() (which currently also
  * exits the test binary). So, "no XDR/TNG support in the binary"
  * leads to the reading test appearing to pass. Still, no fatal error
  * and no segfault is useful information while modifying such code. */
-TEST_F(GmxTrajTestNoFatalError, TRRFile)
-{
-    runTest("spc2-traj.trr");
-}
 
-TEST_F(GmxTrajTestNoFatalError, XTCFile)
+TEST_P(GmxTraj, WithDifferentInputFormats)
 {
-    runTest("spc2-traj.xtc");
+    runTest(GetParam());
 }
 
-TEST_F(GmxTrajTestNoFatalError, GROFile)
-{
-    runTest("spc2-traj.gro");
-}
+// ==
 
-TEST_F(GmxTrajTestNoFatalError, PDBFile)
+class TrjconvWithIndexGroupSubset : public gmx::test::IntegrationTestFixture,
+                                    public ::testing::WithParamInterface<const char *>
 {
-    runTest("spc2-traj.pdb");
-}
+    public:
+        int runTest(const char *fileName)
+        {
+            gmx::test::CommandLine caller;
+            caller.append("trjconv");
 
-TEST_F(GmxTrajTestNoFatalError, G96File)
-{
-    runTest("spc2-traj.g96");
-}
+            caller.addOption("-s", fileManager_.getInputFilePath("spc2.gro"));
+
+            std::string inputTrajectoryFileName = fileManager_.getInputFilePath(fileName);
+            caller.addOption("-f", inputTrajectoryFileName);
 
-// .g87 file reading has been broken since at least v4.5
-// proposed on gmx-developers for removing that support
-TEST_F(GmxTrajTestNoFatalError, DISABLED_G87File)
+            std::string ndxFileName = fileManager_.getInputFilePath("spc2.ndx");
+            caller.addOption("-n", ndxFileName);
+
+            caller.addOption("-o", fileManager_.getTemporaryFilePath("spc-traj.tng"));
+
+            redirectStringToStdin("SecondWaterMolecule\n");
+
+            /* TODO Ideally, we would then check that the output file
+               has only 3 of the 6 atoms (which it does), but the
+               infrastructure for doing that automatically is still
+               being built. This would also fix the TODO below. */
+            return gmx_trjconv(caller.argc(), caller.argv());
+        }
+};
+/* TODO These tests are actually not very effective, because trjconv
+ * can only return 0 or exit via gmx_fatal() (which currently also
+ * exits the test binary). So, "no XDR/TNG support in the binary"
+ * leads to the reading test appearing to pass. Still, no fatal error
+ * and no segfault is useful information while modifying such code. */
+
+TEST_P(TrjconvWithIndexGroupSubset, WithDifferentInputFormats)
 {
-    redirectStringToStdin("0\n4\n6\n0.0\n");
-    runTest("spc2-traj.g87");
+    runTest(GetParam());
 }
 
+// ==
+
+/*! \brief Helper array of input files present in the source repo
+ * database. These all have two identical frames of two SPC water
+ * molecules, which were generated via trjconv from the .gro
+ * version. */
+const char *trajectoryFileNames[] = {
+    "spc2-traj.trr",
+#ifdef GMX_USE_TNG
+    "spc2-traj.tng",
+#endif
+    "spc2-traj.xtc",
+    "spc2-traj.gro",
+    "spc2-traj.pdb",
+    "spc2-traj.g96"
+};
+// .g87 and .xyz file reading has been broken (and awkwardly
+// interactive) since at least v4.5 proposed on gmx-developers for
+// removing that support, so not testing it
+
+#ifdef __INTEL_COMPILER
+#pragma warning( disable : 177 )
+#endif
+
+INSTANTIATE_TEST_CASE_P(NoFatalErrorWhenWritingFrom,
+                        GmxTraj,
+                            ::testing::ValuesIn(gmx::ArrayRef<const char*>(trajectoryFileNames)));
+
+INSTANTIATE_TEST_CASE_P(NoFatalErrorWhenWritingFrom,
+                        TrjconvWithIndexGroupSubset,
+                            ::testing::ValuesIn(gmx::ArrayRef<const char*>(trajectoryFileNames)));
+
 } // namespace
diff --git a/src/gromacs/gmxana/legacytests/spc2-traj.tng b/src/gromacs/gmxana/legacytests/spc2-traj.tng
new file mode 100644 (file)
index 0000000..83b29e1
Binary files /dev/null and b/src/gromacs/gmxana/legacytests/spc2-traj.tng differ
diff --git a/src/gromacs/gmxana/legacytests/spc2.ndx b/src/gromacs/gmxana/legacytests/spc2.ndx
new file mode 100644 (file)
index 0000000..78e537a
--- /dev/null
@@ -0,0 +1,6 @@
+[ System ]
+   1    2    3    4    5   6
+[ FirstWaterMolecule ]
+   1    2    3
+[ SecondWaterMolecule ]
+   4    5    6
index 6b5f527dbf339eb061a87364ca62dc37553b67ab..f975f21468b43b43e7b05ad6ea6b07a36414f102 100644 (file)
@@ -144,7 +144,7 @@ const char *ecomb_names[eCOMB_NR+1] = {
 
 const char *gtypes[egcNR+1] = {
     "T-Coupling", "Energy Mon.", "Acceleration", "Freeze",
-    "User1", "User2", "VCM", "XTC", "Or. Res. Fit", "QMMM", NULL
+    "User1", "User2", "VCM", "Compressed X", "Or. Res. Fit", "QMMM", NULL
 };
 
 const char *esimtemp_names[esimtempNR+1] = {
index cf78a4f92026c896f34a5417cd9f9e9e6265392f..59331d43474125e6f2be428f3302307f25afa6b1 100644 (file)
@@ -298,6 +298,42 @@ void pr_rvecs(FILE *fp, int indent, const char *title, rvec vec[], int n)
 }
 
 
+void pr_rvecs_of_dim(FILE *fp, int indent, const char *title, rvec vec[], int n, int dim)
+{
+    const char *fshort = "%12.5e";
+    const char *flong  = "%15.8e";
+    const char *format;
+    int         i, j;
+
+    if (getenv("LONGFORMAT") != NULL)
+    {
+        format = flong;
+    }
+    else
+    {
+        format = fshort;
+    }
+
+    if (available(fp, vec, indent, title))
+    {
+        indent = pr_title_nxn(fp, indent, title, n, dim);
+        for (i = 0; i < n; i++)
+        {
+            (void) pr_indent(fp, indent);
+            (void) fprintf(fp, "%s[%5d]={", title, i);
+            for (j = 0; j < dim; j++)
+            {
+                if (j != 0)
+                {
+                    (void) fprintf(fp, ", ");
+                }
+                (void) fprintf(fp, format, vec[i][j]);
+            }
+            (void) fprintf(fp, "}\n");
+        }
+    }
+}
+
 void pr_reals(FILE *fp, int indent, const char *title, real *vec, int n)
 {
     int i;
@@ -330,6 +366,42 @@ void pr_doubles(FILE *fp, int indent, const char *title, double *vec, int n)
     }
 }
 
+void pr_reals_of_dim(FILE *fp, int indent, const char *title, real *vec, int n, int dim)
+{
+    int         i, j;
+    const char *fshort = "%12.5e";
+    const char *flong  = "%15.8e";
+    const char *format;
+
+    if (getenv("LONGFORMAT") != NULL)
+    {
+        format = flong;
+    }
+    else
+    {
+        format = fshort;
+    }
+
+    if (available(fp, vec, indent, title))
+    {
+        indent = pr_title_nxn(fp, indent, title, n, dim);
+        for (i = 0; i < n; i++)
+        {
+            (void) pr_indent(fp, indent);
+            (void) fprintf(fp, "%s[%5d]={", title, i);
+            for (j = 0; j < dim; j++)
+            {
+                if (j != 0)
+                {
+                    (void) fprintf(fp, ", ");
+                }
+                (void) fprintf(fp, format, vec[i * dim  + j]);
+            }
+            (void) fprintf(fp, "}\n");
+        }
+    }
+}
+
 static void pr_int(FILE *fp, int indent, const char *title, int i)
 {
     pr_indent(fp, indent);
@@ -763,11 +835,11 @@ void pr_inputrec(FILE *fp, int indent, const char *title, t_inputrec *ir,
         PI("nstfout", ir->nstfout);
         PI("nstcalcenergy", ir->nstcalcenergy);
         PI("nstenergy", ir->nstenergy);
-        PI("nstxtcout", ir->nstxtcout);
+        PI("nstxout_compressed", ir->nstxout_compressed);
         PR("init-t", ir->init_t);
         PR("delta-t", ir->delta_t);
 
-        PR("xtcprec", ir->xtcprec);
+        PR("x_compression_precision", ir->x_compression_precision);
         PR("fourierspacing", ir->fourier_spacing);
         PI("nkx", ir->nkx);
         PI("nky", ir->nky);
index f98500e1759c92f8e9c5da7708ec9f8ef51817ab..67ef12f210e639a35d5d8d60df09ea90bf43ce02 100644 (file)
@@ -66,12 +66,12 @@ double compute_io(t_inputrec *ir, int natoms, gmx_groups_t *groups,
     nstx   = div_nsteps(nsteps, ir->nstxout);
     nstv   = div_nsteps(nsteps, ir->nstvout);
     nstf   = div_nsteps(nsteps, ir->nstfout);
-    nstxtc = div_nsteps(nsteps, ir->nstxtcout);
-    if (ir->nstxtcout > 0)
+    nstxtc = div_nsteps(nsteps, ir->nstxout_compressed);
+    if (ir->nstxout_compressed > 0)
     {
         for (i = 0; i < natoms; i++)
         {
-            if (groups->grpnr[egcXTC] == NULL || groups->grpnr[egcXTC][i] == 0)
+            if (groups->grpnr[egcCompressedX] == NULL || groups->grpnr[egcCompressedX][i] == 0)
             {
                 nxtcatoms++;
             }
index 52a760ee1205e38266cf5656edf3ea9d533f9f82..8ee551f192ed275db90ba1cdd3a66ddbb2074718 100644 (file)
@@ -77,7 +77,7 @@ typedef struct t_inputrec_strings
 {
     char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN],
          acc[STRLEN], accgrps[STRLEN], freeze[STRLEN], frdim[STRLEN],
-         energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], xtc_grps[STRLEN],
+         energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN],
          couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN],
          wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN];
     char   fep_lambda[efptNR][STRLEN];
@@ -1747,6 +1747,9 @@ void get_ir(const char *mdparin, const char *mdparout,
     REPL_TYPE("unconstrained-start", "continuation");
     REPL_TYPE("foreign-lambda", "fep-lambdas");
     REPL_TYPE("verlet-buffer-drift", "verlet-buffer-tolerance");
+    REPL_TYPE("nstxtcout", "nstxout-compressed");
+    REPL_TYPE("xtc-grps", "compressed-x-grps");
+    REPL_TYPE("xtc-precision", "compressed-x-precision");
 
     CCTYPE ("VARIOUS PREPROCESSING OPTIONS");
     CTYPE ("Preprocessor information: use cpp syntax.");
@@ -1805,11 +1808,12 @@ void get_ir(const char *mdparin, const char *mdparout,
     ITYPE ("nstcalcenergy", ir->nstcalcenergy, 100);
     ITYPE ("nstenergy",   ir->nstenergy,  1000);
     CTYPE ("Output frequency and precision for .xtc file");
-    ITYPE ("nstxtcout",   ir->nstxtcout,  0);
-    RTYPE ("xtc-precision", ir->xtcprec,   1000.0);
-    CTYPE ("This selects the subset of atoms for the .xtc file. You can");
-    CTYPE ("select multiple groups. By default all atoms will be written.");
-    STYPE ("xtc-grps",    is->xtc_grps,       NULL);
+    ITYPE ("nstxout-compressed", ir->nstxout_compressed,  0);
+    RTYPE ("compressed-x-precision", ir->x_compression_precision, 1000.0);
+    CTYPE ("This selects the subset of atoms for the compressed");
+    CTYPE ("trajectory file. You can select multiple groups. By");
+    CTYPE ("default, all atoms will be written.");
+    STYPE ("compressed-x-grps", is->x_compressed_groups, NULL);
     CTYPE ("Selection of energy groups");
     STYPE ("energygrps",  is->energy,         NULL);
 
@@ -3320,8 +3324,8 @@ void do_index(const char* mdparin, const char *ndx,
     nuser = str_nelem(is->user2, MAXPTR, ptr1);
     do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser2,
                  restnm, egrptpALL_GENREST, bVerbose, wi);
-    nuser = str_nelem(is->xtc_grps, MAXPTR, ptr1);
-    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcXTC,
+    nuser = str_nelem(is->x_compressed_groups, MAXPTR, ptr1);
+    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcCompressedX,
                  restnm, egrptpONE, bVerbose, wi);
     nofg = str_nelem(is->orirefitgrp, MAXPTR, ptr1);
     do_numbering(natoms, groups, nofg, ptr1, grps, gnames, egcORFIT,
index 7148920ab56b6535d7e3b28f8a7b56334cf828e0..95597c0b199f293ff80bc0434c74c26ac6a55fe9 100644 (file)
@@ -40,7 +40,9 @@
 
 #include <stdio.h>
 #include "typedefs.h"
-#include "sim_util.h"
+#include "../math/gmxcomplex.h"
+#include "../timing/walltime_accounting.h"
+#include "../legacyheaders/network.h"
 
 #ifdef __cplusplus
 extern "C" {
index 8cda529c1242d1429a2fea3e2ffa424dc2c4b4c9..d515c7b19a62e4899535e103e0225ceedab1e78d 100644 (file)
@@ -118,7 +118,7 @@ void init_md(FILE *fplog,
              t_nrnb *nrnb, gmx_mtop_t *mtop,
              gmx_update_t *upd,
              int nfile, const t_filenm fnm[],
-             gmx_mdoutf_t **outf, t_mdebin **mdebin,
+             gmx_mdoutf_t *outf, t_mdebin **mdebin,
              tensor force_vir, tensor shake_vir,
              rvec mu_tot,
              gmx_bool *bSimAnn, t_vcm **vcm, unsigned long Flags);
index 6ac929f2543020dc9fc9daa67e6f5dd4af12c46b..ecf99a019fafc92a57ba0f8a0120910b8ce23774 100644 (file)
@@ -62,11 +62,13 @@ void pr_ivec(FILE *fp, int indent, const char *title, int vec[], int n, gmx_bool
 void pr_ivecs(FILE *fp, int indent, const char *title, ivec vec[], int n, gmx_bool bShowNumbers);
 void pr_bvec(FILE *fp, int indent, const char *title, gmx_bool vec[], int n, gmx_bool bShowNnumbers);
 void pr_rvec(FILE *fp, int indent, const char *title, real vec[], int n, gmx_bool bShowNumbers);
+void pr_rvecs_of_dim(FILE *fp, int indent, const char *title, rvec vec[], int n, int dim);
 void pr_dvec(FILE *fp, int indent, const char *title, double vec[], int n, gmx_bool bShowNumbers);
 void pr_rvecs(FILE *fp, int indent, const char *title, rvec vec[], int n);
 void pr_rvecs_len(FILE *fp, int indent, const char *title, rvec vec[], int n);
 void pr_reals(FILE *fp, int indent, const char *title, real vec[], int n);
 void pr_doubles(FILE *fp, int indent, const char *title, double *vec, int n);
+void pr_reals_of_dim(FILE *fp, int indent, const char *title, real *vec, int n, int dim);
 void pr_block(FILE *fp, int indent, const char *title, t_block *block, gmx_bool bShowNumbers);
 void pr_blocka(FILE *fp, int indent, const char *title, t_blocka *block, gmx_bool bShowNumbers);
 void pr_ilist(FILE *fp, int indent, const char *title,
index 54fa1dfaffab2ee3a0873746b68a43e8adcce8ef..b3746da2cacd98886ead20922ab4a6d099c3c320 100644 (file)
@@ -39,6 +39,7 @@
 
 
 #include "simple.h"
+#include "enums.h"
 #include "../sysstuff.h"
 
 #ifdef __cplusplus
@@ -291,131 +292,131 @@ typedef struct {
 } t_adress;
 
 typedef struct {
-    int             eI;                     /* Integration method                 */
-    gmx_int64_t     nsteps;                 /* number of steps to be taken                     */
-    int             simulation_part;        /* Used in checkpointing to separate chunks */
-    gmx_int64_t     init_step;              /* start at a stepcount >0 (used w. tpbconv)    */
-    int             nstcalcenergy;          /* frequency of energy calc. and T/P coupl. upd.   */
-    int             cutoff_scheme;          /* group or verlet cutoffs     */
-    int             ns_type;                /* which ns method should we use?               */
-    int             nstlist;                /* number of steps before pairlist is generated    */
-    int             ndelta;                 /* number of cells per rlong                       */
-    int             nstcomm;                /* number of steps after which center of mass      */
+    int             eI;                      /* Integration method                 */
+    gmx_int64_t     nsteps;                  /* number of steps to be taken                    */
+    int             simulation_part;         /* Used in checkpointing to separate chunks */
+    gmx_int64_t     init_step;               /* start at a stepcount >0 (used w. tpbconv)    */
+    int             nstcalcenergy;           /* frequency of energy calc. and T/P coupl. upd.  */
+    int             cutoff_scheme;           /* group or verlet cutoffs     */
+    int             ns_type;                 /* which ns method should we use?               */
+    int             nstlist;                 /* number of steps before pairlist is generated   */
+    int             ndelta;                  /* number of cells per rlong                      */
+    int             nstcomm;                 /* number of steps after which center of mass     */
     /* motion is removed                               */
-    int             comm_mode;              /* Center of mass motion removal algorithm      */
-    int             nstcheckpoint;          /* checkpointing frequency                      */
-    int             nstlog;                 /* number of steps after which print to logfile    */
-    int             nstxout;                /* number of steps after which X is output */
-    int             nstvout;                /* id. for V                                       */
-    int             nstfout;                /* id. for F                                       */
-    int             nstenergy;              /* number of steps after which energies printed */
-    int             nstxtcout;              /* id. for compressed trj (.xtc)           */
-    double          init_t;                 /* initial time (ps)              */
-    double          delta_t;                /* time step (ps)                          */
-    real            xtcprec;                /* precision of xtc file                        */
-    real            fourier_spacing;        /* requested fourier_spacing, when nk? not set  */
-    int             nkx, nky, nkz;          /* number of k vectors in each spatial dimension*/
-                                            /* for fourier methods for long range electrost.*/
-    int             pme_order;              /* interpolation order for PME                  */
-    real            ewald_rtol;             /* Real space tolerance for Ewald, determines   */
-                                            /* the real/reciprocal space relative weight    */
-    real            ewald_rtol_lj;          /* Real space tolerance for LJ-Ewald            */
-    int             ewald_geometry;         /* normal/3d ewald, or pseudo-2d LR corrections */
-    real            epsilon_surface;        /* Epsilon for PME dipole correction            */
-    gmx_bool        bOptFFT;                /* optimize the fft plan at start               */
-    int             ljpme_combination_rule; /* Type of combination rule in LJ-PME          */
-    int             ePBC;                   /* Type of periodic boundary conditions            */
-    int             bPeriodicMols;          /* Periodic molecules                           */
-    gmx_bool        bContinuation;          /* Continuation run: starting state is correct     */
-    int             etc;                    /* temperature coupling               */
-    int             nsttcouple;             /* interval in steps for temperature coupling   */
-    gmx_bool        bPrintNHChains;         /* whether to print nose-hoover chains        */
-    int             epc;                    /* pressure coupling                            */
-    int             epct;                   /* pressure coupling type                  */
-    int             nstpcouple;             /* interval in steps for pressure coupling      */
-    real            tau_p;                  /* pressure coupling time (ps)                     */
-    tensor          ref_p;                  /* reference pressure (kJ/(mol nm^3))              */
-    tensor          compress;               /* compressability ((mol nm^3)/kJ)        */
-    int             refcoord_scaling;       /* How to scale absolute reference coordinates  */
-    rvec            posres_com;             /* The COM of the posres atoms                  */
-    rvec            posres_comB;            /* The B-state COM of the posres atoms          */
-    int             andersen_seed;          /* Random seed for Andersen thermostat (obsolete) */
-    real            verletbuf_tol;          /* Per atom pair energy drift tolerance (kJ/mol/ps/atom) for list buffer  */
-    real            rlist;                  /* short range pairlist cut-off (nm)               */
-    real            rlistlong;              /* long range pairlist cut-off (nm)                */
-    int             nstcalclr;              /* Frequency of evaluating direct space long-range interactions */
-    real            rtpi;                   /* Radius for test particle insertion           */
-    int             coulombtype;            /* Type of electrostatics treatment             */
-    int             coulomb_modifier;       /* Modify the Coulomb interaction              */
-    real            rcoulomb_switch;        /* Coulomb switch range start (nm)         */
-    real            rcoulomb;               /* Coulomb cutoff (nm)                             */
-    real            epsilon_r;              /* relative dielectric constant                 */
-    real            epsilon_rf;             /* relative dielectric constant of the RF       */
-    int             implicit_solvent;       /* No (=explicit water), or GBSA solvent models */
-    int             gb_algorithm;           /* Algorithm to use for calculation Born radii  */
-    int             nstgbradii;             /* Frequency of updating Generalized Born radii */
-    real            rgbradii;               /* Cutoff for GB radii calculation              */
-    real            gb_saltconc;            /* Salt concentration (M) for GBSA models       */
-    real            gb_epsilon_solvent;     /* dielectric coeff. of implicit solvent     */
-    real            gb_obc_alpha;           /* 1st scaling factor for Bashford-Case GB      */
-    real            gb_obc_beta;            /* 2nd scaling factor for Bashford-Case GB      */
-    real            gb_obc_gamma;           /* 3rd scaling factor for Bashford-Case GB      */
-    real            gb_dielectric_offset;   /* Dielectric offset for Still/HCT/OBC     */
-    int             sa_algorithm;           /* Algorithm for SA part of GBSA                */
-    real            sa_surface_tension;     /* Energy factor for SA part of GBSA */
-    int             vdwtype;                /* Type of Van der Waals treatment              */
-    int             vdw_modifier;           /* Modify the VdW interaction                   */
-    real            rvdw_switch;            /* Van der Waals switch range start (nm)        */
-    real            rvdw;                   /* Van der Waals cutoff (nm)               */
-    int             eDispCorr;              /* Perform Long range dispersion corrections    */
-    real            tabext;                 /* Extension of the table beyond the cut-off,   *
-                                             * as well as the table length for 1-4 interac. */
-    real            shake_tol;              /* tolerance for shake                             */
-    int             efep;                   /* free energy calculations                     */
-    t_lambda       *fepvals;                /* Data for the FEP state                       */
-    gmx_bool        bSimTemp;               /* Whether to do simulated tempering            */
-    t_simtemp      *simtempvals;            /* Variables for simulated tempering            */
-    gmx_bool        bExpanded;              /* Whether expanded ensembles are used          */
-    t_expanded     *expandedvals;           /* Expanded ensemble parameters              */
-    int             eDisre;                 /* Type of distance restraining                 */
-    real            dr_fc;                  /* force constant for ta_disre                     */
-    int             eDisreWeighting;        /* type of weighting of pairs in one restraints    */
-    gmx_bool        bDisreMixed;            /* Use comb of time averaged and instan. viol's    */
-    int             nstdisreout;            /* frequency of writing pair distances to enx   */
-    real            dr_tau;                 /* time constant for memory function in disres    */
-    real            orires_fc;              /* force constant for orientational restraints  */
-    real            orires_tau;             /* time constant for memory function in orires    */
-    int             nstorireout;            /* frequency of writing tr(SD) to enx           */
-    real            dihre_fc;               /* force constant for dihedral restraints (obsolete)       */
-    real            em_stepsize;            /* The stepsize for updating                       */
-    real            em_tol;                 /* The tolerance                           */
-    int             niter;                  /* Number of iterations for convergence of      */
-                                            /* steepest descent in relax_shells             */
-    real            fc_stepsize;            /* Stepsize for directional minimization        */
-                                            /* in relax_shells                              */
-    int             nstcgsteep;             /* number of steps after which a steepest       */
-                                            /* descents step is done while doing cg         */
-    int             nbfgscorr;              /* Number of corrections to the hessian to keep */
-    int             eConstrAlg;             /* Type of constraint algorithm                 */
-    int             nProjOrder;             /* Order of the LINCS Projection Algorithm      */
-    real            LincsWarnAngle;         /* If bond rotates more than %g degrees, warn   */
-    int             nLincsIter;             /* Number of iterations in the final Lincs step */
-    gmx_bool        bShakeSOR;              /* Use successive overrelaxation for shake      */
-    real            bd_fric;                /* Friction coefficient for BD (amu/ps)         */
-    int             ld_seed;                /* Random seed for SD and BD                    */
-    int             nwall;                  /* The number of walls                          */
-    int             wall_type;              /* The type of walls                            */
-    real            wall_r_linpot;          /* The potentail is linear for r<=wall_r_linpot */
-    int             wall_atomtype[2];       /* The atom type for walls                      */
-    real            wall_density[2];        /* Number density for walls                     */
-    real            wall_ewald_zfac;        /* Scaling factor for the box for Ewald         */
-    int             ePull;                  /* Type of pulling: no, umbrella or constraint  */
-    t_pull         *pull;                   /* The data for center of mass pulling          */
-    gmx_bool        bRot;                   /* Calculate enforced rotation potential(s)?    */
-    t_rot          *rot;                    /* The data for enforced rotation potentials    */
-    real            cos_accel;              /* Acceleration for viscosity calculation       */
-    tensor          deform;                 /* Triclinic deformation velocities (nm/ps)     */
-    int             userint1;               /* User determined parameters                   */
+    int             comm_mode;               /* Center of mass motion removal algorithm      */
+    int             nstcheckpoint;           /* checkpointing frequency                      */
+    int             nstlog;                  /* number of steps after which print to logfile   */
+    int             nstxout;                 /* number of steps after which X is output        */
+    int             nstvout;                 /* id. for V                                      */
+    int             nstfout;                 /* id. for F                                      */
+    int             nstenergy;               /* number of steps after which energies printed */
+    int             nstxout_compressed;      /* id. for compressed trj (.xtc,.tng)           */
+    double          init_t;                  /* initial time (ps)              */
+    double          delta_t;                 /* time step (ps)                         */
+    real            x_compression_precision; /* precision of x in compressed trajectory file */
+    real            fourier_spacing;         /* requested fourier_spacing, when nk? not set  */
+    int             nkx, nky, nkz;           /* number of k vectors in each spatial dimension*/
+                                             /* for fourier methods for long range electrost.*/
+    int             pme_order;               /* interpolation order for PME                  */
+    real            ewald_rtol;              /* Real space tolerance for Ewald, determines   */
+                                             /* the real/reciprocal space relative weight    */
+    real            ewald_rtol_lj;           /* Real space tolerance for LJ-Ewald            */
+    int             ewald_geometry;          /* normal/3d ewald, or pseudo-2d LR corrections */
+    real            epsilon_surface;         /* Epsilon for PME dipole correction            */
+    gmx_bool        bOptFFT;                 /* optimize the fft plan at start               */
+    int             ljpme_combination_rule;  /* Type of combination rule in LJ-PME          */
+    int             ePBC;                    /* Type of periodic boundary conditions           */
+    int             bPeriodicMols;           /* Periodic molecules                           */
+    gmx_bool        bContinuation;           /* Continuation run: starting state is correct    */
+    int             etc;                     /* temperature coupling               */
+    int             nsttcouple;              /* interval in steps for temperature coupling   */
+    gmx_bool        bPrintNHChains;          /* whether to print nose-hoover chains        */
+    int             epc;                     /* pressure coupling                            */
+    int             epct;                    /* pressure coupling type                 */
+    int             nstpcouple;              /* interval in steps for pressure coupling      */
+    real            tau_p;                   /* pressure coupling time (ps)                    */
+    tensor          ref_p;                   /* reference pressure (kJ/(mol nm^3))             */
+    tensor          compress;                /* compressability ((mol nm^3)/kJ)        */
+    int             refcoord_scaling;        /* How to scale absolute reference coordinates  */
+    rvec            posres_com;              /* The COM of the posres atoms                  */
+    rvec            posres_comB;             /* The B-state COM of the posres atoms          */
+    int             andersen_seed;           /* Random seed for Andersen thermostat (obsolete) */
+    real            verletbuf_tol;           /* Per atom pair energy drift tolerance (kJ/mol/ps/atom) for list buffer  */
+    real            rlist;                   /* short range pairlist cut-off (nm)              */
+    real            rlistlong;               /* long range pairlist cut-off (nm)               */
+    int             nstcalclr;               /* Frequency of evaluating direct space long-range interactions */
+    real            rtpi;                    /* Radius for test particle insertion           */
+    int             coulombtype;             /* Type of electrostatics treatment             */
+    int             coulomb_modifier;        /* Modify the Coulomb interaction              */
+    real            rcoulomb_switch;         /* Coulomb switch range start (nm)                */
+    real            rcoulomb;                /* Coulomb cutoff (nm)                            */
+    real            epsilon_r;               /* relative dielectric constant                 */
+    real            epsilon_rf;              /* relative dielectric constant of the RF       */
+    int             implicit_solvent;        /* No (=explicit water), or GBSA solvent models */
+    int             gb_algorithm;            /* Algorithm to use for calculation Born radii  */
+    int             nstgbradii;              /* Frequency of updating Generalized Born radii */
+    real            rgbradii;                /* Cutoff for GB radii calculation              */
+    real            gb_saltconc;             /* Salt concentration (M) for GBSA models       */
+    real            gb_epsilon_solvent;      /* dielectric coeff. of implicit solvent     */
+    real            gb_obc_alpha;            /* 1st scaling factor for Bashford-Case GB      */
+    real            gb_obc_beta;             /* 2nd scaling factor for Bashford-Case GB      */
+    real            gb_obc_gamma;            /* 3rd scaling factor for Bashford-Case GB      */
+    real            gb_dielectric_offset;    /* Dielectric offset for Still/HCT/OBC     */
+    int             sa_algorithm;            /* Algorithm for SA part of GBSA                */
+    real            sa_surface_tension;      /* Energy factor for SA part of GBSA */
+    int             vdwtype;                 /* Type of Van der Waals treatment              */
+    int             vdw_modifier;            /* Modify the VdW interaction                   */
+    real            rvdw_switch;             /* Van der Waals switch range start (nm)        */
+    real            rvdw;                    /* Van der Waals cutoff (nm)              */
+    int             eDispCorr;               /* Perform Long range dispersion corrections    */
+    real            tabext;                  /* Extension of the table beyond the cut-off,   *
+                                              * as well as the table length for 1-4 interac. */
+    real            shake_tol;               /* tolerance for shake                            */
+    int             efep;                    /* free energy calculations                     */
+    t_lambda       *fepvals;                 /* Data for the FEP state                       */
+    gmx_bool        bSimTemp;                /* Whether to do simulated tempering            */
+    t_simtemp      *simtempvals;             /* Variables for simulated tempering            */
+    gmx_bool        bExpanded;               /* Whether expanded ensembles are used          */
+    t_expanded     *expandedvals;            /* Expanded ensemble parameters              */
+    int             eDisre;                  /* Type of distance restraining                 */
+    real            dr_fc;                   /* force constant for ta_disre                    */
+    int             eDisreWeighting;         /* type of weighting of pairs in one restraints   */
+    gmx_bool        bDisreMixed;             /* Use comb of time averaged and instan. viol's   */
+    int             nstdisreout;             /* frequency of writing pair distances to enx   */
+    real            dr_tau;                  /* time constant for memory function in disres    */
+    real            orires_fc;               /* force constant for orientational restraints  */
+    real            orires_tau;              /* time constant for memory function in orires    */
+    int             nstorireout;             /* frequency of writing tr(SD) to enx           */
+    real            dihre_fc;                /* force constant for dihedral restraints (obsolete)      */
+    real            em_stepsize;             /* The stepsize for updating                      */
+    real            em_tol;                  /* The tolerance                          */
+    int             niter;                   /* Number of iterations for convergence of      */
+                                             /* steepest descent in relax_shells             */
+    real            fc_stepsize;             /* Stepsize for directional minimization        */
+                                             /* in relax_shells                              */
+    int             nstcgsteep;              /* number of steps after which a steepest       */
+                                             /* descents step is done while doing cg         */
+    int             nbfgscorr;               /* Number of corrections to the hessian to keep */
+    int             eConstrAlg;              /* Type of constraint algorithm                 */
+    int             nProjOrder;              /* Order of the LINCS Projection Algorithm      */
+    real            LincsWarnAngle;          /* If bond rotates more than %g degrees, warn   */
+    int             nLincsIter;              /* Number of iterations in the final Lincs step */
+    gmx_bool        bShakeSOR;               /* Use successive overrelaxation for shake      */
+    real            bd_fric;                 /* Friction coefficient for BD (amu/ps)         */
+    int             ld_seed;                 /* Random seed for SD and BD                    */
+    int             nwall;                   /* The number of walls                          */
+    int             wall_type;               /* The type of walls                            */
+    real            wall_r_linpot;           /* The potentail is linear for r<=wall_r_linpot */
+    int             wall_atomtype[2];        /* The atom type for walls                      */
+    real            wall_density[2];         /* Number density for walls                     */
+    real            wall_ewald_zfac;         /* Scaling factor for the box for Ewald         */
+    int             ePull;                   /* Type of pulling: no, umbrella or constraint  */
+    t_pull         *pull;                    /* The data for center of mass pulling          */
+    gmx_bool        bRot;                    /* Calculate enforced rotation potential(s)?    */
+    t_rot          *rot;                     /* The data for enforced rotation potentials    */
+    real            cos_accel;               /* Acceleration for viscosity calculation       */
+    tensor          deform;                  /* Triclinic deformation velocities (nm/ps)     */
+    int             userint1;                /* User determined parameters                   */
     int             userint2;
     int             userint3;
     int             userint4;
index 6f3657228e98a37f3d2276af3e351d2c17ddd421..4ee71e582cd6d320f0c74e50ac61a46dce560d05 100644 (file)
@@ -34,6 +34,8 @@
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
+#ifndef GMX_LEGACYHEADERS_TOPOLOGY_H
+#define GMX_LEGACYHEADERS_TOPOLOGY_H
 
 #include "atoms.h"
 #include "idef.h"
@@ -47,7 +49,7 @@ extern "C" {
 
 enum {
     egcTC,    egcENER,   egcACC, egcFREEZE,
-    egcUser1, egcUser2,  egcVCM, egcXTC,
+    egcUser1, egcUser2,  egcVCM, egcCompressedX,
     egcORFIT, egcQMMM,
     egcNR
 };
@@ -125,3 +127,5 @@ typedef struct {
 #ifdef __cplusplus
 }
 #endif
+
+#endif
index d85905766b5d7ff6ed718c837682160a08a927b3..ed15e138ce0bfa670fe9f7b24976ef544f03152b 100644 (file)
@@ -314,7 +314,7 @@ void init_em(FILE *fplog, const char *title,
              t_graph **graph, t_mdatoms *mdatoms, gmx_global_stat_t *gstat,
              gmx_vsite_t *vsite, gmx_constr_t constr,
              int nfile, const t_filenm fnm[],
-             gmx_mdoutf_t **outf, t_mdebin **mdebin)
+             gmx_mdoutf_t *outf, t_mdebin **mdebin)
 {
     int  start, homenr, i;
     real dvdl_constr;
@@ -456,14 +456,14 @@ void init_em(FILE *fplog, const char *title,
     if (mdebin != NULL)
     {
         /* Init bin for energy stuff */
-        *mdebin = init_mdebin((*outf)->fp_ene, top_global, ir, NULL);
+        *mdebin = init_mdebin(mdoutf_get_fp_ene(*outf), top_global, ir, NULL);
     }
 
     clear_rvec(mu_tot);
     calc_shifts(ems->s.box, fr->shift_vec);
 }
 
-static void finish_em(t_commrec *cr, gmx_mdoutf_t *outf,
+static void finish_em(t_commrec *cr, gmx_mdoutf_t outf,
                       gmx_walltime_accounting_t walltime_accounting,
                       gmx_wallcycle_t wcycle)
 {
@@ -498,7 +498,7 @@ static void copy_em_coords(em_state_t *ems, t_state *state)
 }
 
 static void write_em_traj(FILE *fplog, t_commrec *cr,
-                          gmx_mdoutf_t *outf,
+                          gmx_mdoutf_t outf,
                           gmx_bool bX, gmx_bool bF, const char *confout,
                           gmx_mtop_t *top_global,
                           t_inputrec *ir, gmx_int64_t step,
@@ -522,9 +522,9 @@ static void write_em_traj(FILE *fplog, t_commrec *cr,
     {
         mdof_flags |= MDOF_F;
     }
-    write_traj(fplog, cr, outf, mdof_flags,
-               step, (double)step,
-               &state->s, state_global, state->f, f_global);
+    mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags,
+                                     top_global, step, (double)step,
+                                     &state->s, state_global, state->f, f_global);
 
     if (confout != NULL && MASTER(cr))
     {
@@ -992,7 +992,7 @@ double do_cg(FILE *fplog, t_commrec *cr,
     gmx_bool          do_log = FALSE, do_ene = FALSE, do_x, do_f;
     tensor            vir, pres;
     int               number_steps, neval = 0, nstcg = inputrec->nstcgsteep;
-    gmx_mdoutf_t     *outf;
+    gmx_mdoutf_t      outf;
     int               i, m, gf, step, nminstep;
     real              terminate = 0;
 
@@ -1043,7 +1043,7 @@ double do_cg(FILE *fplog, t_commrec *cr,
                    NULL, NULL, vir, pres, NULL, mu_tot, constr);
 
         print_ebin_header(fplog, step, step, s_min->s.lambda[efptFEP]);
-        print_ebin(outf->fp_ene, TRUE, FALSE, FALSE, fplog, step, step, eprNORMAL,
+        print_ebin(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step, step, eprNORMAL,
                    TRUE, mdebin, fcd, &(top_global->groups), &(inputrec->opts));
     }
     where();
@@ -1478,7 +1478,7 @@ double do_cg(FILE *fplog, t_commrec *cr,
             {
                 print_ebin_header(fplog, step, step, s_min->s.lambda[efptFEP]);
             }
-            print_ebin(outf->fp_ene, do_ene, FALSE, FALSE,
+            print_ebin(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
                        do_log ? fplog : NULL, step, step, eprNORMAL,
                        TRUE, mdebin, fcd, &(top_global->groups), &(inputrec->opts));
         }
@@ -1518,7 +1518,7 @@ double do_cg(FILE *fplog, t_commrec *cr,
         if (!do_ene || !do_log)
         {
             /* Write final energy file entries */
-            print_ebin(outf->fp_ene, !do_ene, FALSE, FALSE,
+            print_ebin(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
                        !do_log ? fplog : NULL, step, step, eprNORMAL,
                        TRUE, mdebin, fcd, &(top_global->groups), &(inputrec->opts));
         }
@@ -1607,7 +1607,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr,
     gmx_bool           do_log, do_ene, do_x, do_f, foundlower, *frozen;
     tensor             vir, pres;
     int                start, end, number_steps;
-    gmx_mdoutf_t      *outf;
+    gmx_mdoutf_t       outf;
     int                i, k, m, n, nfmax, gf, step;
     int                mdof_flags;
     /* not used */
@@ -1737,7 +1737,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr,
                    NULL, NULL, vir, pres, NULL, mu_tot, constr);
 
         print_ebin_header(fplog, step, step, state->lambda[efptFEP]);
-        print_ebin(outf->fp_ene, TRUE, FALSE, FALSE, fplog, step, step, eprNORMAL,
+        print_ebin(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step, step, eprNORMAL,
                    TRUE, mdebin, fcd, &(top_global->groups), &(inputrec->opts));
     }
     where();
@@ -1811,8 +1811,8 @@ double do_lbfgs(FILE *fplog, t_commrec *cr,
             mdof_flags |= MDOF_F;
         }
 
-        write_traj(fplog, cr, outf, mdof_flags,
-                   step, (real)step, state, state, f, f);
+        mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags,
+                                         top_global, step, (real)step, state, state, f, f);
 
         /* Do the linesearching in the direction dx[point][0..(n-1)] */
 
@@ -2267,7 +2267,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr,
             {
                 print_ebin_header(fplog, step, step, state->lambda[efptFEP]);
             }
-            print_ebin(outf->fp_ene, do_ene, FALSE, FALSE,
+            print_ebin(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
                        do_log ? fplog : NULL, step, step, eprNORMAL,
                        TRUE, mdebin, fcd, &(top_global->groups), &(inputrec->opts));
         }
@@ -2304,7 +2304,7 @@ double do_lbfgs(FILE *fplog, t_commrec *cr,
     }
     if (!do_ene || !do_log) /* Write final energy file entries */
     {
-        print_ebin(outf->fp_ene, !do_ene, FALSE, FALSE,
+        print_ebin(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
                    !do_log ? fplog : NULL, step, step, eprNORMAL,
                    TRUE, mdebin, fcd, &(top_global->groups), &(inputrec->opts));
     }
@@ -2377,7 +2377,7 @@ double do_steep(FILE *fplog, t_commrec *cr,
     t_graph          *graph;
     real              stepsize, constepsize;
     real              ustep, fnormn;
-    gmx_mdoutf_t     *outf;
+    gmx_mdoutf_t      outf;
     t_mdebin         *mdebin;
     gmx_bool          bDone, bAbort, do_x, do_f;
     tensor            vir, pres;
@@ -2472,7 +2472,7 @@ double do_steep(FILE *fplog, t_commrec *cr,
                 upd_mdebin(mdebin, FALSE, FALSE, (double)count,
                            mdatoms->tmass, enerd, &s_try->s, inputrec->fepvals, inputrec->expandedvals,
                            s_try->s.box, NULL, NULL, vir, pres, NULL, mu_tot, constr);
-                print_ebin(outf->fp_ene, TRUE,
+                print_ebin(mdoutf_get_fp_ene(outf), TRUE,
                            do_per_step(steps_accepted, inputrec->nstdisreout),
                            do_per_step(steps_accepted, inputrec->nstorireout),
                            fplog, count, count, eprNORMAL, TRUE,
@@ -2595,7 +2595,7 @@ double do_nm(FILE *fplog, t_commrec *cr,
              gmx_walltime_accounting_t walltime_accounting)
 {
     const char          *NM = "Normal Mode Analysis";
-    gmx_mdoutf_t        *outf;
+    gmx_mdoutf_t         outf;
     int                  natoms, atom, d;
     int                  nnodes, node;
     rvec                *f_global;
index b580f0c47b3d4fb4339080ed0d7940f3c68aa294..a06fae500db41631d1317db9fd62a16f6747c625 100644 (file)
@@ -2705,7 +2705,7 @@ void init_md(FILE *fplog,
              t_nrnb *nrnb, gmx_mtop_t *mtop,
              gmx_update_t *upd,
              int nfile, const t_filenm fnm[],
-             gmx_mdoutf_t **outf, t_mdebin **mdebin,
+             gmx_mdoutf_t *outf, t_mdebin **mdebin,
              tensor force_vir, tensor shake_vir, rvec mu_tot,
              gmx_bool *bSimAnn, t_vcm **vcm, unsigned long Flags)
 {
@@ -2761,8 +2761,8 @@ void init_md(FILE *fplog,
     {
         *outf = init_mdoutf(nfile, fnm, Flags, cr, ir, mtop, oenv);
 
-        *mdebin = init_mdebin((Flags & MD_APPENDFILES) ? NULL : (*outf)->fp_ene,
-                              mtop, ir, (*outf)->fp_dhdl);
+        *mdebin = init_mdebin((Flags & MD_APPENDFILES) ? NULL : mdoutf_get_fp_ene(*outf),
+                              mtop, ir, mdoutf_get_fp_dhdl(*outf));
     }
 
     if (ir->bAdress)
index 4552ec19bdd45475637ff5d7e4307e54294905cc..31e2cf84d7a35ee698915285c87e30fad86ec3a2 100644 (file)
@@ -763,10 +763,10 @@ static void cmp_inputrec(FILE *fp, t_inputrec *ir1, t_inputrec *ir2, real ftol,
     cmp_int(fp, "inputrec->nstfout", -1, ir1->nstfout, ir2->nstfout);
     cmp_int(fp, "inputrec->nstcalcenergy", -1, ir1->nstcalcenergy, ir2->nstcalcenergy);
     cmp_int(fp, "inputrec->nstenergy", -1, ir1->nstenergy, ir2->nstenergy);
-    cmp_int(fp, "inputrec->nstxtcout", -1, ir1->nstxtcout, ir2->nstxtcout);
+    cmp_int(fp, "inputrec->nstxout_compressed", -1, ir1->nstxout_compressed, ir2->nstxout_compressed);
     cmp_double(fp, "inputrec->init_t", -1, ir1->init_t, ir2->init_t, ftol, abstol);
     cmp_double(fp, "inputrec->delta_t", -1, ir1->delta_t, ir2->delta_t, ftol, abstol);
-    cmp_real(fp, "inputrec->xtcprec", -1, ir1->xtcprec, ir2->xtcprec, ftol, abstol);
+    cmp_real(fp, "inputrec->x_compression_precision", -1, ir1->x_compression_precision, ir2->x_compression_precision, ftol, abstol);
     cmp_real(fp, "inputrec->fourierspacing", -1, ir1->fourier_spacing, ir2->fourier_spacing, ftol, abstol);
     cmp_int(fp, "inputrec->nkx", -1, ir1->nkx, ir2->nkx);
     cmp_int(fp, "inputrec->nky", -1, ir1->nky, ir2->nky);
index f894def871dcb66efd593ac82148907e63ac4b79..731c81bc1c010e1247bfedf8352e17665d962660 100644 (file)
 #include "gromacs/fileio/tpxio.h"
 #include "gromacs/fileio/trnio.h"
 #include "gromacs/fileio/futil.h"
+#include "gromacs/fileio/tngio_for_tools.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #include "gromacs/linearalgebra/mtxio.h"
 #include "gromacs/linearalgebra/sparsematrix.h"
@@ -328,6 +333,60 @@ void list_xtc(const char *fn, gmx_bool bXVG)
     close_xtc(xd);
 }
 
+/* Callback used by list_tng_for_gmx_dump. */
+void list_tng_inner(const char *fn,
+                    gmx_bool bFirstFrame,
+                    gmx_bool bXVG,
+                    real *values,
+                    gmx_int64_t step, 
+                    double frame_time,
+                    gmx_int64_t n_values_per_frame,
+                    gmx_int64_t n_atoms,
+                    real prec,
+                    gmx_int64_t nframe,
+                    char *block_name)
+{
+    char                 buf[256];
+    int                  indent = 0;
+
+    if (bXVG)
+    {
+        gmx_int64_t j;
+        int         d;
+
+        if (bFirstFrame)
+        {
+            fprintf(stdout, "%g", (real)frame_time);
+        }
+        for (j = 0; j < n_atoms; j++)
+        {
+            for (d = 0; d < DIM; d++)
+            {
+                fprintf(stdout, " %g", values[j * DIM + d]);
+            }
+        }
+        fprintf(stdout, "\n");
+    }
+    else
+    {
+        if (bFirstFrame)
+        {
+            sprintf(buf, "%s frame %" GMX_PRId64, fn, nframe);
+            indent = 0;
+            indent = pr_title(stdout, indent, buf);
+            pr_indent(stdout, indent);
+            fprintf(stdout, "natoms=%10" GMX_PRId64 "  step=%10" GMX_PRId64 "  time=%12.7e",
+                    n_atoms, step, frame_time);
+            if (prec > 0)
+            {
+                fprintf(stdout, "  prec=%10g", prec);
+            }
+            fprintf(stdout, "\n");
+        }
+        pr_reals_of_dim(stdout, indent, block_name, values, n_atoms, n_values_per_frame);
+    }
+}
+
 void list_trx(const char *fn, gmx_bool bXVG)
 {
     int ftp;
@@ -341,6 +400,10 @@ void list_trx(const char *fn, gmx_bool bXVG)
     {
         list_trn(fn);
     }
+    else if (ftp == efTNG)
+    {
+        list_tng_for_gmx_dump(fn, bXVG);
+    }
     else
     {
         fprintf(stderr, "File %s is of an unsupported type. Try using the command\n 'less %s'\n",
index cf0195e1e3f3379586942d8bfa602983a0a0e100..8db58a8efcfb66409e7ee9bc18c9bd784961f963 100644 (file)
@@ -35,6 +35,8 @@
 #ifndef GMX_TOOLS_DUMP_H
 #define GMX_TOOLS_DUMP_H
 
+#include "gromacs/legacyheaders/types/simple.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -49,6 +51,22 @@ extern "C" {
  */
 int gmx_dump(int argc, char *argv[]);
 
+/*! \brief Callback used by list_tng_for_gmx_dump.
+ *
+ * This keeps TNG-related stuff in a TNG-related file, and
+ * dumping-related stuff in the dumping file. */
+void list_tng_inner(const char *fn,
+                    gmx_bool bFirstFrame,
+                    gmx_bool bXVG,
+                    real *values,
+                    gmx_int64_t step, 
+                    double frame_time,
+                    gmx_int64_t n_values_per_frame,
+                    gmx_int64_t n_atoms,
+                    real prec,
+                    gmx_int64_t nframe,
+                    char *block_name);
+
 #ifdef __cplusplus
 }
 #endif
index df64572da6bc942776ab043d972072e6e896b739..6c0435ff591406796fa0938e3b72640416359d68 100644 (file)
@@ -148,7 +148,7 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
              unsigned long Flags,
              gmx_walltime_accounting_t walltime_accounting)
 {
-    gmx_mdoutf_t   *outf;
+    gmx_mdoutf_t    outf = NULL;
     gmx_int64_t     step, step_rel;
     double          elapsed_time;
     double          t, t0, lam0[efptNR];
@@ -285,7 +285,7 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
 
     if (bRerunMD)
     {
-        ir->nstxtcout = 0;
+        ir->nstxout_compressed = 0;
     }
     groups = &top_global->groups;
 
@@ -1079,7 +1079,7 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
                                         nrnb, wcycle, graph, groups,
                                         shellfc, fr, bBornRadii, t, mu_tot,
                                         &bConverged, vsite,
-                                        outf->fp_field);
+                                        mdoutf_get_fp_field(outf));
             tcount += count;
 
             if (bConverged)
@@ -1098,7 +1098,7 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
                      state->box, state->x, &state->hist,
                      f, force_vir, mdatoms, enerd, fcd,
                      state->lambda, graph,
-                     fr, vsite, mu_tot, t, outf->fp_field, ed, bBornRadii,
+                     fr, vsite, mu_tot, t, mdoutf_get_fp_field(outf), ed, bBornRadii,
                      (bNS ? GMX_FORCE_NS : 0) | force_flags);
         }
 
@@ -1323,12 +1323,12 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
          * coordinates at time t. We must output all of this before
          * the update.
          */
-        do_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t,
-                              ir, state, state_global, top_global, fr, upd,
-                              outf, mdebin, ekind, f, f_global,
-                              wcycle, mcrng, &nchkpt,
-                              bCPT, bRerunMD, bLastStep, (Flags & MD_CONFOUT),
-                              bSumEkinhOld);
+        do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t,
+                                 ir, state, state_global, top_global, fr, upd,
+                                 outf, mdebin, ekind, f, f_global,
+                                 wcycle, mcrng, &nchkpt,
+                                 bCPT, bRerunMD, bLastStep, (Flags & MD_CONFOUT),
+                                 bSumEkinhOld);
 
         /* kludge -- virial is lost with restart for NPT control. Must restart */
         if (bStartingFromCpt && bVV)
@@ -1759,7 +1759,7 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
                 do_dr  = do_per_step(step, ir->nstdisreout);
                 do_or  = do_per_step(step, ir->nstorireout);
 
-                print_ebin(outf->fp_ene, do_ene, do_dr, do_or, do_log ? fplog : NULL,
+                print_ebin(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or, do_log ? fplog : NULL,
                            step, t,
                            eprNORMAL, bCompact, mdebin, fcd, groups, &(ir->opts));
             }
@@ -1952,13 +1952,12 @@ double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[],
     {
         if (ir->nstcalcenergy > 0 && !bRerunMD)
         {
-            print_ebin(outf->fp_ene, FALSE, FALSE, FALSE, fplog, step, t,
+            print_ebin(mdoutf_get_fp_ene(outf), FALSE, FALSE, FALSE, fplog, step, t,
                        eprAVER, FALSE, mdebin, fcd, groups, &(ir->opts));
         }
     }
 
     done_mdoutf(outf);
-
     debug_gmx();
 
     if (ir->nstlist == -1 && nlh.nns > 0 && fplog)
index ad60cbe66ae3be182b949365051905ee6575b306..ec2f19d340705165c3a78098419404675ca9e438 100644 (file)
@@ -382,7 +382,7 @@ int gmx_mdrun(int argc, char *argv[])
     t_filenm      fnm[] = {
         { efTPX, NULL,      NULL,       ffREAD },
         { efTRN, "-o",      NULL,       ffWRITE },
-        { efXTC, "-x",      NULL,       ffOPTWR },
+        { efCOMPRESSED, "-x", NULL,     ffOPTWR },
         { efCPT, "-cpi",    NULL,       ffOPTRD },
         { efCPT, "-cpo",    NULL,       ffOPTWR },
         { efSTO, "-c",      "confout",  ffWRITE },
index 53b52a267422b508629a8d106fbed104537c5036..fe813a15d0fbfc9260fe83b0c31db10553db49e4 100644 (file)
@@ -42,7 +42,8 @@ gmx_build_unit_test(
     ${exename}
     # files with code for tests
     rerun.cpp
-    xtc_output.cpp
+    trajectory_writing.cpp
+    compressed_x_output.cpp
     # files with code for test fixtures
     moduletest.cpp
     # pseudo libraries for code for grompp and mdrun
similarity index 81%
rename from src/programs/mdrun/tests/xtc_output.cpp
rename to src/programs/mdrun/tests/compressed_x_output.cpp
index 667681e7ebce92b3e1ae27047eb9e39d5e7de3b8..2b1204996845862a6a7423e747b6511359a66583 100644 (file)
@@ -51,28 +51,28 @@ namespace
 {
 
 //! Test fixture for mdrun -x
-class XtcOutputTest : public gmx::test::MdrunTestFixture,
+class CompressedXOutputTest : public gmx::test::MdrunTestFixture,
                       public testing::WithParamInterface<const char*>
 {
 };
 
 /* Among other things, this test ensures mdrun can write a compressed trajectory. */
-TEST_P(XtcOutputTest, ExitsNormally)
+TEST_P(CompressedXOutputTest, ExitsNormally)
 {
     std::string mdpFile("cutoff-scheme = Group\n"
                         "nsteps = 1\n"
-                        "nstxtcout = 1\n");
+                        "nstxout-compressed = 1\n");
     mdpFile += GetParam();
     useStringAsMdpFile(mdpFile.c_str());
     useTopGroAndNdxFromDatabase("spc2");
     ASSERT_EQ(0, callGrompp());
 
-    xtcFileName = fileManager_.getTemporaryFilePath(".xtc");
+    reducedPrecisionTrajectoryFileName = fileManager_.getTemporaryFilePath(".xtc");
     ASSERT_EQ(0, callMdrun());
 
     ::gmx::test::CommandLine checkCaller;
     checkCaller.append("check");
-    checkCaller.addOption("-f", xtcFileName);
+    checkCaller.addOption("-f", reducedPrecisionTrajectoryFileName);
     ASSERT_EQ(0, gmx_check(checkCaller.argc(), checkCaller.argv()));
 }
 
@@ -85,21 +85,21 @@ TEST_P(XtcOutputTest, ExitsNormally)
 #pragma warning( disable : 177 )
 #endif
 
-INSTANTIATE_TEST_CASE_P(WithDifferentMdpOptions, XtcOutputTest,
+INSTANTIATE_TEST_CASE_P(WithDifferentMdpOptions, CompressedXOutputTest,
                             ::testing::Values
-                            ( // Test writing the whole system to XTC via
+                            ( // Test writing the whole system via
                               // the default behaviour
                             "",
 
-                            // Test writing the whole system to XTC
+                            // Test writing the whole system
                             // explicitly
-                            "xtc-grps = System\n",
+                            "compressed-x-grps = System\n",
 
-                            // Test writing part of the system to XTC.
+                            // Test writing only part of the system.
                             // It would be nice to check that this test
                             // writes 3 atoms and the others write 6, but
-                            // that's not yet easy
-                            "xtc-grps = SecondWaterMolecule\n"
+                            // that's not yet easy.
+                            "compressed-x-grps = SecondWaterMolecule\n"
                             ));
 
 } // namespace
index ffa29f3dfb236f5b63f96cf3d2f765f8fbbc4d26..91320d5f20871b05a89291e2d265a1b9efdee0b9 100644 (file)
@@ -92,7 +92,7 @@ GMX_TEST_OPTIONS(MdrunTestOptions, options)
 MdrunTestFixture::MdrunTestFixture() :
     topFileName(),
     groFileName(),
-    trrFileName(),
+    fullPrecisionTrajectoryFileName(),
     ndxFileName(),
     mdpInputFileName(fileManager_.getTemporaryFilePath("input.mdp")),
     mdpOutputFileName(fileManager_.getTemporaryFilePath("output.mdp")),
@@ -121,6 +121,12 @@ MdrunTestFixture::useEmptyMdpFile()
 
 void
 MdrunTestFixture::useStringAsMdpFile(const char *mdpString)
+{
+    useStringAsMdpFile(std::string(mdpString));
+}
+
+void
+MdrunTestFixture::useStringAsMdpFile(const std::string &mdpString)
 {
     gmx::File::writeFileFromString(mdpInputFileName, mdpString);
 }
@@ -175,8 +181,10 @@ MdrunTestFixture::callMdrun(const CommandLine &callerRef)
 
     caller.addOption("-g", logFileName);
     caller.addOption("-e", edrFileName);
-    caller.addOption("-o", trrFileName);
-    caller.addOption("-x", xtcFileName);
+    caller.addOption("-o", fullPrecisionTrajectoryFileName);
+    caller.addOption("-x", reducedPrecisionTrajectoryFileName);
+
+    caller.addOption("-deffnm", fileManager_.getTemporaryFilePath("state"));
 
 #ifdef GMX_THREAD_MPI
     caller.addOption("-nt", numThreads);
index f049e414193a8f43e94843f253681506ed9b34d8..4bcf10e0f14fb208bc26a0a7abc625383e76e89e 100644 (file)
@@ -91,6 +91,8 @@ class MdrunTestFixture : public IntegrationTestFixture
         void useEmptyMdpFile();
         //! Use a string as -f input to grompp
         void useStringAsMdpFile(const char *mdpString);
+        //! Use a string as -f input to grompp
+        void useStringAsMdpFile(const std::string &mdpString);
         //! Use a string as -n input to grompp
         void useStringAsNdxFile(const char *ndxString);
         //! Use a standard .top and .gro file as input to grompp
@@ -118,8 +120,8 @@ class MdrunTestFixture : public IntegrationTestFixture
          */
         std::string topFileName;
         std::string groFileName;
-        std::string trrFileName;
-        std::string xtcFileName;
+        std::string fullPrecisionTrajectoryFileName;
+        std::string reducedPrecisionTrajectoryFileName;
         std::string ndxFileName;
         std::string mdpInputFileName;
         std::string mdpOutputFileName;
index 68573f0a7dd1e2efa700dfbc76744209fc8dd6d2..8ba5d6b559fbc47cd72165e797abaa995a28b4ac 100644 (file)
@@ -49,16 +49,19 @@ namespace
 {
 
 //! Test fixture for mdrun -rerun
-typedef gmx::test::MdrunTestFixture RerunTest;
+class MdrunRerun : public gmx::test::MdrunTestFixture,
+                   public ::testing::WithParamInterface<const char *>
+{
+};
 
 /* Among other things, this test ensures mdrun can read a trajectory. */
-TEST_F(RerunTest, RerunExitsNormally)
+TEST_P(MdrunRerun, WithDifferentInputFormats)
 {
     useEmptyMdpFile();
     useTopGroAndNdxFromDatabase("spc2");
     EXPECT_EQ(0, callGrompp());
 
-    std::string rerunFileName = fileManager_.getInputFilePath("spc2.trr");
+    std::string rerunFileName = fileManager_.getInputFilePath(GetParam());
 
     ::gmx::test::CommandLine rerunCaller;
     rerunCaller.append("mdrun");
@@ -66,6 +69,31 @@ TEST_F(RerunTest, RerunExitsNormally)
     ASSERT_EQ(0, callMdrun(rerunCaller));
 }
 
+/*! \brief Helper array of input files present in the source repo
+ * database. These all have two identical frames of two SPC water
+ * molecules, which were generated via trjconv from the .gro
+ * version. */
+const char *trajectoryFileNames[] = {
+    "../../../gromacs/gmxana/legacytests/spc2-traj.trr",
+#ifdef GMX_USE_TNG
+    "../../../gromacs/gmxana/legacytests/spc2-traj.tng",
+#endif
+    "../../../gromacs/gmxana/legacytests/spc2-traj.xtc",
+    "../../../gromacs/gmxana/legacytests/spc2-traj.gro",
+    "../../../gromacs/gmxana/legacytests/spc2-traj.pdb",
+    "../../../gromacs/gmxana/legacytests/spc2-traj.g96"
+};
+// TODO later. Find a better way to manage this file database and
+// these string arrays that index it
+
+#ifdef __INTEL_COMPILER
+#pragma warning( disable : 177 )
+#endif
+
+INSTANTIATE_TEST_CASE_P(NoFatalErrorFrom,
+                        MdrunRerun,
+                        ::testing::ValuesIn(gmx::ArrayRef<const char*>(trajectoryFileNames)));
+
 /*! \todo Add other tests for mdrun -rerun, e.g.
  *
  * - RerunReproducesRunWhenRunOnlyWroteEnergiesOnNeighborSearchSteps
diff --git a/src/programs/mdrun/tests/spc-and-methanol.gro b/src/programs/mdrun/tests/spc-and-methanol.gro
new file mode 100644 (file)
index 0000000..4e937af
--- /dev/null
@@ -0,0 +1,9 @@
+
+ 6
+    1MeOH   Me1    1   1.970   1.460   1.209 -0.8587 -0.1344 -0.0643
+    1MeOH    O2    2   1.978   1.415   1.082  0.0623 -0.1787  0.0036
+    1MeOH    H3    3   1.905   1.460   1.030 -0.5020 -0.9564  0.0997
+    2SOL     OW    4   1.555   1.511   0.703   0.869   1.245   1.665
+    2SOL    HW1    5   1.498   1.495   0.784   0.169   0.275   1.565
+    2SOL    HW2    6   1.496   1.521   0.623   0.269   2.275   1.465
+   3.01000   3.01000   3.01000
diff --git a/src/programs/mdrun/tests/spc-and-methanol.ndx b/src/programs/mdrun/tests/spc-and-methanol.ndx
new file mode 100644 (file)
index 0000000..d59ec6d
--- /dev/null
@@ -0,0 +1,6 @@
+[ System ]
+   1    2    3    4    5   6
+[ Methanol ]
+   1    2    3
+[ SOL ]
+   4    5    6
diff --git a/src/programs/mdrun/tests/spc-and-methanol.top b/src/programs/mdrun/tests/spc-and-methanol.top
new file mode 100644 (file)
index 0000000..7f78ad6
--- /dev/null
@@ -0,0 +1,12 @@
+#include "gromos43a1.ff/forcefield.itp"
+#include "gromos43a1.ff/methanol.itp"
+#include "gromos43a1.ff/spc.itp"
+
+[ system ]
+; Name
+spc-and-methanol
+
+[ molecules ]
+; Compound  #mols
+Methanol    1
+SOL         1
diff --git a/src/programs/mdrun/tests/trajectory_writing.cpp b/src/programs/mdrun/tests/trajectory_writing.cpp
new file mode 100644 (file)
index 0000000..f0eef1b
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief
+ * Tests for the .mdp nst*out functionality
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdrun
+ */
+#include <gtest/gtest.h>
+#include "gromacs/legacyheaders/string2.h"
+#include "moduletest.h"
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/utility/stringutil.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+namespace
+{
+
+// TODO configure these tests so they test all formats for mdrun trajectory writing
+#ifdef GMX_USE_TNG
+
+//! Test fixture for mdrun trajectory writing
+class TrajectoryWritingTest :
+    public gmx::test::MdrunTestFixture,
+    public ::testing::WithParamInterface<const char *>
+{
+    public:
+        //! The file name of the MDP file
+        std::string theMdpFile;
+
+        //! Execute the trajectory writing test
+        void runTest()
+        {
+            useStringAsMdpFile(theMdpFile);
+            useTopGroAndNdxFromDatabase("spc-and-methanol");
+            EXPECT_EQ(0, callGrompp());
+
+            fullPrecisionTrajectoryFileName    = fileManager_.getTemporaryFilePath("spc-and-methanol.tng");
+            reducedPrecisionTrajectoryFileName = fileManager_.getTemporaryFilePath("spc-and-methanol-reduced.tng");
+            ASSERT_EQ(0, callMdrun());
+            // TODO When there is a way to sense something like the
+            // output of gmx check, compare the result with that from
+            // writing .trr and .xtc and assert the behaviour is
+            // correct. Note that TNG will always write the box, even
+            // when constant - this will be a source of
+            // trajectory-file differences.
+        }
+};
+
+//! Helper typedef for naming test cases like sentences
+typedef TrajectoryWritingTest Trajectories;
+
+/* This test ensures mdrun can write various quantities at various
+   frequencies */
+TEST_P(Trajectories, ThatDifferInNstxout)
+{
+    theMdpFile = gmx::formatString("integrator = md\n"
+                                   "nsteps = 6\n"
+                                   "nstxout = %s\n"
+                                   "nstvout = 2\n"
+                                   "nstfout = 4\n"
+                                   "nstxout-compressed = 5\n"
+                                   "tcoupl = v-rescale\n"
+                                   "tc-grps = System\n"
+                                   "tau-t = 1\n"
+                                   "ref-t = 298\n"
+                                   "compressed-x-grps = Sol\n",
+                                   GetParam());
+    runTest();
+}
+
+//! Helper typedef for naming test cases like sentences
+typedef TrajectoryWritingTest NptTrajectories;
+
+/* This test ensures mdrun can write trajectories in TNG format from NPT ensembles. */
+TEST_P(NptTrajectories, WithDifferentPcoupl)
+{
+    theMdpFile = gmx::formatString("integrator = md\n"
+                                   "nsteps = 2\n"
+                                   "nstxout = 2\n"
+                                   "nstvout = 1\n"
+                                   "pcoupl = %s\n"
+                                   "tau-p = 1\n"
+                                   "ref-p = 1\n"
+                                   "compressibility = 4.5e-5\n"
+                                   "tcoupl = v-rescale\n"
+                                   "tc-grps = System\n"
+                                   "tau-t = 1\n"
+                                   "ref-t = 298\n",
+                                   GetParam());
+    runTest();
+}
+
+#ifdef __INTEL_COMPILER
+#pragma warning( disable : 177 )
+#endif
+
+// TODO Consider spamming more of the parameter space when we don't
+// have to write .mdp and .tpr files to do it.
+INSTANTIATE_TEST_CASE_P(CanWrite,
+                        Trajectories,
+                            ::testing::Values("1", "2", "3"));
+
+INSTANTIATE_TEST_CASE_P(CanWrite,
+                        NptTrajectories,
+                            ::testing::Values("no", "Berendsen", "Parrinello-Rahman"));
+
+#endif
+
+} // namespace