Python documentationView source ↗
On this page

Quick Start

Installation

Install COSMolKit from PyPI:

pip install cosmolkit

Verify the installation:

import cosmolkit as ck

print(ck.__version__)

COSMolKit depends on NumPy for array outputs such as coordinates and distance bounds matrices.

Value-Style Molecule Values

Top-level constructor shortcuts reference the existing class methods directly:

import cosmolkit as ck

mol = ck.mol_from_smiles("CCO")
batch = ck.mols_from_smiles_list(["CCO", "CCN"], n_jobs=1)

Both forms accept exactly the same parameters, defaults and errors as Molecule.from_smiles and MoleculeBatch.from_smiles_list. The class methods remain available. Other molecule shortcuts are mol_from_mol, mol_from_sdf, mol_from_mol2, mol_from_xyz_block, mol_from_inchi, mol_from_binary and mol_from_rdkit. Batch constructors also provide mols_from_sdf_records and mols_from_records. Reaction and BIO shortcuts are reaction_from_smirks, bio_from_pdb, bio_from_mmcif, protein_from_pdb and protein_from_mmcif.

COSMolKit molecules use value semantics. Transform methods return a new Molecule and leave the original object unchanged. Internally the library uses copy-on-write (COW) storage to share unchanged data efficiently:

import cosmolkit as ck

mol = ck.mol_from_smiles("CCO")
mol_h = mol.with_hydrogens()

assert mol is not mol_h
print(mol.to_smiles())
print(mol_h.to_smiles())

Keep the returned Molecule when applying a value-style transformation.

In-place molecule operations are explicit and always end with _:

mol = ck.mol_from_smiles("CCO")
mol.add_hydrogens_()
mol.compute_2d_coordinates_()

Molecule Graphs And Stereochemistry

Create a molecule from SMILES and export a depiction:

import cosmolkit as ck

mol = ck.mol_from_smiles("c1ccccc1O")
drawn = mol.with_2d_coordinates()

print(mol.to_smiles())
drawn.write_png("python/examples/output/phenol.png", width=400, height=300)

Inspect atoms and bonds:

import cosmolkit as ck

mol = ck.mol_from_smiles("c1ccccc1O")

for atom in mol.atoms():
    print(atom.id(), atom.atomic_number(), atom.is_aromatic())

for bond in mol.bonds():
    if bond.order() == ck.BondOrder.SINGLE:
        print(bond.begin(), bond.end(), bond.order().name)

Inspect chiral tags without converting to an ordered tetrahedral record:

chiral = ck.mol_from_smiles("F[C@H](Cl)Br")

print(chiral.to_smiles())
print(chiral.to_smiles(isomeric_smiles=False))

for atom in chiral.atoms():
    if atom.chiral_tag() != ck.ChiralTag.CHI_UNSPECIFIED:
        print(atom.id(), atom.chiral_tag().name)

Assign atom chiral tags from a stored 3D conformer:

import numpy as np

spatial = ck.mol_from_smiles("C(F)(Cl)Br").with_only_3d_conformer(
    np.array(
        [
            [0.0, 0.0, 0.0],
            [1.0, 0.0, 0.0],
            [0.0, 1.0, 0.0],
            [0.0, 0.0, 1.0],
        ]
    )
)
assigned = spatial.with_chiral_tags_from_structure()

assert spatial.atoms()[0].chiral_tag() == ck.ChiralTag.CHI_UNSPECIFIED
assert assigned.atoms()[0].chiral_tag() != ck.ChiralTag.CHI_UNSPECIFIED

with_chiral_tags_from_structure() is a stable, RDKit-parity operation. Its in-place counterpart is assign_chiral_tags_from_structure_(). The value-style form leaves caller state unchanged on failure. Like other in-place operations, the _ form may retain valid partial changes after an error but keeps the molecule’s internal storage complete.

Enumerate unassigned atom and double-bond stereo lazily while preserving the source molecule:

import cosmolkit as ck

source = ck.mol_from_smiles("CC(F)C(Cl)Br")
options = ck.StereoisomerOptions(max_isomers=4)

print(source.stereoisomer_count(options))
print([isomer.to_smiles() for isomer in source.enumerate_stereoisomers(options)])

Molecular File IO And Stored Coordinates

Read and write the first SDF record:

mol = ck.Molecule.read_sdf_with_params(
    "input.sdf", ck.SdfReadParams(coordinate_mode="preserve"),
)
mol.write_sdf_with_params(
    "python/examples/output/output.sdf", ck.MolBlockWriteParams(format="v2000"),
)

Read MOL2 with the RDKit-style parser profile:

mol = ck.Molecule.read_mol2("input.mol2")

Access coordinates as NumPy arrays:

mol2d = ck.mol_from_smiles("CCO").with_2d_coordinates()
coords = mol2d.coordinates_2d()

print(coords.shape)

Round-trip a molecule through Python pickle:

import pickle

mol = ck.mol_from_smiles("F[C@H](Cl)[13CH3:7]").with_2d_coordinates()

restored = pickle.loads(pickle.dumps(mol, protocol=pickle.HIGHEST_PROTOCOL))

print(restored.to_smiles(canonical=False))
print(restored.has_2d_coordinates())

Conformers And Force Fields

Generate a native 3D conformer with ETKDGv3:

import cosmolkit as ck

mol = ck.mol_from_smiles("CC(=O)NC").with_hydrogens()
params = ck.EmbedParams.etkdg_v3()
params.random_seed = 0xF00D
params.num_threads = 1
params.track_failures = True

embedded = mol.with_3d_conformer(params)

print(embedded.num_3d_conformers())
print(embedded.coordinates_3d().shape)
print(params.failures)

with_3d_conformer() follows RDKit’s ETKDG behavior for trusted molecular graphs. A molecule without explicit hydrogens is embedded as a heavy-atom-only conformer; the operation does not fail and does not automatically add hydrogens. Add hydrogens first when you need all-atom geometry, force-field optimization, or hydrogen-bond-sensitive coordinates.

Generate multiple conformers with RMS pruning:

params = ck.EmbedParams.etkdg()
params.random_seed = 123
params.num_threads = 1
params.prune_rms_thresh = 0.5
params.enable_sequential_random_seeds = True

conformers = mol.with_3d_conformers(5, params)
print(conformers.num_3d_conformers())

Enum inputs accept either an enum member or its canonical snake-case string. For example, coordinate_mode="require_3d" and coordinate_mode=ck.SdfCoordinateMode.Require3D are equivalent. Configuration assignment accepts both forms; getters continue to return enum members.

Optimize an existing 3D conformer with UFF:

mol = ck.Molecule.read_sdf_with_params(
    "input_3d.sdf", ck.SdfReadParams(coordinate_mode="require_3d"),
)

if mol.uff_has_all_molecule_params():
    result = mol.with_uff_optimized_with_params(ck.UffOptimizationParams(max_iterations=200))
    optimized = result.molecule()

    print(not result.needs_more())
    print(result.status_code())
    print(result.energy())
    print(optimized.coordinates_3d().shape)

if mol.mmff_has_all_molecule_params():
    result = mol.with_mmff_optimized_with_params(
        ck.MmffOptimizationParams(mmff_variant="MMFF94", max_iterations=200),
    )
    print(not result.needs_more())
    print(result.status_code())

Fingerprints And SMARTS

Generate source-backed fingerprints:

import cosmolkit as ck

mol = ck.mol_from_smiles("c1ccccc1O")
morgan = mol.fingerprint_morgan(
    radius=2,
    fp_size=2048,
)
topological = mol.fingerprint_topological(fp_size=2048)
avalon = mol.fingerprint_avalon(n_bits=512)
pattern = mol.fingerprint_pattern(n_bits=2048, tautomeric=False)
atom_pair = mol.fingerprint_atom_pair()
torsion = mol.fingerprint_topological_torsion(
    torsion_atom_count=4,
    fp_size=2048,
)

print(morgan.on_bits())
print(topological.on_bits())
print(avalon.on_bits())
print(pattern.on_bits())
print(atom_pair.on_bits())
print(torsion.on_bits())

on_bits() returns the sparse bit indexes set inside the fixed-length binary fingerprint. The exposed Morgan, MACCS, RDKit topological, Avalon, Pattern, AtomPair, and Topological Torsion branches are covered by exact pinned-RDKit comparisons; structurally similar hashes or similarity correlation are not compatibility claims. It is not a dense neural embedding.

Parse SMARTS metadata:

import cosmolkit as ck

query = ck.parse_smarts("[#6]-O")

print(query.num_atoms())
print(query.num_bonds())

parse_smarts() returns a detached QueryGraph, not a Molecule. ck.QueryGraph.from_smarts(text) is the equivalent class factory. Both delegate to the same parser and preserve its defaults and typed errors. Pass the query to mol.substruct_matches(query) or mol.has_substruct_match(query). Explicit options use ck.parse_smarts_with_params(text, params) or ck.QueryGraph.from_smarts_with_params(text, params).

Matching accepts a parameter object or keyword options, including Python callbacks:

params = ck.SubstructMatchParams()
params.extra_final_check = lambda target, atom_ids: atom_ids[0] == 0
matches = mol.substruct_matches(query, params)
# Equivalent callback spelling:
matches = mol.substruct_matches(query, final_match=params.extra_final_check)

atom_match(query_atom, target_atom) receives a read-only QueryAtom and Atom; bond_match(query_bond, target_bond) receives two read-only Bond values. Callback exceptions propagate unchanged. Unknown configuration attributes raise AttributeError instead of being silently ignored.

Batch Processing

Process a list of molecules:

import cosmolkit as ck

batch = ck.mols_from_smiles_list(
    ["CCO", "c1ccccc1", "not-smiles"],
    errors=ck.BatchErrorMode.KEEP,
).with_parallel_jobs(8)

for error in batch.errors():
    print(error.index(), error.operation(), error.message())

prepared = batch.with_2d_coordinates(errors=ck.BatchErrorMode.KEEP)
fingerprints = prepared.fingerprint_morgan_list(fp_size=2048)

print(prepared.valid_mask())
print(prepared.to_smiles_list(canonical=True))
print([mol.to_smiles() if mol is not None else None for mol in prepared])
print([fp.on_bits() if fp is not None else None for fp in fingerprints])

MoleculeBatch preserves input order. When errors="keep" or BatchErrorMode.KEEP is used, invalid records stay aligned with their input positions and appear as None in molecule-valued outputs.