How-to — migrate a stack onto CON¶
Note
Diátaxis how-to (goal-oriented). Learning paths:
Tutorial — your first CON checkpoint (native CON I/O) and Convert other formats into CON (foreign formats).
Executable conversion (CI): Executable Chemfiles notebook /
scripts/run-chemfiles-notebook.sh.
Why adopt CON (what you gain)¶
Reach for a maintained library with a real frame API, not a hand-rolled XYZ
parser and a home-grown Atoms struct. readcon-core already gives you
parse/write, typed metadata, optional sections, validation, compression,
lazy multi-frame iteration, selection, hourglass bindings in every major
language, campaign storage via readcon-db, and plotting via chemparseplot —
one interchange file and one API surface.
CON on disk is the checkpoint (cell, type-grouped coordinates, constraints,
atom_id, optional sections, JSON). The library is the reason to use it even
from a single code in a single language: you do not re-marshall atoms, masks,
selectors, or energy fields yourself.
Layer |
What you get |
|---|---|
Frame API |
Read/write, builders, |
Multi-frame + compression |
Lazy iterators, gzip/zstd, hot-path parse; trajectories stay usable as CON sequences |
Selection |
|
Hourglass |
Same semantics in Fortran / C / C++ / Python / Julia / Rust when the stack grows |
|
LMDB corpora on CON text: energy / formula / section indexes, dedup, multi-reader |
Plotting |
chemparseplot / rgpycrumbs on the same checkpoints |
Ingress |
Chemfiles: XYZ/PDB/GRO/… → CON with one convert path |
Performance gates |
Cachegrind I-refs (main); Python ASV + spyglass (PRs); peer scripts under |
Migrate so the structure object, selector, campaign store, and plots all speak CON. Format rules: The CON File Format Specification.
Performance¶
What the library actually does on the CON path: fast-float2 on atom floats,
zero-copy line views, header-sized atom vectors, read_to_string vs mmap at
64 KiB, optional Rayon multi-frame parse at ≥ 48 KiB with parallel. How CI
and local scripts measure that (Cachegrind, ASV, peers):
Performance Benchmarks.
Benefit: campaign store (readcon-db)¶
readcon-core is the decoder/writer. readcon-db is the campaign
layer that assumes CON text is authoritative:
Capability |
What you get after switching to CON |
|---|---|
Indexed corpora |
Energy, formula, section-presence flags from the same projection as |
Dedup |
Content hash on frame blobs so re-ingested images do not explode the store |
Multi-reader |
SWMR LMDB access for screening jobs without serializing on one process |
Join / split / reindex |
Corpus ops stay on UTF-8 CON; no opaque binary fork of the structure |
Install (separate package): cargo add readcon-db / pip install readcon-db.
Package docs: lode-org.github.io/readcon-db.
Rust API: docs.rs/readcon-db.
Source: github.com/lode-org/readcon-db.
Multi-frame CON (with optional compression) plus selection and index_proj
feed the same campaign path; you do not need a second structure dialect for
“real” trajectories or screening corpora.
Benefit: the API (stop owning your own atoms object)¶
Use the library types and helpers instead of marshaling coordinates, masks, and metadata by hand in every tool:
Frames:
read_con/iter_con/write_con/convert_to_con/ buildersPer-atom: symbols, positions, fixed masks,
atom_id, optional force/velocity/charge fieldsFrame-level:
energy, units, reserved JSON keys,validateSelection:
frame.select_atoms("name H")(topology selectors when bonds exist)C ABI:
rkr_frame_*so Fortran and C++ do not reimplement the table
import readcon
frame = readcon.read_first_frame("structure.con")
h_idx = frame.select_atoms("name H")
# topology: frame.select("angles: all") when metadata bonds are present
Recipes: How-to — Chemfiles conversion and selection, How-to — CON I/O by language, Language bindings.
Benefit: plotting stack (chemparseplot)¶
chemparseplot is the plotting / geometry-analysis side of the same CON stack: once structures (and NEB / reaction paths) are CON checkpoints, the same files feed 2D reaction-valley style figures and geometry plots without a custom exporter per code. Companion CLIs and crumbs: rgpycrumbs.
Foreign XYZ/PDB still enter via chemfiles → CON first; viz then sits on CON
(and on campaign selections from readcon-db) rather than on each code’s
private dump. Ecosystem context: conf.py Ecosystem nav ·
Explanation — Chemfiles ingress and CON topology.
One-command convert (CLI)¶
Build with chemfiles when the input is not already CON:
cargo build --release --features chemfiles
./target/release/readcon-core convert structure.xyz structure.con
./target/release/readcon-core convert structure.pdb structure.con
# native CON inspect / rewrite (no chemfiles required):
./target/release/readcon-core convert input.con out.con
./target/release/readcon-core input.con # summary only
Library entry (same logic as the CLI):
use readcon_core::convert::convert_path_to_con;
use std::path::Path;
let report = convert_path_to_con(Path::new("in.xyz"), Path::new("out.con"))?;
assert!(report.n_atoms_last > 0);
Python one-liner¶
Chemfiles-linked install for foreign formats:
pip install 'readcon-chemfiles==0.14.0' # or: maturin develop --features python,chemfiles
import readcon
# Foreign or CON → CON
report = readcon.convert_to_con("structure.xyz", "structure.con")
print(report) # n_frames, n_atoms_last, native_con
# Same via frame API (tutorial / notebook path; CI-run)
frame = readcon.read_chemfiles_first("structure.xyz")
frame.write_con("structure.con")
Executable Org Babel for the XYZ path: scripts/run-chemfiles-notebook.sh
(wired in ci_python.yml chemfiles matrix).
Into a campaign store¶
Once files are CON text:
# see readcon-db docs for open/ingest/select; packages:
# cargo add readcon-db
# pip install readcon-db
Screening indexes use the same field meanings as readcon_core::index_proj
(finite energy, formula, sections mask). That only works if the blob on disk is
CON, not a private trajectory dialect.
Batch: many foreign files → CON¶
for f in structures/*.{xyz,pdb,gro}; do
./target/release/readcon-core convert "$f" "con/${f##*/}.con"
done
Python:
from pathlib import Path
import readcon
for path in Path("structures").glob("*.xyz"):
readcon.convert_to_con(str(path), str(Path("con") / f"{path.stem}.con"))
More chemfiles recipes: How-to — Chemfiles conversion and selection.