============================== Convert other formats into CON ============================== .. note:: **Executable Org Babel source** (Python only) is :doc:`chemfiles-notebook`. CI runs ``scripts/run-chemfiles-notebook.sh`` (tangle + drift check + ``python3 docs/notebooks/chemfiles_ingress.py``). Rust and other language snippets on *this* page are prose only — they are **not** Babel-run in CI. Do not hand-edit the tangled ``.py``; update the notebook Org source and re-tangle with ``READCON_TANGLE_UPDATE=1``. Diátaxis companions: :doc:`chemfiles-howto` (tasks), :doc:`chemfiles-explain` (why), :doc:`chemfiles-reference` (API tables). .. mermaid:: flowchart LR subgraph Foreign["Foreign trajectories"] XYZ[XYZ] PDB[PDB] GRO[GRO] OTH[…] end CF[chemfiles reader] RC[readcon ConFrame] CON[".con / .convel"] SEL[selection grammar] XYZ --> CF PDB --> CF GRO --> CF OTH --> CF CF -->|read_chemfiles*| RC RC --> CON RC --> SEL SEL -->|atom_data indices| RC This tutorial is **learning-oriented**: one successful path from a **non-CON** file into CON. Prefer ``pip install readcon-chemfiles`` (or Rust ``--features chemfiles``). For native CON files without conversion, start with :doc:`tutorial` instead. What you will build ------------------- 1. Install a chemfiles-linked build. 2. Drive conversion from another format (we use XYZ; the same APIs accept PDB, GRO, LAMMPS dump, and other chemfiles formats). 3. Inspect geometry and optional bonds on the resulting CON frame. 4. Run a selection (``name O``, ``angles: all``) in CON ``atom_data`` order. 5. Write a ``.con`` (and optionally multi-frame) file for eOn / amsel / CON consumers. You do **not** need a pre-existing ``.con`` file. The point is **ingress from the wider ecosystem into CON**. Foreign trajectory → chemfiles → ``ConFrame`` (optional bonds) → ``.con``. After ingress, geometries are first-class for chemparseplot / rgpycrumbs / Choose one install path ----------------------- Pick **one** environment. Do not install both ``readcon`` and ``readcon-chemfiles`` in the same venv (both provide ``import readcon``). Path A — Python full wheel (recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: shell python -m venv .venv && source .venv/bin/activate pip install -U 'readcon-chemfiles==0.14.0' python -c "import readcon; print(readcon.has_chemfiles_support())" # True Path B — Rust with chemfiles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: shell cargo build --features chemfiles cargo test --features chemfiles --lib chemfiles Path C — Editable Python from this repo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: shell maturin develop --features python,chemfiles python -c "import readcon; assert readcon.has_chemfiles_support()" A small XYZ to convert ---------------------- Create ``water.xyz``: .. code:: text 3 water demo for readcon-core chemfiles tutorial O 0.000 0.000 0.000 H 0.957 0.000 0.000 H -0.240 0.927 0.000 Any chemfiles-readable file works the same way (``structure.pdb``, ``conf.gro``, …). Convert XYZ → CON (Rust) ------------------------ .. code:: rust use readcon_core::chemfiles_import::{ chemfiles_enabled, con_frame_from_trajectory_path, con_frames_from_trajectory_path, }; use readcon_core::writer::ConFrameWriter; fn main() -> Result<(), Box> { assert!(chemfiles_enabled(), "rebuild with --features chemfiles"); let frame = con_frame_from_trajectory_path("water.xyz")?; println!( "atoms={} has_bonds={} bonds={}", frame.atom_data.len(), frame.has_bonds(), frame.bonds().len() ); let mut w = ConFrameWriter::from_path("water_from_xyz.con")?; w.write_frame(&frame)?; let all = con_frames_from_trajectory_path("water.xyz")?; println!("trajectory frames: {}", all.len()); Ok(()) } Build with ``--features chemfiles``. Line 2 of the CON file is JSON (``con_spec_version`` 2). Topology, if present in the source format, becomes ``metadata["bonds"]`` (indices in ``atom_data`` order; see :doc:\`chemfiles-explain\`). Convert XYZ → CON (Python) -------------------------- With ``readcon-chemfiles``, Python mirrors the Rust ingress APIs: .. code:: python import readcon assert readcon.has_chemfiles_support(), "pip install readcon-chemfiles" frame = readcon.read_chemfiles_first("water.xyz") # or: frames = readcon.read_chemfiles("water.xyz") print("atoms", len(frame.atoms), "has_bonds", frame.has_bonds) for i, a in enumerate(frame.atoms): print(f" [{i}] {a.symbol} id={a.atom_id} ({a.x:.3f},{a.y:.3f},{a.z:.3f})") frame.write_con("water_from_xyz.con") data = open("water.xyz", encoding="utf-8").read() mem_frames = readcon.read_chemfiles_memory(data, "XYZ") assert len(mem_frames) == 1 print("oxygens", frame.select_atoms("name O")) Plain XYZ usually has **no bonds**. Geometry still converts; use PDB (or the next section) for ``angles:`` / ``bonds:``. Drive topology -------------- Prefer a bonded source format ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: rust let frame = con_frame_from_trajectory_path("ligand.pdb")?; // has_bonds() often true → angles: / is_bonded work after projection Or attach bonds after ingress (indices = atom\_data order) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: rust use readcon_core::chemfiles_import::con_frame_from_trajectory_path; use readcon_core::chemfiles_selection::evaluate_selection_on_con_frame; use readcon_core::types::{Bond, ConFrameBuilder}; use readcon_core::writer::ConFrameWriter; let imported = con_frame_from_trajectory_path("water.xyz")?; let mut b = ConFrameBuilder::new(imported.header.boxl, imported.header.angles); for a in &imported.atom_data { b.add_atom( a.symbol.as_ref(), a.x, a.y, a.z, [a.fixed_x, a.fixed_y, a.fixed_z], a.atom_id, a.mass, ); } b.set_bonds(&[Bond::new(0, 1), Bond::new(0, 2)]); let frame = b.build(); let angles = evaluate_selection_on_con_frame("angles: all", &frame)?; assert_eq!(angles.context_size, 3); let mut w = ConFrameWriter::from_path("water_with_bonds.con")?; w.write_frame(&frame)?; Python selection on bonded CON ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: python import readcon frame = readcon.read_first_frame("water_with_bonds.con") assert frame.has_bonds print(frame.select_atoms("type H")) print(frame.select("bonds: all")["matches"]) print(frame.select("angles: all")["matches"]) frame.write_con("water_selected_roundtrip.con") Multi-format conversion habit ----------------------------- Treat chemfiles as the **format router**; CON as the on-disk checkpoint store. .. code:: rust use readcon_core::chemfiles_import::con_frames_from_trajectory_path; use readcon_core::writer::ConFrameWriter; for path in ["conf.gro", "system.pdb", "dump.lammpstrj", "traj.xyz"] { if !std::path::Path::new(path).exists() { continue; } let frames = con_frames_from_trajectory_path(path)?; let out = format!("{path}.converted.con"); let mut w = ConFrameWriter::from_path(&out)?; for f in &frames { w.write_frame(f)?; } } In-memory: .. code:: rust use readcon_core::chemfiles_import::con_frames_from_memory; let xyz = std::fs::read_to_string("water.xyz")?; let frames = con_frames_from_memory(&xyz, "XYZ")?; Checkpoint ---------- You succeeded if: - ``has_chemfiles_support()`` / ``chemfiles_enabled()`` is true for your build. - You produced a ``.con`` **from a non-CON file**. - You know XYZ may lack bonds; PDB or ``set_bonds`` enables topology selectors. - You ran at least one selection and saw CON ``atom_data`` indices. Next: :doc:\`chemfiles-howto\`, :doc:\`chemfiles-explain\`, :doc:\`chemfiles-reference\`.