Quickstart

Reading and writing of trajectories

Reading and writing with automatic filetype detection

The garnett.read() and garnett.write() functions will automatically determine the type of a trajectory from its file extension. This can be used to quickly load and save Trajectory objects.

import garnett
# Load a GSD file...
with garnett.read('dump.gsd') as traj:
    print(len(traj))
    # ...do things with the trajectory, then output a GTAR file
    garnett.write(traj, 'output.tar')

Using reader and writer classes

Readers and writers are defined in the reader and writer modules. The following code uses the PosFileReader and PosFileWriter as an example.

from garnett.reader import PosFileReader
from garnett.writer import PosFileWriter

pos_reader = PosFileReader()
pos_writer = PosFileWriter()

with open('posfile.pos') as file:
    # Access the trajectory
    traj = pos_reader.read(file)

    # Write to standard out:
    pos_writer.write(traj)

    # or directly to a file:
    with open('out.pos', 'w') as posfile:
        pos_writer.write(traj, posfile)

Data access

Indexing and slicing

Once you read a trajectory, access individual frames or sub-trajectories by indexing and slicing:

# Select individual frames:
first_frame = traj[0]
last_frame = traj[-1]
n_th_frame = traj[n]
# and so on

# Create a sub-trajectory from the ith frame
# to the (j-1)th frame:
sub_trajectory = traj[i:j]

# We can use advanced slicing techniques:
every_second_frame = traj[::2]
the_last_ten_frames = traj[-10::]

The actual trajectory data is then either accessed on a per trajectory or per frame basis.

Trajectory array access

Access positions, orientations and types as coherent numpy arrays, by calling the load_arrays() method. This method will load the complete trajectory into memory and make positions, orientations and types available via properties:

traj.load_arrays()
traj.N               # M
traj.positions       # MxNx3
traj.orientations    # MxNx4
traj.velocities      # MxNx3
traj.mass            # MxN
traj.charge          # MxN
traj.diameter        # MxN
traj.moment_inertia  # MxNx3
traj.angmom          # MxNx4
traj.image           # MxNx4
traj.types           # MxN
traj.type_ids        # MxN
traj.type            # list of type names ordered by type_id

# where M=len(traj) is the number of frames and N=max((len(f) for f in traj))
# is the is the maximum number of particles in any frame.

Individual frame access

Inidividual frame objects can be accessed via indexing of a (sub-)trajectory object:

frame = traj[i]
frame.box              # garnett.trajectory.Box object
frame.types            # N
frame.positions        # Nx3
frame.orientations     # Nx4
frame.velocities       # Nx3
frame.mass             # N
frame.charge           # N
frame.diameter         # N
frame.moment_inertia   # Nx3
frame.angmom           # Nx4
frame.data             # A dictionary of lists for each attribute
frame.data_key         # A list of strings
frame.shapedef         # A ordered dictionary of instances of ShapeDefinition

Iterating over trajectories

Iterating over trajectories is the most memory-efficient form of data access. Each frame will be loaded prior to access and unloaded post access, such that there is only one frame loaded into memory at the same time.

# Iterate over a trajectory directly for read-only data access
for frame in traj:
    print(frame.positions)

Efficient modification of trajectories

Use a combination of reading, writing, and iteration for memory-efficient modification of large trajectory data. This is an example on how to modify frames in-place:

import numpy as np
import garnett as gt

def center(frame):
    frame.positions -= np.average(frame.positions, axis=0)
    return frame

with gt.read('in.pos') as traj:
    traj_centered = Trajectory((center(frame) for frame in traj))
    gt.write(traj_centered, 'out.pos')

Loading trajectories into memory

The Trajectory class is designed to be memory-efficient. This means that loading all trajectory data into memory requires an explicit call of the load() or load_arrays() methods.

# Make trajectory data accessible via arrays:
traj.load_arrays()
traj.positions

# Load all frames:
traj.load()
frame = traj[i]
traj.positions    # load() also loads arrays

Note

In general, loading all frames with load() is more expensive than just loading arrays with load_arrays(). Loading all frames also loads the arrays.

Sub-trajectories inherit already loaded data:

traj.load_arrays()
sub_traj = traj[i:j]
sub_traj.positions

Tip

If you are only interested in sub-trajectory data, consider to call load() or load_arrays() only for the sub-trajectory.

Example use with HOOMD-blue

The garnett frames can be used to initialize HOOMD-blue by creating snapshots with the make_snapshot() method or by copying the frame data to existing snapshots with the copyto_snapshot() methods:

from hoomd import init
import garnett as gt

with gt.read('cube.pos') as traj:

    # Initialize from last frame
    snapshot = traj[-1].make_snapshot()
    system = init.read_snapshot(snapshot)

    # Restore last frame
    snapshot = system.take_snapshot()
    traj[-1].copyto_snapshot(snapshot)