Python API#
Build the extension (from a checkout next to readcon-core):
cd python
maturin develop --features python
from pathlib import Path
import readcon
from readcon_db import ConCorpus
db = ConCorpus("/tmp/corpus")
ro = ConCorpus("/tmp/corpus", readonly=True)
db.append_trajectory(1, "run.con")
db.append_trajectory_str(2, Path("run.con").read_text(), source="run.con")
db.append_trajectory_frames(3, readcon.read_con("run.con"))
keys = db.select(
traj_id=1,
symbol="Cu",
natoms_min=1,
natoms_max=10_000,
energy_min=-50.0,
energy_max=0.0,
require_forces=True,
)
h = db.frame_hash(1, 0) # bytes(16)
assert db.find_by_hash(h) == (1, 0)
text = db.get_frame_text(1, 0)
raw = ConCorpus.xxh3_128(b"blob")
Optional select kwargs: exact_hash= (16-byte LE xxh3-128), energy_min / energy_max, fmax_min / fmax_max, mass_min / mass_max, volume_min / volume_max, frame_index_min / frame_index_max, charge_min / charge_max, element_exact, element_min, formula, require_forces, require_velocities, require_energy, limit.
MPI: pack on root, Bcast on the caller communicator#
bcast_packed_frame(comm, corpus_dir, traj_id, frame_idx, root=0) takes an mpi4py Comm — LAMMPS lmp.world, COMM_WORLD.Split(...), a Dup. mpi4py already called MPI_Init; the helper never does, and never names the process-wide world handle.
from mpi4py import MPI
from readcon_db import ConCorpus, bcast_packed_frame, bcast_packed_frames
# Host-owned comm. A LAMMPS Python fix passes lmp.world (or a split).
comm = MPI.COMM_WORLD.Dup()
blob = bcast_packed_frame(comm, "/scratch/corpus", traj_id=1, frame_idx=0)
xyz = ConCorpus.unpack_positions(blob)
batch = bcast_packed_frames(comm, "/scratch/corpus", [(1, 0), (1, 1), (1, 2)])
frames = ConCorpus.unpack_batch(batch)
comm.Free()
Cooked H5MD 1.1 interchange (h5py; fixed natoms; CON stays authority):
db.export_h5md(traj_id=1, path="traj.h5")
Dest must not exist. File "x"; a write failure removes the dest.
The file has /h5md version [1,1] with author=/=creator, particles/all/position/value of shape [T][N][3], position/step and position/time of shape [T], box/edges/value of shape [T][3][3], integer-Z species, optional particles/all/velocity [T][N][3] (dest Angstrom ps-1), and unit attributes for one engine system (like metatomic model vs engine): length Angstrom, time ps, force kJ mol-1 Angstrom-1, velocity Angstrom ps-1. H5MD velocities follow D10. v1, v2, and v3.0 frames use (amu*angstrom^2/eV)^0.5 whatever the time label says. v3.1 frames use units.time and the export fails when velocities have no units.time. export_h5md(traj_id, path, velocity_time_unit=None) overrides that rule for a file that breaks it. Pass fs when a pre-v3.1 file really stored Angstrom/fs, or the natural unit when a v3.1 file mislabeled that unit as fs. CON units.length / units.energy and the clock units.time convert through unit_conversion_factor (SI). Time is CON header.time(), or i * timestep, else the frame index, all in ps. A v3.1 frame with time or timestep and no units.time is an error. velocity_time_unit supplies that missing clock unit. A present units.time still scales the clock. v1, v2, and v3.0 clocks with no units.time are read as fs. unit_conversion_factor(from, to) and canonicalize_unit(expr) are on the Python module. Callers write aliases (A, ev, femtosecond); append_trajectory(..., units={...}), extend_trajectory(..., units={...}), ingest_directory(..., units={...}), and set_units(traj_id, {...}) store canonical names (angstrom, eV, fs) in CON metadata. append / extend stamp the incoming numbers; set_units converts stored numbers so the new label is honest. A v3.1 clock or velocities frame with no units.time is an error, not a silent fs; v1, v2, and v3.0 clocks with no units.time still convert as fs. get_units(traj_id, frame_idx) returns the stored units JSON as a string. Mixed-force trajectories write a full [T][N][3] force dataset (zeros on frames without forces). Mixed-velocity trajectories write a full [T][N][3] velocity dataset the same way. Box boundary follows CON pbc (periodic when absent). author=/=creator=/=boundary attrs are fixed-length ASCII. Physical unit attrs are short H5MD strings (MDAnalysis 2.10 indexes them as dict keys).
Standalone: examples/mpi_bcast_frame.py.
Cooked SoA (RCSO)#
See docs/orgmode/cooked-soa.org. Tier is opt-in; CON text remains authority. Bindings expose cook / delete / has-valid / positions / forces / velocities.