Python documentationView source ↗
On this page

Batch Workflows

MoleculeBatch is an ordered collection for processing many molecules with a single API call. Valid records keep their original input order through transform, export, and filtering steps.

import cosmolkit as ck

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

prepared = batch.with_hydrogens().with_2d_coordinates()

print(prepared.valid_mask)
print(prepared.errors)

repr() on BatchParams, BatchQueryParams, BatchExportParams and BatchImageParams displays every registered configuration field and its current value, including nested configuration. Printing does not execute a progress callback or modify the configuration.

Error Handling

Batch construction, transforms, and exports accept errors to control per-record failures:

  • "raise" raises an exception when a record fails.

  • "keep" keeps failed records and exposes structured errors. Export methods write valid records and include both existing input errors and new export errors in the returned report. total == success + failed and failed == len(errors); there is no separate skipped state.

Omitting errors (or passing None) inherits the batch policy for transforms and exports. New batches default to "raise". An explicit override becomes the returned batch’s policy without modifying the source batch; error_mode reports that policy. Slices and filtering preserve it. Default BatchParams and BatchExportParams therefore display errors=None (inherit).

Important

errors="keep" is not a pipeline-wide guarantee that calls never raise. Queries reject a batch containing failed records before computing any rows. The BatchValidationError retains the indexed errors and explains how to filter valid records before retrying. A new calculation error on a valid record also raises; no partial query result is returned. The source batch is unchanged.

batch = ck.mols_from_smiles_list(["CCO", "not-smiles"], errors="keep")
print(batch.errors)  # Inspect failures before filtering.
clean = batch[batch.valid_mask]  # Or batch.with_valid_records().
print(clean.to_smiles())
# ['CCO'] — filtering preserves survivor order and renumbers the rows.

clean.to_smiles(rooted_at_atom=99)
# Raises BatchValidationError: atom 99 is out of range for CCO.

String modes remain supported, but Python callers can also pass BatchErrorMode enum members. Per-record BatchError values expose the input index, operation name, and message:

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

try:
    ck.mols_from_smiles_list(["C1CC"], errors=ck.BatchErrorMode.RAISE)
except ck.BatchValidationError as exc:
    print(exc.record_errors)  # Indexed failures; exc.errors is their count.

The read-only BATCH_ERROR_MODE_MAP converts external string names to enum members when needed.

Batch Values

MoleculeBatch behaves like an ordered Python container. Valid records are returned as Molecule objects and invalid kept records are returned as None:

Boolean selection accepts Python boolean lists and one-dimensional NumPy boolean arrays, including strided views. The mask length must match the batch; numeric arrays are not converted to boolean masks.

molecules = prepared.to_list()
first = prepared[0]
tail = prepared[5:]
valid = prepared[prepared.valid_mask]

for molecule in prepared:
    if molecule is not None:
        print(molecule.to_smiles())

Integer indexing returns Molecule | None because kept invalid records are represented as None. Slices, integer index lists, and boolean masks return a MoleculeBatch. Selecting records does not compute molecule results: a slice may still contain failed records, and queries on that slice will reject them. Use batch[batch.valid_mask] to select only valid records. Selection preserves the requested order and the batch-level parallel-job setting. Retained errors keep their original source indices even after reordering; use valid_mask to locate valid rows in the current batch. New calculation errors after filtering refer to the filtered batch’s indices.

Export Images

report = prepared.write_images(
    "molecule_images",
    format="png",
    width=300,
    height=300,
    errors="keep",
    filenames=["ethanol.png", "benzene.png", "invalid.png"],
    report_path="image_errors.json",
)

print(report.total, report.success, report.failed)

filenames is optional. Entries must match the batch length; None uses the default zero-padded name for that record. Names are relative to the output directory, and missing extensions are filled from format.

Export SDF

SDF input/output paths, output directories and report paths accept str or os.PathLike[str], including pathlib.Path. This also applies to SdfDataset.open(), SdfReader.open() and their configured forms. Byte paths are rejected; custom path objects must return text from __fspath__(). Parameter-object and keyword-based calls use the same rules.

report = prepared.write_sdf(
    "prepared.sdf",
    format="v2000",
    errors="keep",
    report_path="sdf_errors.csv",
)

Use write_sdf_files() when each valid record should be written to its own SDF file:

report = prepared.write_sdf_files(
    "prepared_records",
    format="v2000",
    errors="keep",
    filenames=["ethanol", "benzene.sdf", "invalid.sdf"],
)

Derived Outputs

Remove retained failures explicitly before querying. Keep prepared.errors if you need the original input indices; filtering does not modify prepared.

prepared = prepared.with_valid_records()
smiles = prepared.to_smiles()
rooted = prepared.to_smiles(rooted_at_atom=0)
explicit = prepared.to_smiles(
    all_bonds_explicit=True,
    all_hydrogens_explicit=True,
)
svgs = prepared.to_svg(width=300, height=300)
bounds = prepared.dg_bounds_matrix()
fingerprints = prepared.fingerprint_morgan(fp_size=2048)
patterns = prepared.fingerprint_pattern(n_bits=2048, tautomeric=False)
atom_pairs = prepared.fingerprint_atom_pair(fp_size=2048)

Fingerprint method names and algorithm parameters match Molecule. Pass either a parameter object or algorithm keywords. Execution keywords remain independent of algorithm configuration:

options = ck.MorganFingerprintParams(radius=2)
fingerprints = prepared.fingerprint_morgan(
    options,
    n_jobs=4,
)

Morgan, Pattern, and AtomPair batch APIs use the same source-backed cores as the single-molecule APIs and preserve record order. Pattern batch calls reuse the fixed compile-once SMARTS table and return one result per input; they are not the separate RDKit MolBundle intersection operation. Topological Torsion exposes the same ordered bulk execution through its generator methods. Morgan and AtomPair can also collect provenance through the following MoleculeBatch conveniences:

results = prepared.fingerprint_morgan_with_output(
    ck.MorganFingerprintParams(radius=2, fp_size=2048),
)

for result in results:
    if result is not None:
        print(result.fingerprint.on_bits)
        print(result.additional_output.bit_info_map)

atom_pair_results = prepared.fingerprint_atom_pair_with_output(
    ck.AtomPairFingerprintParams(fp_size=2048),
)

for result in atom_pair_results:
    if result is not None:
        print(result.fingerprint.on_bits)
        print(result.additional_output.bit_info_map)

SMILES Options

to_smiles() accepts the same output-shaping options for every record:

  • isomeric_smiles includes stereochemical and isotopic information.

  • canonical returns canonical SMILES when enabled.

  • kekule writes aromatic systems in Kekule form.

  • clean_stereo normalizes stereo output where possible.

  • all_bonds_explicit writes explicit bond symbols.

  • all_hydrogens_explicit writes explicit hydrogens.

  • include_dative_bonds includes dative bond notation.

  • ignore_atom_map_numbers omits atom map numbers from canonical decisions.

  • rooted_at_atom starts traversal from a selected atom index.

Batch Chirality

Batch SMILES output preserves isomeric chirality by default:

chiral_batch = ck.mols_from_smiles_list(
    ["F[C@H](Cl)Br", "F[C@@H](Cl)Br"],
    errors="raise",
)

print(chiral_batch.to_smiles(isomeric_smiles=True))
print(chiral_batch.to_smiles(isomeric_smiles=False))

Use canonical=False when you want output to stay closer to each record’s input traversal while keeping the same CW/CCW chiral tag path:

print(chiral_batch.to_smiles(canonical=False))

Parallel Work

Batch calls use one worker by default; n_jobs=1 can be omitted. Explicit n_jobs or a stored with_parallel_jobs() setting enables more workers. Changing the worker count never changes fingerprint parameters or results.

with_parallel_jobs() returns a new batch with a default worker count for later parallel operations. Because molecule values use copy-on-write storage, this configuration step does not duplicate the molecular data.

configured = batch.with_parallel_jobs(8)
prepared = configured.with_2d_coordinates(errors="keep")
smiles = prepared.with_valid_records().to_smiles()

Method-level n_jobs still overrides the batch default for a single call:

svgs = prepared.with_valid_records().to_svg(300, 300, n_jobs=2)

with_progress_bar() returns a new batch with a default Rust-side progress bar setting. Progress is emitted by Rust to stderr, matching the usual terminal stream for progress indicators, and method-level progress_bar overrides the batch default for one call:

tracked = batch.with_parallel_jobs(8).with_progress_bar(True)
prepared = tracked.with_2d_coordinates(errors="keep")
smiles = prepared.with_valid_records().to_smiles(progress_bar=False)

Pass None to clear the stored batch-level override:

default_scheduled = prepared.with_parallel_jobs(None)
quiet = prepared.with_progress_bar(None)