Skip to main content

openclatura

Deterministic structure-to-IUPAC naming, with structural auditing and atom-level explanations.

PyPI Python CI License: MIT

openclatura demo

Openclatura is an open-source, rule-based structure-to-IUPAC naming framework for SMILES and RDKit molecules. It combines IUPAC 2013 nomenclature, atom-linked decision traces, built-in reconstruction auditing, and optional independent verification through OPSIN.

Names are constructed from the molecular graph using nomenclature rules and retained-name vocabulary—not predicted by a neural model. Naming and built-in auditing run locally with Python and RDKit. Java is required only for the optional OPSIN cross-check.

It supports deterministic SMILES-to-IUPAC conversion (smiles2iupac) for individual structures and molecular datasets.

What that buys you

  • Auditable. Each name carries the Blue Book rules it hit (P-44, P-61, …) and a step-by-step trace of parse → perception → parent → numbering → assembly.
  • Broad. Chains and rings from 1 to 1000 skeletal atoms (P-14.2.1), plus fused, spiro and bridged systems, and the Blue Book's retained names.
  • Verifiable. Optional round-trip through OPSIN: parse the generated name back to a structure and compare.
  • Explainable. describe() renders the trace as prose — useful for teaching material and for building (SMILES, name, description) datasets.

Coverage

Round-trip accuracy against public datasets (details and rerun instructions in evaluations/):

dataset QM9 PubChem ZINC22
coverage 100.00% 99.76% 97.70%

The package is in beta. Naming is solid across common organic chemistry; exotic corners of the Blue Book — and stereodescriptor edge cases — are still being filled in. Bug reports are very welcome.

Install

pip install openclatura

Optional extras:

extra adds
[opsin] py2opsin for OPSIN-based round-trip verification
[datasets] datasets + tqdm for PubChem/QM9-style evaluations
[web] FastAPI + uvicorn for the HTTP service
[dev] pytest, ruff, pre-commit, hypothesis, py2opsin
pip install "openclatura[opsin,datasets]"

The default install does not include OPSIN verification. Install the [opsin] extra and make sure Java 8+ is available if you want round-trip verification through OPSIN:

pip install "openclatura[opsin]"
java -version

If py2opsin is installed but Java is missing or inaccessible, OPSIN verification is skipped gracefully. The name generation still succeeds and the verification status is reported as skipped_no_java.

Quick start

from openclatura import name_smiles

name_smiles("CCO")          # 'ethanol'
name_smiles("c1ccccc1")     # 'benzene'
name_smiles("CC(=O)O")      # 'acetic acid'

Typed result with rules hit + OPSIN round-trip

For everything richer than the bare string, use openclatura.name:

from openclatura import name

result = name("CC(=O)Nc1ccccc1", include_trace=True, verify_opsin=True)

result.name           # 'N-phenylacetamide'
result.smiles         # 'CC(=O)Nc1ccccc1'
result.ok             # True
result.rules_hit      # ('P-44', 'P-45', 'P-41', 'P-61', 'P-67')
result.rule_hints     # ('Parent hydride / parent structure: Blue Book P-44 and P-45.',
                      #  'Principal characteristic groups: Blue Book P-41, P-44, and P-61-P-67.')
result.opsin_check.status   # 'matched' | 'mismatched' | 'skipped_no_java' | ...
result.verified       # True when opsin_check is matched

verify_opsin defaults to False. When set to True, verification is best-effort and does not raise if OPSIN support is unavailable:

  • no py2opsin installed: result.opsin_check.status == "skipped_no_opsin"
  • py2opsin installed but Java unavailable: status == "skipped_no_java"
  • OPSIN parses and round-trips: status == "matched" or "mismatched"

Errors do not raise — they are captured on result.error, which makes the batch API safe to point at noisy datasets:

from openclatura import name_many

results = name_many(
    ["CCO", "c1ccccc1", "definitely-not-a-smiles"],
    processes="auto",       # or an integer, or 1 for in-process
    verify_opsin=False,
)
[r.name for r in results if r.ok]  # ['ethanol', 'benzene']

Naming an existing RDKit molecule

If you already hold an rdkit.Chem.rdchem.Mol — from an SD file, a reaction, or an earlier step in a pipeline — skip the SMILES round-trip:

from rdkit import Chem
from openclatura import name_rdkit_mol, name_mol, name_many

for mol in Chem.SDMolSupplier("compounds.sdf"):
    if mol is not None:
        print(name_rdkit_mol(mol))          # -> 'benzoic acid'

name_mol(mol)                                # typed NamingResult, as `name`
name_many([mol, "CCO"])                      # batches take either form

The input molecule is never modified, explicit hydrogens (as SD files usually carry them) are handled, and name_rdkit_mol_with_trace / analyze_rdkit_mol mirror their SMILES counterparts.

For the full decision trace (one TraceStep per phase: parse, perception, parent selection, numbering, assembly, …):

from openclatura import analyze_smiles

analysis = analyze_smiles("CC(=O)Nc1ccccc1")
for step in analysis.decisions:
    print(step.phase, step.decision, step.reason)

CLI

openclatura name "CC(=O)Nc1ccccc1"            # → N-phenylacetamide
openclatura name "CC(=O)Nc1ccccc1" --json     # JSON with trace + rules
openclatura batch smiles.txt --output names.jsonl --processes auto

The CLI verifies with OPSIN by default when possible. This is different from the Python API, where verify_opsin=False by default. Disable CLI verification with --no-verify:

openclatura name "CC(=O)Nc1ccccc1" --no-verify

If OPSIN support is unavailable, the command still prints the generated name and reports the verification status:

N-phenylacetamide
  opsin: skipped_no_java

Other possible skipped statuses include skipped_no_opsin when py2opsin is not installed. Install openclatura[opsin] and Java 8+ for full CLI verification.

Explaining a name

Two renderers turn the same decision trace into prose. Both are deterministic — same input, same output, no LLM in the loop.

describe — rule-by-rule

A multi-paragraph account of how the name was built, keyed to the Blue Book rules that fired:

from openclatura import describe

d = describe("CC(=O)Nc1ccccc1")
print(d)            # multi-paragraph prose
d.rules_hit         # ('P-44', 'P-45', 'P-41', 'P-61', 'P-67')
d.components[0]     # DescribedComponent(phase='parse', text='RDKit parsed ...')
The molecule CC(=O)Nc1ccccc1 is named **N-phenylacetamide**.

Processed SMILES: CC(=O)Nc1ccccc1
Atom ids in that SMILES: C{0}C{1}(=O{2})N{3}c{4}1c{5}c{6}c{7}c{8}c{9}1

RDKit parsed the SMILES into a molecular graph with 10 atoms and 10 bonds.
The structure is a single connected component, named in one piece.
Perception identified 1 functional group (amide); principal candidate: amide.
The amide group is selected as the principal group by the registered seniority order.
The parent skeleton is a 2-atom acyclic chain (atoms [0, 1]).
Parent numbering was selected from the final atom-to-locant map (atom 1->1, atom 0->2).

Component and substituent structure:
- Component 1: N-phenylacetamide covers 10 atoms and 10 bonds.
  Parent: chain parent with 2 atoms retained as acetamide; locants 1->1, 2->0.
  Principal group: amide at 1 (atoms 1,2,3).
  - Substituent at N: phenyl covers 6 atoms and 6 bonds.
    Parent: ring parent with 6 atoms retained as benzene; locants 1->4, 2->5, 3->6, 4->7, 5->8, 6->9.

Name pieces contributed by the trace:
- parent skeleton: contributes "acetamide" (atoms 0,1; bonds 1).
- amide: contributes "amide" (atoms 1,2,3; bonds 2,3).
  - substituent parent skeleton: contributes "benzene" (atoms 4,5,6,7,8,9; bonds 5,6,7,8,9,10).

IUPAC Blue Book rules applied: P-44, P-45, P-41, P-61, P-67.

describe_human — how a chemist would say it

The same information, phrased the way a person would explain the structure at a whiteboard, with every position tied back to an atom index in the SMILES:

from openclatura import describe_human

d = describe_human("CN1C=NC2=C1C(=O)N(C(=O)N2C)C")   # caffeine
print(d.text)
Input SMILES: CN1C=NC2=C1C(=O)N(C(=O)N2C)C
Processed SMILES: Cn1cnc2c1c(=O)n(C)c(=O)n2C
Atom ids in that SMILES: C{0}n{1}1c{2}n{3}c{4}2c{5}1c{6}(=O{7})n{8}(C{13})c{9}(=O{10})n{11}2C{12}

The molecule is named 1,3,7-trimethyl-3,7-dihydro-1H-purine-2,6-dione.

The molecule is built around the retained purine parent, 9-atom polycyclic heteroskeleton.
The parent ring basis comprises a 5-membered ring of aromatic atoms containing position 4 (atom id 4), position 5 (atom id 5), position 7 (atom id 1), position 8 (atom id 2), and position 9 (atom id 3) and a 6-membered ring of aromatic atoms containing position 1 (atom id 8), position 2 (atom id 9), position 3 (atom id 11), position 4 (atom id 4), position 5 (atom id 5), and position 6 (atom id 6).
These rings share parent bonds between position 4 (atom id 4) and position 5 (atom id 5).
Within that parent framework, there is nitrogen at positions 1 (atom id 8), 3 (atom id 11), 7 (atom id 1), and 9 (atom id 3).
The principal characteristic feature is oxo groups at positions 2 (atom id 9) and 6 (atom id 6).
Attached to this framework are methyl groups at positions 1 (atom id 8), 3 (atom id 11), and 7 (atom id 1).

Development

git clone https://github.com/lamalab-org/openclatura
cd openclatura
pip install -e ".[dev]"

# run the unit + round-trip tests
pytest

# run only fast tests
pytest -m "not slow and not dataset and not golden"

# strict RDKit-version regression suite (also runs in the rdkit-compat CI job)
pytest -m golden

# lint and format
ruff check --fix src/openclatura
ruff format src/openclatura

Java is required for the OPSIN-based round-trip checks (see py2opsin).

HTTP service (Docker)

The [web] extra ships a FastAPI app with name, batch, describe and healthz endpoints. The bundled Dockerfile includes a headless JRE so verify_opsin=True works out of the box.

# build + run
docker build -t openclatura:local .
docker run --rm -p 8000:8000 openclatura:local

# or via compose
docker compose -f docker/compose.yaml up --build

Call the API:

curl -X POST localhost:8000/name -H 'content-type: application/json' \
     -d '{"smiles":"CC(=O)Nc1ccccc1","include_trace":true,"verify_opsin":true}'

curl -X POST localhost:8000/batch -H 'content-type: application/json' \
     -d '{"smiles":["CCO","c1ccccc1","CC(=O)O"],"processes":1}'

curl -X POST localhost:8000/describe -H 'content-type: application/json' \
     -d '{"smiles":"CC(=O)Nc1ccccc1"}'

OpenAPI docs are served at http://localhost:8000/docs.

Changelog

See CHANGELOG.md for release notes.

License

MIT. See LICENSE.

How to cite

If you use Openclatura in your research, please cite the Openclatura preprint:

@article{openclatura2026,
  author  = {Mirza, Adrian and Jablonka, Kevin Maik and Fedorov, Rostislav},
  title   = {Openclatura--An Open-Source Nomenclature Framework for Rule-Based Molecule Naming},
  journal = {ChemRxiv},
  year    = {2026},
  month   = jul,
  day     = {15},
  doi     = {10.26434/chemrxiv.15006114/v2},
  url     = {https://doi.org/10.26434/chemrxiv.15006114/v2},
  note    = {Preprint}
}

If you are using OPSIN for verification, please cite the original OPSIN publication:

@article{lowe2011opsin,
  author  = {Lowe, Daniel M. and Corbett, Peter T. and Murray-Rust, Peter and Glen, Robert C.},
  title   = {Chemical Name to Structure: {OPSIN}, an Open Source Solution},
  journal = {Journal of Chemical Information and Modeling},
  year    = {2011},
  volume  = {51},
  number  = {3},
  pages   = {739--753},
  doi     = {10.1021/ci100384d},
  url     = {https://doi.org/10.1021/ci100384d}
}

Metadata

Release files for openclatura 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openclatura 0.4.1
File Size Uploaded
openclatura-0.4.1.tar.gz 1.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for openclatura 0.4.1
File Interpreter ABI Platform
openclatura-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.7 MB

Release files / openclatura-0.4.1.tar.gz

Download URL openclatura-0.4.1.tar.gz
Size 1.0 MB
Tags Source
SHA-256 checksum
How to use checksums
b9985e574f5ac1ead38e782b5708129376b3983290626116947a959c84a4cf1d
BLAKE2b-256 checksum
How to use checksums
e2c0a656df14a6c69db44a56bc340bc67a37e0044325992028a6a00b004007f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / openclatura-0.4.1-py3-none-any.whl

Download URL openclatura-0.4.1-py3-none-any.whl
Size 645.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
544b50696fe12a92baa4e9e248cfdaadc8b7493a7ffa4f2c8a22df325f98f3a4
BLAKE2b-256 checksum
How to use checksums
b95f5f03f53784ae8dfe31b9fbc1df051bb570305fece7bffb1f66805c12e198
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.3

2 release files

0.4.2

2 release files

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page