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.