Skip to main content

📜 Papyrus Structure Pipeline

PyPI version Supported Python versions License: MIT Tests Ruff

A small, opinionated molecule standardization pipeline built on top of the ChEMBL Structure Pipeline and RDKit. It turns arbitrary input structures into a consistent parent form suitable for bioactivity datasets: salts and metals stripped, charges neutralized, tautomers canonicalized, and mixtures/inorganics/out-of-range molecular weights filtered out. First used to curate the Papyrus bioactivity dataset.

✨ Features

  • 🧪 ChEMBL-based standardization — wraps the ChEMBL Structure Pipeline's parent-structure extraction and normalization, run twice (before and after tautomer canonicalization) for a consistent final form.
  • 🧂 Configurable salt & metal stripping — removes a user-extensible set of salts/metals beyond what the ChEMBL pipeline already handles.
  • ⚛️ Charge neutralization — uncharges ionizable groups and zwitterions.
  • 🔀 Bounded tautomer canonicalization — deterministic canonical tautomer picking with a capped search, so runtime stays predictable even for polyphenol-like molecules with many tautomerizable sites.
  • 🧬 Organic/inorganic filtering — configurable allowed-atom list and C-C bond requirement.
  • 🧩 Mixture & size filtering — drop multi-fragment mixtures and molecules outside a molecular-weight window.
  • 🔍 Rich failure reportingreturn_type=True reports why a molecule was rejected (mixture, inorganic, too small/large, standardization error) instead of just returning None.
  • 🏷️ Typed API — ships py.typed, fully type-hinted, mypy-clean.

Citing

If you use this package, please cite the Papyrus dataset it was first used in:

DOI

📦 Installation

pip install papyrus-structure-pipeline

Or from source:

git clone https://github.com/OlivierBeq/Papyrus_structure_pipeline.git
pip install ./Papyrus_structure_pipeline

🛠️ Requirements

💡 Usage

Standardize a compound

Comparison to the ChEMBL Structure Pipeline:

from rdkit import Chem
from chembl_structure_pipeline import standardizer as ChEMBL_standardizer
from papyrus_structure_pipeline import standardizer as Papyrus_standardizer

# CHEMBL1560279
smiles = "CCN(CC)C(=O)[n+]1ccc(OC)cc1.c1ccc([B-](c2ccccc2)(c2ccccc2)c2ccccc2)cc1"

mol = Chem.MolFromSmiles(smiles)
out1 = ChEMBL_standardizer.standardize_mol(mol)
out2 = Papyrus_standardizer.standardize(mol)

print(Chem.MolToSmiles(out1))
# CCN(CC)C(=O)[n+]1ccc(OC)cc1.c1ccc([B-](c2ccccc2)(c2ccccc2)c2ccccc2)cc1

print(Chem.MolToSmiles(out2))
# CCN(CC)C(=O)[n+]1ccc(OC)cc1

Get details on the standardization to identify why it fails for some molecules:

smiles_list = [
    # erlotinib
    "n1cnc(c2cc(c(cc12)OCCOC)OCCOC)Nc1cc(ccc1)C#C",
    # midecamycin
    "CCC(=O)O[C@@H]1CC(=O)O[C@@H](C/C=C/C=C/[C@@H]([C@@H](C[C@@H]([C@@H]([C@H]1OC)O[C@H]2[C@@H]([C@H]([C@@H]([C@H](O2)C)O[C@H]3C[C@@]([C@H]([C@@H](O3)C)OC(=O)CC)(C)O)N(C)C)O)CC=O)C)O)C",
    # selenofolate
    "C1=CC(=CC=C1C(=O)NC(CCC(=O)OCC[Se]C#N)C(=O)O)NCC2=CN=C3C(=N2)C(=O)NC(=N3)N",
    # cisplatin
    "N.N.Cl[Pt]Cl",
]

for smiles in smiles_list:
    mol = Chem.MolFromSmiles(smiles)
    print(Papyrus_standardizer.standardize(mol, return_type=True))

# (<rdkit.Chem.rdchem.Mol object at 0x000000946F99B580>, <StandardizationResult.CORRECT_MOLECULE: 1>)
# (None, <StandardizationResult.NON_SMALL_MOLECULE: 2>)
# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)
# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)

Allow other atoms to be considered organic:

smiles = "CCN(CC)C(=O)C1=CC=C(S1)C2=C3C=CC(=[N+](C)C)C=C3[Se]C4=C2C=CC(=C4)N(C)C.F[P-](F)(F)(F)(F)F"
mol = Chem.MolFromSmiles(smiles)

print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)

Papyrus_standardizer.ORGANIC_ATOMS.append('Se')

print(Papyrus_standardizer.standardize(mol, return_type=True))
# (<rdkit.Chem.rdchem.Mol object at 0x0000009F24D15F90>, <StandardizationResult.CORRECT_MOLECULE: 1>)

Papyrus_standardizer.ORGANIC_ATOMS = Papyrus_standardizer.ORGANIC_ATOMS[:-1]

print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.INORGANIC_MOLECULE: 3>)

Add custom substructures to be removed as salts:

# lomitapide
smiles = "C1CN(CCC1NC(=O)C2=CC=CC=C2C3=CC=C(C=C3)C(F)(F)F)CCCCC4(C5=CC=CC=C5C6=CC=CC=C64)C(=O)NCC(F)(F)F.c1ccccc1"
mol = Chem.MolFromSmiles(smiles)

print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)

Papyrus_standardizer.SALTS.append('c1ccccc1')

print(Papyrus_standardizer.standardize(mol, return_type=True))
# (<rdkit.Chem.rdchem.Mol object at 0x0000009F24D15F90>, <StandardizationResult.CORRECT_MOLECULE: 1>)

Papyrus_standardizer.SALTS = Papyrus_standardizer.SALTS[:-1]

print(Papyrus_standardizer.standardize(mol, return_type=True))
# (None, <StandardizationResult.MIXTURE_MOLECULE: 4>)

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

📚 API Documentation

def standardize(mol,
                remove_additional_salts=True, remove_additional_metals=True,
                filter_mixtures=True, filter_inorganic=True, filter_non_small_molecule=True,
                canonicalize_tautomer=True, small_molecule_min_mw=200, small_molecule_max_mw=800,
                tautomer_allow_stereo_removal=True, tautomer_max_tautomers=50, return_type=False,
                raise_error=True
                ) -> Chem.Mol | None:

Standardizes a molecule: ChEMBL parent extraction, salt/metal stripping, mixture/inorganic/size filtering, charge neutralization, tautomer canonicalization, then ChEMBL parent extraction again.

Parameters

  • mol : Chem.Mol RDKit molecule object to standardize.
  • remove_additional_salts : bool Removes a custom set of fragments if present in the molecule object.
  • remove_additional_metals : bool Removes metal fragments if present in the molecule object. Ignored if remove_additional_salts is set to False.
  • filter_mixtures : bool Return None if the molecule is a mixture.
  • filter_inorganic : bool Return None if the molecule is inorganic.
  • filter_non_small_molecule : bool Return None if the molecule is not a small molecule.
  • canonicalize_tautomer : bool Canonicalize the tautomeric state of the molecule.
  • small_molecule_min_mw : float Molecular weight under which a molecule is considered too small.
  • small_molecule_max_mw : float Molecular weight above which a molecule is considered too big.
  • tautomer_allow_stereo_removal : bool Allow the tautomer search algorithm to remove stereocenters.
  • tautomer_max_tautomers : int Maximum number of tautomers to consider by the tautomer search algorithm. Bounds worst-case runtime for molecules with many tautomerizable sites.
  • return_type : bool Add a StandardizationResult to the return value.
  • raise_error : bool Raise an exception upon failure, otherwise return None (or (None, StandardizationResult.STANDARDIZATION_ERROR) if return_type is also set).

def is_organic(mol, return_type=False) -> bool:

Returns whether the RDKit molecule is organic: no ChEMBL exclusion flag, at least one C-C bond, and made up only of atoms listed in ORGANIC_ATOMS.

Parameters

  • mol : Chem.Mol RDKit molecule object to check the organic nature of.
  • return_type : bool Add an InorganicSubtype to the return value.

def is_small_molecule(mol, min_molwt=200, max_molwt=800) -> bool:

Returns whether the RDKit molecule has a molecular weight within min_molwt and max_molwt.

Parameters

  • mol : Chem.Mol RDKit molecule object to check the molecular weight of.
  • min_molwt : float Molecular weight under which a molecule is considered too small.
  • max_molwt : float Molecular weight above which a molecule is considered too big.

def is_mixture(mol) -> bool:

Returns whether the RDKit molecule is composed of multiple fragments.

Parameters

  • mol : Chem.Mol RDKit molecule object to check the fragment count of.

Result types

  • StandardizationResult — outcome of standardize(): CORRECT_MOLECULE, NON_SMALL_MOLECULE, INORGANIC_MOLECULE, MIXTURE_MOLECULE, STANDARDIZATION_ERROR.
  • InorganicSubtype — reason is_organic() returned False: NO_CC_BOND, NOT_CHONPSFClIBrB, EXCLUSION_FLAG_SET.
  • SaltStrippingResult — outcome of the internal salt-stripping step: STRIPPED_MOLECULE, EMPTY_MOLECULE.

Module-level configuration

  • SALTS : list[str] — extra salt SMILES stripped beyond the ChEMBL pipeline's own list; user-extensible.
  • METALS : list[str] — metal SMILES stripped when remove_additional_metals=True.
  • ORGANIC_ATOMS : list[str] — atom symbols allowed for a molecule to be considered organic.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

papyrus_structure_pipeline-0.1.0.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

papyrus_structure_pipeline-0.1.0-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file papyrus_structure_pipeline-0.1.0.tar.gz.

File metadata

File hashes

Hashes for papyrus_structure_pipeline-0.1.0.tar.gz
Algorithm Hash digest
SHA256 dc484e3f16bc793853b1759e7a7debbe114171d1947374cec22492dbf10be49a
MD5 67f6396d63d751ba0755e6fce4d880fe
BLAKE2b-256 776aec6e3ed7edb4bbdbef29a47a38a204276c375e551f6a724c342a3f18e347

See more details on using hashes here.

File details

Details for the file papyrus_structure_pipeline-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for papyrus_structure_pipeline-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 24cd8a9beb8581421708cc19d02c9bc56c0c01cbcb4e30775aa84edc818181d2
MD5 311bdb9e6b4dfbc25dd6130b48380518
BLAKE2b-256 f97c674ba8da147e854b0cbb951bb1ab909c4ceedda53490e129e25ce6ce6379

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.0

2 files

This release

0.1.0 This release

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1.post4

2 files

0.0.1.post3

2 files

0.0.1.post2

2 files

0.0.1.post1

2 files

0.0.1

2 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