Examples
TNG files can be read using the TNGFileIterator
class as a file handle,
which supports use as a context manager.
The TNGFileIterator has attributes related to the trajectory metadata, such as the number of integrator steps, the number of steps with data, the block_ids available at each step, and the stride at which each block is written.
The TNGFileIterator returns one frame at a time, which is accessed from the
TNGFileIterator.current_integrator_step
attribute or as part of any
slicing or indexing operation. A NumPy array of the right size and datatype must then be
provided to a getter method for the data to be read into. The required datatype is dictated by what type of block is being read.
Supported datatypes are:
TNG_INT_DATA : np.int64
TNG_FLOAT_DATA : np.float32
TNG_DOUBLE_DATA : np.float64
Helper methods are provided to create np.ndarray
instances of the right shape
and datatype for a particular block in TNGFileIterator.make_ndarray_for_block_from_name
and TNGFileIterator.make_ndarray_for_block_from_name
.
An example of how to read positions and box vectors from a TNG file is shown below:
import pytng
import numpy as np
with pytng.TNGFileIterator("traj.tng", 'r') as tng:
# make a numpy array to hold the data using helper function
# this array will then be updated in-place
positions = tng.make_ndarray_for_block_from_name("TNG_TRAJ_POSITIONS")
box_vec = tng.make_ndarray_for_block_from_name("TNG_TRAJ_BOX_SHAPE")
# the TNG API uses regular strides for data deposition, here we check
# that the strides for positions and box_vectors are the same
# and then iterate over all timesteps with this data
# len(tng) is the total number of steps in the file
assert (tng.block_strides["TNG_TRAJ_POSITIONS"] == tng.block_strides["TNG_TRAJ_BOX_SHAPE"])
for ts in tng[0:len(tng):tng.block_strides["TNG_TRAJ_POSITIONS"]]:
# read the integrator timestep, modifying the current_integrator_step
# which is returned as ts
# get the data from the requested block by supplying NumPy array which
# is updated in-place or returned
# update in place or return by value
ts.get_positions(positions)
# positions = ts.get_positions(positions) is equivalent
# you can check if the last read was successful (contained data) easily
if not ts.read_success:
raise IOError("No position data at this timestep")
ts.get_box(box_vec)
# box_vec = ts.get_box(box_vec) is equivalent
# you can check if the last read was successful (contained data) easily
if not ts.read_success:
raise IOError("No box data at this timestep")
It is also possible to slice and index the file object to select particular frames individually:
import pytng
import numpy as np
with pytng.TNGFileIterator('traj.tng', 'r') as tng:
positions = tng.make_ndarray_for_block_from_name("TNG_TRAJ_POSITIONS")
box_vec = tng.make_ndarray_for_block_from_name("TNG_TRAJ_BOX_SHAPE")
positions = tng[100].get_positions(positions)
box_vec = tng[200].get_box(box_vec)
If the step to read is not on the stride of the requested datatype, the NumPy array will be returned filled with np.nan. A contrived example of this is given below:
import pytng
import numpy as np
with pytng.TNGFileIterator("traj.tng", 'r') as tng:
# make array for positions
positions = tng.make_ndarray_for_block_from_name("TNG_TRAJ_POSITIONS")
# choose a step
step = 42
# check that we are off stride (stride%step != 0)
assert(tng.block_strides["TNG_TRAJ_POSITIONS"]%step != 0 )
# slice a single timestep
ts = tng[step]
# get the data, which will be returned full of np.nan
ts.get_positions(positions)
# the read_success property will indicate that there was no data found
# when the getter was called
assert(ts.read_success == False)
# but we can also double check that the read was blank
is_blank_read = np.all(np.isnan(positions)) # this will be true
if is_blank_read:
print("This is a blank read")
Available data blocks are listed at the end of this documentation. Common blocks for which there are getter methods include:
positions :
TNGFileIterator.get_positions
box vectors :
TNGFileIterator.get_box
forces :
TNGFileIterator.get_forces
velocities :
TNGFileIterator.get_velocities
Other blocks can be accessed using the TNGCurrentIntegratorStep.get_blockid
method, where the block id needs to be supplied and can be accessed from the
TNGFileIterator.block_ids
attribute. An example of this is shown below:
import pytng
import numpy as np
with pytng.TNGFileIterator("traj.tng", 'r') as tng:
# make array for the GMX potential energy block
Epot = tng.make_ndarray_for_block_from_name("TNG_GMX_ENERGY_POTENTIAL")
# get the block id for the GMX potential energy block
Epot_block_id = tng.block_ids["TNG_GMX_ENERGY_POTENTIAL"]
# get the block data for frame 0 with get_blockid
tng[0].get_blockid[Epot_block_id]