Skip to content

I/O Formats

File I/O maps external molecular formats into Frame. The conversion is not a lossless universal schema; each format carries a different mix of topology, coordinates, unit-cell data, and metadata. molrs keeps the mapping explicit by placing parsed columns into named blocks and attaching a simulation box when the source format provides one.

PDB and XYZ are common coordinate-oriented inputs. LAMMPS data and dump files carry simulation-oriented structure. CHGCAR and Gaussian Cube introduce grid data in addition to atoms. Zarr-based frame-sequence storage keeps trajectories in a layout designed for repeated analysis.

Reader Expectations

Readers should be treated as boundary code. They validate enough structure to produce a frame, but domain assumptions still belong to the caller. A file can be syntactically valid while missing the columns required for a later force field or neighbor query.

For development, happy-path I/O tests use real files from the shared tests-data repository. Hand-written strings are reserved for narrow parser edge cases. This keeps the docs and tests aligned with files that users are likely to encounter.

Worked Example: Read, Inspect, Write

from pathlib import Path
import molrs

frame = molrs.read_xyz("water.xyz")
atoms = frame["atoms"]

print("blocks:", frame.keys())
print("rows:", atoms.nrows)
print("columns:", atoms.keys())
print("first atom:", atoms.view("element")[0])

out = Path("water-copy.xyz")
molrs.write_xyz(str(out), frame)
print("wrote:", out)

The reader produces a frame; the writer consumes a frame. The format controls which columns survive. XYZ preserves element symbols and coordinates, while LAMMPS formats can carry more simulation topology and box information.

XYZ connectivity extension

MolCrafts hand-written XYZ can preserve simple topology with a Connct field on the comment line:

3
name=water Connct="[0,1,0,2]"
O  0.0000  0.0000  0.0000
H  0.9572  0.0000  0.0000
H -0.2390  0.9266  0.0000

Connct is a flat list of zero-based atom-index pairs, so this example defines bonds 0–1 and 0–2. Each pair becomes one row in the frame's bonds block using the canonical atomi and atomj columns. Bond order is implicitly one. The XYZ writer emits the same field when a frame already has canonical bond columns.

Format Expectations

Format Typical content Notes
PDB atoms, residues, coordinates, optional box Good for structural biology interop, not a complete force-field container
XYZ element symbols, coordinates, optional Connct pairs Simple coordinate snapshots with a MolCrafts single-bond topology extension
LAMMPS data atoms, bonds, box, simulation topology Used for simulation setup and engine interop
LAMMPS dump trajectory frames Often read lazily when files are large
CHGCAR / Cube atoms plus grid fields Useful for volumetric scalar data
Trajectory / Zarr frames, trajectories Designed for repeated analysis and richer metadata

Lazy LAMMPS Trajectory Access

Large trajectory files should not always be read into memory at once. LAMMPSTrajReader builds an index and reads frames on demand:

reader = molrs.LAMMPSTrajReader("dump.lammpstrj")
print("frames:", len(reader))

first = reader[0]
last = reader.read_step(len(reader) - 1)

print(first["atoms"].nrows)
print(last["atoms"].nrows if last is not None else "missing")

Use read_lammps_traj when the file is small and a list of frames is more convenient. Use LAMMPSTrajReader when you want random access or streaming behavior.

Why Tests Use Real Files

Format readers fail in the details: optional records, whitespace, indexing conventions, element naming, triclinic boxes, and incomplete metadata. Synthetic strings rarely cover those details. molrs therefore tests happy paths against real files from the shared test-data repository and keeps hand-written snippets for narrow malformed-input cases.