Skip to content

Trajectory Class

Overview

The Trajectory class manages molecular dynamics trajectory data, including particles, time information, and simulation box data. It serves as the primary container for time-series molecular simulation data.

Class Definition

class Trajectory:
    """Container for molecular dynamics trajectory data."""

Constructor

Trajectory()

Creates an empty trajectory.

Example:

import smolsat

# Create empty trajectory
trajectory = smolsat.Trajectory()
print(f"Particles: {trajectory.num_particles()}")  # 0
print(f"Frames: {trajectory.num_frames()}")        # 0

Particle Management

add_particle(id, type, mass=1.0, type_name="") → Particle

Adds a new particle to the trajectory.

Parameters: - id (int): Unique particle identifier - type (int): Particle type identifier
- mass (float): Particle mass (default: 1.0) - type_name (str): Human-readable type name (default: "")

Returns: New Particle object

Example:

trajectory = smolsat.Trajectory()

# Add different types of particles
hydrogen = trajectory.add_particle(0, 1, 1.0, "H")
carbon = trajectory.add_particle(1, 6, 12.0, "C")
oxygen = trajectory.add_particle(2, 8, 16.0, "O")

print(f"Added {trajectory.num_particles()} particles")

particle(index) → Particle

Retrieves a particle by index.

Parameters: - index (int): Particle index (0-based)

Returns: Particle object

Example:

# Access particles
first_particle = trajectory.particle(0)
print(f"Particle ID: {first_particle.id()}")
print(f"Particle type: {first_particle.type_name()}")

num_particles() → int

Returns the number of particles in the trajectory.

Time Information

add_time(time)

Adds a time point to the trajectory.

Parameters: - time (float): Time value

Example:

# Add time points (e.g., every 0.001 time units)
for frame in range(1000):
    trajectory.add_time(frame * 0.001)

time(frame) → float

Gets the time for a specific frame.

Parameters: - frame (int): Frame index

Returns: Time value

num_frames() → int

Returns the number of frames in the trajectory.

Box Information

add_box_size(box_size)

Adds box dimensions for a frame.

Parameters: - box_size (Coordinate): Box dimensions

Example:

# Add box information (cubic box with side length 10)
box = smolsat.Coordinate(10.0, 10.0, 10.0)
for frame in range(num_frames):
    trajectory.add_box_size(box)

box_size(frame) → Coordinate

Gets box dimensions for a specific frame.

Parameters: - frame (int): Frame index

Returns: Coordinate representing box dimensions

Type-Based Selection

particles_of_type(type_id) → List[Particle]

Gets all particles of a specific type.

Parameters: - type_id (int): Type identifier

Returns: List of particles

Example:

# Get all hydrogen atoms (type 1)
hydrogens = trajectory.particles_of_type(1)
print(f"Found {len(hydrogens)} hydrogen atoms")

# Get all carbon atoms (type 6)
carbons = trajectory.particles_of_type(6)

particles_of_type_name(type_name) → List[Particle]

Gets all particles with a specific type name.

Parameters: - type_name (str): Type name

Returns: List of particles

Example:

# Get particles by name
waters = trajectory.particles_of_type_name("O")  # Oxygen in water
polymers = trajectory.particles_of_type_name("C")  # Carbon backbone

Molecule Management

add_molecule(particle_ids) → Molecule

Creates a molecule from a list of particle IDs.

Parameters: - particle_ids (List[int]): List of particle IDs

Returns: New Molecule object

Example:

# Create water molecule (H-O-H)
water_ids = [0, 1, 2]  # H, O, H particle IDs
water_molecule = trajectory.add_molecule(water_ids)

# Create polymer chain
polymer_ids = list(range(10, 50))  # Particles 10-49
polymer_chain = trajectory.add_molecule(polymer_ids)

molecule(index) → Molecule

Gets a molecule by index.

Parameters: - index (int): Molecule index

Returns: Molecule object

num_molecules() → int

Returns the number of molecules in the trajectory.

Utility Methods

clear()

Removes all data from the trajectory.

Example:

trajectory.clear()
print(f"Particles after clear: {trajectory.num_particles()}")  # 0

Usage Examples

Creating a Simple Trajectory

import smolsat
import numpy as np

# Create trajectory
trajectory = smolsat.Trajectory()

# Add particles
num_particles = 100
for i in range(num_particles):
    trajectory.add_particle(i, 1, 1.0, "A")

# Add time and box information
num_frames = 1000
dt = 0.001
box_size = smolsat.Coordinate(10.0, 10.0, 10.0)

for frame in range(num_frames):
    trajectory.add_time(frame * dt)
    trajectory.add_box_size(box_size)

# Add position data to particles
np.random.seed(42)
for i in range(num_particles):
    particle = trajectory.particle(i)

    # Random walk trajectory
    pos = np.random.uniform(0, 10, 3)
    for frame in range(num_frames):
        # Add some random motion
        pos += np.random.normal(0, 0.1, 3)
        position = smolsat.Coordinate(pos[0], pos[1], pos[2])
        particle.add_position(position)

print(f"Created trajectory: {trajectory.num_particles()} particles, {trajectory.num_frames()} frames")

Loading and Processing Trajectory Data

# Load trajectory from file
trajectory = smolsat.load_trajectory("simulation.xyz")

# Get trajectory information
print(f"Trajectory contains:")
print(f"  Particles: {trajectory.num_particles()}")
print(f"  Frames: {trajectory.num_frames()}")
print(f"  Time range: {trajectory.time(0):.3f} - {trajectory.time(trajectory.num_frames()-1):.3f}")

# Analyze particle types
for type_id in range(1, 10):  # Check types 1-9
    particles = trajectory.particles_of_type(type_id)
    if particles:
        print(f"  Type {type_id}: {len(particles)} particles")

# Create molecules (example: water molecules)
# Assuming particles 0,1,2 = first water, 3,4,5 = second water, etc.
num_waters = trajectory.num_particles() // 3
for i in range(num_waters):
    water_ids = [i*3, i*3+1, i*3+2]
    trajectory.add_molecule(water_ids)

print(f"Created {trajectory.num_molecules()} water molecules")

Trajectory Analysis Preparation

# Get specific particles for analysis
solvent_particles = trajectory.particles_of_type_name("SOL")
polymer_particles = trajectory.particles_of_type_name("POL")

# Create system for analysis
system = smolsat.System(trajectory, periodic_boundaries=True)

# Perform analysis on specific particle groups
if len(solvent_particles) > 0:
    msd_analysis = smolsat.MeanSquareDisplacement(system, solvent_particles)
    msd_analysis.compute()
    print("Solvent MSD analysis completed")

if len(polymer_particles) > 0:
    rg_analysis = smolsat.RadiusOfGyration(system, polymer_particles)
    rg_analysis.compute()
    print("Polymer radius of gyration analysis completed")

Working with Molecules

# Create complex molecular system
trajectory = smolsat.Trajectory()

# Add atoms for a polymer chain
chain_length = 20
for i in range(chain_length):
    trajectory.add_particle(i, 1, 12.0, "C")  # Carbon backbone

# Create the polymer molecule
polymer_ids = list(range(chain_length))
polymer = trajectory.add_molecule(polymer_ids)

# Add solvent molecules
solvent_start = chain_length
num_solvents = 100
for i in range(num_solvents):
    solvent_id = solvent_start + i
    trajectory.add_particle(solvent_id, 2, 18.0, "SOL")
    # Each solvent is its own molecule
    trajectory.add_molecule([solvent_id])

print(f"System contains:")
print(f"  Total particles: {trajectory.num_particles()}")
print(f"  Molecules: {trajectory.num_molecules()}")
print(f"  Polymer size: {polymer.num_particles()} particles")

Integration with Analysis

The Trajectory class integrates seamlessly with SMolSAT analysis methods:

# Create trajectory
trajectory = smolsat.create_example_trajectory(num_particles=200, num_frames=5000)

# Quick analysis using high-level functions
lag_times, msd_values = smolsat.quick_msd(trajectory)
times, rg_values = smolsat.quick_rg(trajectory)

# Advanced analysis with specific particle selection
system = smolsat.System(trajectory, periodic_boundaries=True)
selected_particles = trajectory.particles_of_type(1)[:50]  # First 50 type-1 particles

msd_analysis = smolsat.MeanSquareDisplacement(system, selected_particles)
msd_analysis.compute()

See Also