Python documentationView source ↗
On this page

Reactions

The experimental reaction API projects the existing Rust implementation. Python and JavaScript do not implement separate reaction chemistry.

Generate products

import cosmolkit as ck

reaction = ck.Reaction.from_smirks("[C:1]>>[N:1]")
source = ck.mol_from_smiles("C")
product_sets = reaction.run([source], ck.ReactionRunParams())
assert product_sets[0][0].to_smiles() == "N"
assert source.to_smiles() == "C"
assert reaction.is_initialized

ck.parse_smirks is an equivalent factory. Explicit parse/write options use the _with_params methods and ReactionParseParams / ReactionWriteParams. to_smirks() and to_cx_smirks() return strings. As in the source overloads, the former fixes CX output off and the latter fixes it on; include_cx does not override that choice. cx_fields selects the fields emitted by the CX overload.

Products remain a list of product sets: each inner list preserves product template order for one matching combination. No match returns an empty outer list. Execution may initialize the same reaction handle.

For multiple inputs, preserve reactant-template order. Execution belongs to the reaction, not an unrelated molecule receiver:

pair = ck.Reaction.from_smirks("[C:1].[C:2]>>[C:1].[C:2]")
products = pair.run([source, source], ck.ReactionRunParams(max_products=1000))
assert [m.to_smiles() for m in products[0]] == ["C", "C"]

Rust uses use cosmolkit::{Reaction, ReactionRunParams}; and the inherent reaction.run(&[&mol_a, &mol_b], &params) method; no extension trait or domain-crate import is needed. The public reaction type, constructors, execution method and registry entries require cap-reaction.

ReactionSingleRunParams selects coordinates for one input; ReactionRunParams.coordinate_selections selects per-reactant coordinates. Use ReactionCoordinateSelection.auto(), two_d(id) or three_d(id). Explicit IDs are conformer IDs, not vector positions.

Typed atom metadata

User metadata retains its type instead of being converted to text:

carbon = ck.mol_from_smiles("C")
tagged = carbon.with_atom_property_int(0, "tracking_id", 42)
assert carbon.atom_property_int(0, "tracking_id") is None
assert tagged.atom_property_int(0, "tracking_id") == 42
tagged.set_atom_property_int_(0, "tracking_id", 43)

Choose a typed suffix: bool, int, uint, double, string, int_vector or string_vector. Missing keys return None; invalid atom indices, reserved keys and unsupported value types raise errors. Setters accept nonempty user keys, not underscore-prefixed private keys, computed properties, atom-map numbers or reaction bookkeeping keys. Clones and value-returning operations retain COW isolation.

Reaction copying is an explicit CK extension, disabled by default:

oxygen = ck.mol_from_smiles("O").with_atom_property_int(0, "tracking_id", 84)
reaction = ck.Reaction.from_smirks("[C:1].[O:2]>>[C:1][O:2]")
params = ck.ReactionRunParams(copy_atom_properties=True)
product = reaction.run([tagged, oxygen], params)[0][0]
assert [product.atom_property_int(i, "tracking_id") for i in range(2)] == [43, 84]

Copying follows both the input-reactant index and source-atom index, not destination positions. Each duplicate inherits its source’s user metadata; new atoms inherit none and deleted atoms contribute none. Existing product values, including explicit template values, win over copied values. Private, computed, CIP and reaction bookkeeping properties are not additionally copied. False means no extra copying; it does not clear properties retained by the normal reaction algorithm. Chemical assignment follows the normal pipeline.

JavaScript uses the same typed families: withAtomPropertyInt, setAtomPropertyInt_ and atomPropertyInt; missing keys return undefined. Set params.copyAtomProperties = true before reaction.run(inputs, params).

Restricted application

source = ck.mol_from_smiles("C")
reaction = ck.Reaction.from_smirks("[C:1]>>[N:1]")
result = source.apply_reaction(reaction)
assert result.changed
assert result.molecule.to_smiles() == "N"
assert source.to_smiles() == "C"
changed = source.apply_reaction_(reaction)
assert changed and source.to_smiles() == "N"

Value application returns ReactionApplyResult with read-only molecule and changed properties. The _ variant changes the receiver and returns the source-defined boolean, not a graph-difference approximation. Explicit parameter forms accept ReactionApplyParams. Application requires one reactant and one product template and cannot add new product atoms; use product generation for other supported reactions. Use the value-returning form when the input must remain unchanged after a failure.

Templates, validation and errors

Reaction.from_templates(reactants, products, agents) accepts QueryGraph values. Ordered template queries and with_*_template return detached values. without_agents and without_unmapped_* return ReactionTemplateRemoval with reaction and removed_templates.

validate() returns ReactionValidationReport with warning/error issues, counts and is_valid. with_initialized() returns an initialized copy without changing the source reaction. Explicit forms accept ReactionValidationParams(silent=True).

Results are read-only; ReactionRunParams fields are writable configuration. Errors retain domain, a typed variant and payload fields such as role, template, index, count and atom where applicable. OperationError.__cause__ retains ReactionRunError or ReactionApplyError. Invalid initialization includes a validation report. Measured comparison coverage is recorded in VALIDATION.md.

JavaScript

Names use camelCase: Reaction.fromSmirks, molecule.reactionProducts, reaction.withInitialized and ReactionCoordinateSelection.threeD. Value application is molecule.applyReaction; mutation is molecule.applyReaction_. Typed error payloads are under error.detail and concrete causes under error.cause.