File I/O¶
MolPy’s IO layer is built around Frame + Box.
Readers return a Frame (with blocks like "atoms", "bonds" and a box in metadata),
and writers take a Frame and produce on‑disk files compatible with external tools.
This page gives you small, runnable examples for the most common formats.
PDB: quick coordinate round‑trip¶
PDB is convenient for small molecules and quick visualization.
import molpy as mp
from molpy.io import read_pdb, write_pdb
# Build a tiny system in a frame
box = mp.Box.cubic(20.0)
frame = mp.Frame()
frame["atoms", "x"] = [0.0, 1.0, 2.0]
frame["atoms", "y"] = [0.0, 0.0, 0.0]
frame["atoms", "z"] = [0.0, 0.0, 0.0]
frame["atoms", "element"] = ["C", "C", "C"]
frame.metadata["box"] = box
write_pdb("example.pdb", frame)
loaded = read_pdb("example.pdb")
print("Loaded atoms:", loaded["atoms"].nrows)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[1], line 8 5 box = mp.Box.cubic(20.0) 7 frame = mp.Frame() ----> 8 frame["atoms", "x"] = [0.0, 1.0, 2.0] 9 frame["atoms", "y"] = [0.0, 0.0, 0.0] 10 frame["atoms", "z"] = [0.0, 0.0, 0.0] File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/core/frame.py:730, in Frame.__setitem__(self, key, value) 711 """ 712 Set a Block by name. 713 (...) 727 True 728 """ 729 if not isinstance(value, Block): --> 730 value = Block(value) 731 self._blocks[key] = value File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/core/frame.py:83, in Block.__init__(self, vars_) 81 if vars_ is not None: 82 if not isinstance(vars_, dict): ---> 83 raise ValueError(f"vars_ must be a dict, got {type(vars_)}") 84 for k, v in vars_.items(): 85 try: ValueError: vars_ must be a dict, got <class 'list'>
Under the hood:
read_pdbpopulates:frame["atoms"]with an atomsBlock(includingxyzcoordinates)frame["bonds"]fromCONECTrecords when availableframe.metadata["box"]fromCRYST1where possible
write_pdbwrites:ATOM/HETATMrecords fromframe["atoms"]CRYST1from the boxCONECTfrom the"bonds"block
LAMMPS data: structure for simulations¶
MolPy can read and write LAMMPS “data” files using Frame as the in‑memory representation.
Writing a LAMMPS data file¶
from molpy.io import write_lammps_data
# Assume `frame` has been constructed or loaded before
write_lammps_data("system.data", frame, atom_style="full")
The writer expects at least:
"atoms"block with the required columns for the chosenatom_style"bonds"/"angles"/"dihedrals"blocks when present- A
Boxstored inframe.metadata["box"]
Reading a LAMMPS data file¶
from molpy.io import read_lammps_data
frame = read_lammps_data("system.data", atomstyle="full")
print("Atoms:", frame["atoms"].nrows)
print("Has bonds:", "bonds" in list(frame.blocks()))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[3], line 3 1 from molpy.io import read_lammps_data ----> 3 frame = read_lammps_data("system.data", atomstyle="full") 4 print("Atoms:", frame["atoms"].nrows) 5 print("Has bonds:", "bonds" in list(frame.blocks())) TypeError: read_lammps_data() got an unexpected keyword argument 'atomstyle'. Did you mean 'atom_style'?
For legacy workflows there is also molpy.io.read_lammps(...) which can optionally
load force‑field scripts; for new code, prefer read_lammps_data.
LAMMPS trajectories¶
Trajectory readers return a Frame per snapshot.
MolPy’s LAMMPS trajectory reader is designed to be lazy and memory‑friendly.
from molpy.io.trajectory.lammps import LammpsTrajectoryReader
reader = LammpsTrajectoryReader("traj.lammpstrj")
for i, frame in enumerate(reader):
print("Step", i, "atoms:", frame["atoms"].nrows)
if i >= 9:
break # just peek at the first 10 frames
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) Cell In[4], line 3 1 from molpy.io.trajectory.lammps import LammpsTrajectoryReader ----> 3 reader = LammpsTrajectoryReader("traj.lammpstrj") 5 for i, frame in enumerate(reader): 6 print("Step", i, "atoms:", frame["atoms"].nrows) File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/io/trajectory/lammps.py:60, in LammpsTrajectoryReader.__init__(self, fpath, frame, extra_type_mappings) 53 def __init__( 54 self, 55 fpath: PathLike | list[PathLike], (...) 58 ): 59 # Pass the fpath (single or multiple) to the base class ---> 60 super().__init__(fpath) 61 self.frame = frame 62 if extra_type_mappings: File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/io/trajectory/base.py:51, in BaseTrajectoryReader.__init__(self, fpath) 49 for path in self.fpaths: 50 if not path.exists(): ---> 51 raise FileNotFoundError(f"File not found: {path}") 53 self._frame_locations: list[FrameLocation] = [] # location info for each frame 54 self._mms: list[mmap.mmap] = [] # memory-mapped file objects for each file FileNotFoundError: File not found: traj.lammpstrj
from molpy.io import read_amber
frame, ff = read_amber("system.prmtop", "system.inpcrd")
print("Atoms:", frame["atoms"].nrows)
print("Box:", frame.metadata.get("box"))
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) Cell In[5], line 3 1 from molpy.io import read_amber ----> 3 frame, ff = read_amber("system.prmtop", "system.inpcrd") 4 print("Atoms:", frame["atoms"].nrows) 5 print("Box:", frame.metadata.get("box")) File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/io/readers.py:247, in read_amber_prmtop(prmtop, inpcrd, frame) 245 prmtop_path = Path(prmtop) 246 reader = AmberPrmtopReader(prmtop_path) --> 247 frame, ff = reader.read(frame) 249 if inpcrd is not None: 250 from .data.amber import AmberInpcrdReader File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/io/forcefield/amber.py:26, in AmberPrmtopReader.read(self, frame) 25 def read(self, frame: mp.Frame): ---> 26 with open(self.file) as f: 27 lines = filter( 28 lambda line: line, map(AmberPrmtopReader.sanitizer, f.readlines()) 29 ) 31 # read file and split into sections FileNotFoundError: [Errno 2] No such file or directory: 'system.prmtop'
GROMACS¶
from molpy.io import read_gromacs
frame, ff = read_gromacs("conf.gro", "topol.top")
print("Atoms:", frame["atoms"].nrows)
--------------------------------------------------------------------------- ImportError Traceback (most recent call last) Cell In[6], line 1 ----> 1 from molpy.io import read_gromacs 3 frame, ff = read_gromacs("conf.gro", "topol.top") 4 print("Atoms:", frame["atoms"].nrows) ImportError: cannot import name 'read_gromacs' from 'molpy.io' (/opt/buildhome/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/io/__init__.py)
These helpers return:
- A
Framedescribing the atomic system (+Boxin metadata) - A force‑field object you can later map into MolPy’s force‑field layer
Other formats¶
MolPy’s IO layer is designed to be extensible. In addition to the formats above, there are readers/writers for:
- XSF (
read_xsf,write_xsf) - XYZ and simple CSV‑like formats (via
Block.from_csvand small utilities) - Additional trajectory readers under
molpy.io.trajectory
Most of them follow the same pattern:
- Input: file path + optional existing
Frame - Output: populated
Framewith"atoms"(and optionally"bonds","box", …)
When in doubt, look at the corresponding module under molpy.io.* and the
unit tests under tests/io for the exact expectations.