Python API ========== Build the extension (from a checkout next to ``readcon-core``): .. code:: bash cd python maturin develop --features python .. code:: 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. .. code:: python 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): .. code:: python 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.