Skill Tracer
Skills › Scientific Agent Skills › rdkit › core_capabilities.md

core_capabilities.md

635 lines · skills/rdkit/references/core_capabilities.md at 5f2bc03

Open on GitHub
1# RDKit Core Capabilities
2
3The twelve capability areas in full, with worked code: molecular I/O and creation,
4sanitization and validation, analysis and properties, descriptors, fingerprints and
5similarity, substructure searching with SMARTS, chemical reactions, 2D/3D coordinate
6generation, visualization, molecular modification, hashes and standardization, and
7pharmacophore and 3D features.
8
9Code fragments with undefined inputs such as `mol1`, paths, or atom IDs are
10illustrative templates. The checked, self-contained examples and coverage are
11listed in [review.md](review.md).

RDKit Core Capabilities

The twelve capability areas in full, with worked code: molecular I/O and creation, sanitization and validation, analysis and properties, descriptors, fingerprints and similarity, substructure searching with SMARTS, chemical reactions, 2D/3D coordinate generation, visualization, molecular modification, hashes and standardization, and pharmacophore and 3D features.

Code fragments with undefined inputs such as mol1, paths, or atom IDs are illustrative templates. The checked, self-contained examples and coverage are listed in review.md.

Raw lines 1 to 12
13## Core Capabilities

Core Capabilities

Raw lines 13 to 14
15### 1. Molecular I/O and Creation
17**Reading Molecules:**
19Read molecular structures from various formats:
21```python
22from rdkit import Chem
24# From SMILES strings
25mol = Chem.MolFromSmiles('Cc1ccccc1') # Returns Mol object or None
27# From MOL files
28mol = Chem.MolFromMolFile('path/to/file.mol')
30# From MOL blocks (string data)
31mol = Chem.MolFromMolBlock(mol_block_string)
33# From InChI
34mol = Chem.MolFromInchi('InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H')
35```
37**Writing Molecules:**
39Convert molecules to text representations:
41```python
42# To canonical SMILES
43smiles = Chem.MolToSmiles(mol)
45# To MOL block
46mol_block = Chem.MolToMolBlock(mol)
48# To InChI
49inchi = Chem.MolToInchi(mol)
50```
52**Batch Processing:**
54For processing multiple molecules, use Supplier/Writer objects:
56```python
57# Read SDF files
58suppl = Chem.SDMolSupplier('molecules.sdf')
59for mol in suppl:
60 if mol is not None: # Check for parsing errors
61 # Process molecule
62 pass
64# Read SMILES files
65suppl = Chem.SmilesMolSupplier('molecules.smi', titleLine=False)
66smiles_mols = [m for m in suppl if m is not None] # iterate, do not use list(suppl)
68# For large files or compressed data
69import gzip
71with gzip.open('molecules.sdf.gz') as f:
72 suppl = Chem.ForwardSDMolSupplier(f)
73 for mol in suppl:
74 # Process molecule
75 pass
77# Multithreaded processing for large datasets
78suppl = Chem.MultithreadedSDMolSupplier('molecules.sdf')
80# Write molecules to SDF
81writer = Chem.SDWriter('output.sdf')
82for mol in molecules:
83 writer.write(mol)
84writer.close()
85```
87**Important Notes:**
88- Tested 2026.03.6 behavior: `list(SmilesMolSupplier(...))` on a fresh supplier
89 can return an empty list because of length-hint interaction. A comprehension or
90 ordinary iteration reads the records correctly; the bundled reader uses source
91 lines directly.
92- Multithreaded suppliers are still marked experimental upstream; they may
93 reorder results. Save `GetLastRecordId()` at each iteration.
94- Many molecular parse failures return `None`; missing files, wrong argument types,
95 and some sanitization errors raise exceptions. Empty SMILES gives a zero-atom Mol; reject it for these workflows.
96- Always check for `None` before processing molecules
97- Molecules are automatically sanitized on import (validates valence, perceives aromaticity)

1. Molecular I/O and Creation

Reading Molecules:

Read molecular structures from various formats:

from rdkit import Chem

# From SMILES strings
mol = Chem.MolFromSmiles('Cc1ccccc1')  # Returns Mol object or None

# From MOL files
mol = Chem.MolFromMolFile('path/to/file.mol')

# From MOL blocks (string data)
mol = Chem.MolFromMolBlock(mol_block_string)

# From InChI
mol = Chem.MolFromInchi('InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H')

Writing Molecules:

Convert molecules to text representations:

# To canonical SMILES
smiles = Chem.MolToSmiles(mol)

# To MOL block
mol_block = Chem.MolToMolBlock(mol)

# To InChI
inchi = Chem.MolToInchi(mol)

Batch Processing:

For processing multiple molecules, use Supplier/Writer objects:

# Read SDF files
suppl = Chem.SDMolSupplier('molecules.sdf')
for mol in suppl:
    if mol is not None:  # Check for parsing errors
        # Process molecule
        pass

# Read SMILES files
suppl = Chem.SmilesMolSupplier('molecules.smi', titleLine=False)
smiles_mols = [m for m in suppl if m is not None]  # iterate, do not use list(suppl)

# For large files or compressed data
import gzip

with gzip.open('molecules.sdf.gz') as f:
    suppl = Chem.ForwardSDMolSupplier(f)
    for mol in suppl:
        # Process molecule
        pass

# Multithreaded processing for large datasets
suppl = Chem.MultithreadedSDMolSupplier('molecules.sdf')

# Write molecules to SDF
writer = Chem.SDWriter('output.sdf')
for mol in molecules:
    writer.write(mol)
writer.close()

Important Notes:

  • Tested 2026.03.6 behavior: list(SmilesMolSupplier(...)) on a fresh supplier can return an empty list because of length-hint interaction. A comprehension or ordinary iteration reads the records correctly; the bundled reader uses source lines directly.
  • Multithreaded suppliers are still marked experimental upstream; they may reorder results. Save GetLastRecordId() at each iteration.
  • Many molecular parse failures return None; missing files, wrong argument types, and some sanitization errors raise exceptions. Empty SMILES gives a zero-atom Mol; reject it for these workflows.
  • Always check for None before processing molecules
  • Molecules are automatically sanitized on import (validates valence, perceives aromaticity)
Raw lines 15 to 98
99### 2. Molecular Sanitization and Validation
101RDKit automatically sanitizes molecules during parsing, checking supported valences and assigning graph properties such as aromaticity.
102This is not a chemical plausibility, stability, or synthetic-feasibility proof.
104**Sanitization Control:**
106```python
107# Disable automatic sanitization
108mol = Chem.MolFromSmiles('C1=CC=CC=C1', sanitize=False)
110# Manual sanitization
111Chem.SanitizeMol(mol)
113# Detect problems before sanitization
114problems = Chem.DetectChemistryProblems(mol)
115for problem in problems:
116 print(problem.GetType(), problem.Message())
118# Partial sanitization (skip specific steps)
119Chem.SanitizeMol(mol, sanitizeOps=Chem.SANITIZE_ALL ^ Chem.SANITIZE_PROPERTIES)
120```
122**Common Sanitization Issues:**
123- Atoms with explicit valence exceeding maximum allowed will raise exceptions
124- Invalid aromatic rings will cause kekulization errors
125- Radical electrons may not be properly assigned without explicit specification

2. Molecular Sanitization and Validation

RDKit automatically sanitizes molecules during parsing, checking supported valences and assigning graph properties such as aromaticity. This is not a chemical plausibility, stability, or synthetic-feasibility proof.

Sanitization Control:

# Disable automatic sanitization
mol = Chem.MolFromSmiles('C1=CC=CC=C1', sanitize=False)

# Manual sanitization
Chem.SanitizeMol(mol)

# Detect problems before sanitization
problems = Chem.DetectChemistryProblems(mol)
for problem in problems:
    print(problem.GetType(), problem.Message())

# Partial sanitization (skip specific steps)
Chem.SanitizeMol(mol, sanitizeOps=Chem.SANITIZE_ALL ^ Chem.SANITIZE_PROPERTIES)

Common Sanitization Issues:

  • Atoms with explicit valence exceeding maximum allowed will raise exceptions
  • Invalid aromatic rings will cause kekulization errors
  • Radical electrons may not be properly assigned without explicit specification
Raw lines 99 to 126
127### 3. Molecular Analysis and Properties
129**Accessing Molecular Structure:**
131```python
132# Iterate atoms and bonds
133for atom in mol.GetAtoms():
134 print(atom.GetSymbol(), atom.GetIdx(), atom.GetDegree())
136for bond in mol.GetBonds():
137 print(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond.GetBondType())
139# Ring information
140ring_info = mol.GetRingInfo()
141ring_info.NumRings()
142ring_info.AtomRings() # Returns tuples of atom indices
144# Check if atom is in ring
145atom = mol.GetAtomWithIdx(0)
146atom.IsInRing()
147atom.IsInRingSize(6) # Check for 6-membered rings
149# Find the symmetrized SSSR (can contain more rings than the minimal SSSR)
150from rdkit.Chem import GetSymmSSSR
151rings = GetSymmSSSR(mol)
152```
154**Stereochemistry:**
156```python
157# Find chiral centers
158from rdkit.Chem import FindMolChiralCenters
159chiral_centers = FindMolChiralCenters(mol, includeUnassigned=True,
160 useLegacyImplementation=False)
161# Returns list of (atom_idx, chirality) tuples
163# Assign stereochemistry from existing 3D coordinates; this may overwrite tags
164from rdkit.Chem import AssignStereochemistryFrom3D
165AssignStereochemistryFrom3D(mol)
167# Check bond stereochemistry
168bond = mol.GetBondWithIdx(0)
169stereo = bond.GetStereo() # STEREONONE, STEREOZ, STEREOE, etc.
170```
172**Fragment Analysis:**
174```python
175# Get disconnected fragments
176frags = Chem.GetMolFrags(mol, asMols=True)
178# Fragment on specific bonds
179from rdkit.Chem import FragmentOnBonds
180frag_mol = FragmentOnBonds(mol, [bond_idx1, bond_idx2])
182# Extract Murcko scaffold (not a ring-system count)
183from rdkit.Chem.Scaffolds import MurckoScaffold
184scaffold = MurckoScaffold.GetScaffoldForMol(mol)
185```

3. Molecular Analysis and Properties

Accessing Molecular Structure:

# Iterate atoms and bonds
for atom in mol.GetAtoms():
    print(atom.GetSymbol(), atom.GetIdx(), atom.GetDegree())

for bond in mol.GetBonds():
    print(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond.GetBondType())

# Ring information
ring_info = mol.GetRingInfo()
ring_info.NumRings()
ring_info.AtomRings()  # Returns tuples of atom indices

# Check if atom is in ring
atom = mol.GetAtomWithIdx(0)
atom.IsInRing()
atom.IsInRingSize(6)  # Check for 6-membered rings

# Find the symmetrized SSSR (can contain more rings than the minimal SSSR)
from rdkit.Chem import GetSymmSSSR
rings = GetSymmSSSR(mol)

Stereochemistry:

# Find chiral centers
from rdkit.Chem import FindMolChiralCenters
chiral_centers = FindMolChiralCenters(mol, includeUnassigned=True,
                                    useLegacyImplementation=False)
# Returns list of (atom_idx, chirality) tuples

# Assign stereochemistry from existing 3D coordinates; this may overwrite tags
from rdkit.Chem import AssignStereochemistryFrom3D
AssignStereochemistryFrom3D(mol)

# Check bond stereochemistry
bond = mol.GetBondWithIdx(0)
stereo = bond.GetStereo()  # STEREONONE, STEREOZ, STEREOE, etc.

Fragment Analysis:

# Get disconnected fragments
frags = Chem.GetMolFrags(mol, asMols=True)

# Fragment on specific bonds
from rdkit.Chem import FragmentOnBonds
frag_mol = FragmentOnBonds(mol, [bond_idx1, bond_idx2])

# Extract Murcko scaffold (not a ring-system count)
from rdkit.Chem.Scaffolds import MurckoScaffold
scaffold = MurckoScaffold.GetScaffoldForMol(mol)
Raw lines 127 to 186
187### 4. Molecular Descriptors and Properties
189**Basic Descriptors:**
191```python
192from rdkit.Chem import Descriptors
194# Molecular weight
195mw = Descriptors.MolWt(mol)
196exact_mw = Descriptors.ExactMolWt(mol)
198# LogP (lipophilicity)
199logp = Descriptors.MolLogP(mol)
201# Topological polar surface area
202tpsa = Descriptors.TPSA(mol)
204# Number of hydrogen bond donors/acceptors
205hbd = Descriptors.NumHDonors(mol)
206hba = Descriptors.NumHAcceptors(mol)
208# Number of rotatable bonds
209rot_bonds = Descriptors.NumRotatableBonds(mol)
211# Number of aromatic rings
212aromatic_rings = Descriptors.NumAromaticRings(mol)
213```
215**Batch Descriptor Calculation:**
217```python
218# Calculate all descriptors at once
219all_descriptors = Descriptors.CalcMolDescriptors(mol)
220# Returns dictionary: {'MolWt': 180.16, 'MolLogP': 1.23, ...}
222# Get list of available descriptor names
223descriptor_names = [desc[0] for desc in Descriptors._descList]
224```
226**Lipinski's Rule of Five:**
228```python
229# Check drug-likeness
230mw = Descriptors.MolWt(mol) <= 500
231logp = Descriptors.MolLogP(mol) <= 5
232hbd = Descriptors.NumHDonors(mol) <= 5
233hba = Descriptors.NumHAcceptors(mol) <= 10
235is_drug_like = mw and logp and hbd and hba
236```

4. Molecular Descriptors and Properties

Basic Descriptors:

from rdkit.Chem import Descriptors

# Molecular weight
mw = Descriptors.MolWt(mol)
exact_mw = Descriptors.ExactMolWt(mol)

# LogP (lipophilicity)
logp = Descriptors.MolLogP(mol)

# Topological polar surface area
tpsa = Descriptors.TPSA(mol)

# Number of hydrogen bond donors/acceptors
hbd = Descriptors.NumHDonors(mol)
hba = Descriptors.NumHAcceptors(mol)

# Number of rotatable bonds
rot_bonds = Descriptors.NumRotatableBonds(mol)

# Number of aromatic rings
aromatic_rings = Descriptors.NumAromaticRings(mol)

Batch Descriptor Calculation:

# Calculate all descriptors at once
all_descriptors = Descriptors.CalcMolDescriptors(mol)
# Returns dictionary: {'MolWt': 180.16, 'MolLogP': 1.23, ...}

# Get list of available descriptor names
descriptor_names = [desc[0] for desc in Descriptors._descList]

Lipinski's Rule of Five:

# Check drug-likeness
mw = Descriptors.MolWt(mol) <= 500
logp = Descriptors.MolLogP(mol) <= 5
hbd = Descriptors.NumHDonors(mol) <= 5
hba = Descriptors.NumHAcceptors(mol) <= 10

is_drug_like = mw and logp and hbd and hba
Raw lines 187 to 237
238### 5. Fingerprints and Molecular Similarity
240**Fingerprint Types:**
242```python
243from rdkit.Chem import rdFingerprintGenerator
244from rdkit.Chem import MACCSkeys
246# RDKit topological fingerprint
247rdk_gen = rdFingerprintGenerator.GetRDKitFPGenerator(minPath=1, maxPath=7, fpSize=2048)
248fp = rdk_gen.GetFingerprint(mol)
250# Morgan fingerprints (circular fingerprints, similar to ECFP)
251# Modern API using rdFingerprintGenerator
252morgan_gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
253fp = morgan_gen.GetFingerprint(mol)
254# Count-based fingerprint
255fp_count = morgan_gen.GetCountFingerprint(mol)
257# MACCS keys: 167-bit vector, 166 keys and unused bit 0
258fp = MACCSkeys.GenMACCSKeys(mol)
260# Atom pair fingerprints
261ap_gen = rdFingerprintGenerator.GetAtomPairGenerator()
262fp = ap_gen.GetFingerprint(mol)
264# Topological torsion fingerprints
265tt_gen = rdFingerprintGenerator.GetTopologicalTorsionGenerator()
266fp = tt_gen.GetFingerprint(mol)
268# Avalon fingerprints (if available)
269from rdkit.Avalon import pyAvalonTools
270fp = pyAvalonTools.GetAvalonFP(mol)
271```
273**Similarity Calculation:**
275```python
276from rdkit import DataStructs
277from rdkit.Chem import rdFingerprintGenerator
279# Generate fingerprints using generator
280mfpgen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
281fp1 = mfpgen.GetFingerprint(mol1)
282fp2 = mfpgen.GetFingerprint(mol2)
284# Calculate Tanimoto similarity
285similarity = DataStructs.TanimotoSimilarity(fp1, fp2)
287# Calculate similarity for multiple molecules
288fps = [mfpgen.GetFingerprint(m) for m in [mol2, mol3, mol4]]
289similarities = DataStructs.BulkTanimotoSimilarity(fp1, fps)
291# Other similarity metrics
292dice = DataStructs.DiceSimilarity(fp1, fp2)
293cosine = DataStructs.CosineSimilarity(fp1, fp2)
294```
296**Clustering and Diversity:**
298```python
299# Butina clustering based on fingerprint similarity
300from rdkit.ML.Cluster import Butina
302# Calculate distance matrix
303dists = []
304mfpgen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
305fps = [mfpgen.GetFingerprint(mol) for mol in mols]
306for i in range(len(fps)):
307 sims = DataStructs.BulkTanimotoSimilarity(fps[i], fps[:i])
308 dists.extend([1-sim for sim in sims])
310# Cluster with distance cutoff
311clusters = Butina.ClusterData(dists, len(fps), distThresh=0.3, isDistData=True)
312```

5. Fingerprints and Molecular Similarity

Fingerprint Types:

from rdkit.Chem import rdFingerprintGenerator
from rdkit.Chem import MACCSkeys

# RDKit topological fingerprint
rdk_gen = rdFingerprintGenerator.GetRDKitFPGenerator(minPath=1, maxPath=7, fpSize=2048)
fp = rdk_gen.GetFingerprint(mol)

# Morgan fingerprints (circular fingerprints, similar to ECFP)
# Modern API using rdFingerprintGenerator
morgan_gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
fp = morgan_gen.GetFingerprint(mol)
# Count-based fingerprint
fp_count = morgan_gen.GetCountFingerprint(mol)

# MACCS keys: 167-bit vector, 166 keys and unused bit 0
fp = MACCSkeys.GenMACCSKeys(mol)

# Atom pair fingerprints
ap_gen = rdFingerprintGenerator.GetAtomPairGenerator()
fp = ap_gen.GetFingerprint(mol)

# Topological torsion fingerprints
tt_gen = rdFingerprintGenerator.GetTopologicalTorsionGenerator()
fp = tt_gen.GetFingerprint(mol)

# Avalon fingerprints (if available)
from rdkit.Avalon import pyAvalonTools
fp = pyAvalonTools.GetAvalonFP(mol)

Similarity Calculation:

from rdkit import DataStructs
from rdkit.Chem import rdFingerprintGenerator

# Generate fingerprints using generator
mfpgen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
fp1 = mfpgen.GetFingerprint(mol1)
fp2 = mfpgen.GetFingerprint(mol2)

# Calculate Tanimoto similarity
similarity = DataStructs.TanimotoSimilarity(fp1, fp2)

# Calculate similarity for multiple molecules
fps = [mfpgen.GetFingerprint(m) for m in [mol2, mol3, mol4]]
similarities = DataStructs.BulkTanimotoSimilarity(fp1, fps)

# Other similarity metrics
dice = DataStructs.DiceSimilarity(fp1, fp2)
cosine = DataStructs.CosineSimilarity(fp1, fp2)

Clustering and Diversity:

# Butina clustering based on fingerprint similarity
from rdkit.ML.Cluster import Butina

# Calculate distance matrix
dists = []
mfpgen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
fps = [mfpgen.GetFingerprint(mol) for mol in mols]
for i in range(len(fps)):
    sims = DataStructs.BulkTanimotoSimilarity(fps[i], fps[:i])
    dists.extend([1-sim for sim in sims])

# Cluster with distance cutoff
clusters = Butina.ClusterData(dists, len(fps), distThresh=0.3, isDistData=True)
Raw lines 238 to 313
314### 6. Substructure Searching and SMARTS
316**Basic Substructure Matching:**
318```python
319# Define query using SMARTS
320query = Chem.MolFromSmarts('[#6]1:[#6]:[#6]:[#6]:[#6]:[#6]:1') # Benzene ring
322# Check if molecule contains substructure
323has_match = mol.HasSubstructMatch(query)
325# Get all matches (returns tuple of tuples with atom indices)
326matches = mol.GetSubstructMatches(query)
328# Get only first match
329match = mol.GetSubstructMatch(query)
330```
332**Common SMARTS Patterns:**
334```python
335# Primary alcohols
336primary_alcohol = Chem.MolFromSmarts('[CH2][OH1]')
338# Carboxylic acids
339carboxylic_acid = Chem.MolFromSmarts('C(=O)[OH]')
341# Amides
342amide = Chem.MolFromSmarts('C(=O)N')
344# Aromatic heterocycles
345aromatic_n = Chem.MolFromSmarts('[nR]') # Aromatic nitrogen in ring
347# Atoms whose smallest ring has at least 12 atoms
348macrocycle = Chem.MolFromSmarts('[r{12-}]')
349```
351**Matching Rules:**
352- Unspecified properties in query match any value in target
353- Hydrogen count, explicit H atoms, aromaticity, and charge are query-specific.
354 Stereo is ignored by default; set `useChirality=True` for specified query stereo.
355- Charged query atom won't match uncharged target atom
356- Aromatic query atom won't match aliphatic target atom (unless query is generic)

6. Substructure Searching and SMARTS

Basic Substructure Matching:

# Define query using SMARTS
query = Chem.MolFromSmarts('[#6]1:[#6]:[#6]:[#6]:[#6]:[#6]:1')  # Benzene ring

# Check if molecule contains substructure
has_match = mol.HasSubstructMatch(query)

# Get all matches (returns tuple of tuples with atom indices)
matches = mol.GetSubstructMatches(query)

# Get only first match
match = mol.GetSubstructMatch(query)

Common SMARTS Patterns:

# Primary alcohols
primary_alcohol = Chem.MolFromSmarts('[CH2][OH1]')

# Carboxylic acids
carboxylic_acid = Chem.MolFromSmarts('C(=O)[OH]')

# Amides
amide = Chem.MolFromSmarts('C(=O)N')

# Aromatic heterocycles
aromatic_n = Chem.MolFromSmarts('[nR]')  # Aromatic nitrogen in ring

# Atoms whose smallest ring has at least 12 atoms
macrocycle = Chem.MolFromSmarts('[r{12-}]')

Matching Rules:

  • Unspecified properties in query match any value in target
  • Hydrogen count, explicit H atoms, aromaticity, and charge are query-specific. Stereo is ignored by default; set useChirality=True for specified query stereo.
  • Charged query atom won't match uncharged target atom
  • Aromatic query atom won't match aliphatic target atom (unless query is generic)
Raw lines 314 to 357
358### 7. Chemical Reactions
360**Reaction SMARTS:**
362```python
363from rdkit.Chem import AllChem
365# Define reaction using SMARTS: reactants >> products
366rxn = AllChem.ReactionFromSmarts('[C:1]=[O:2]>>[C:1][O:2]') # Carbonyl bond-order transform
368# Apply reaction to molecules
369reactants = (mol1,)
370products = rxn.RunReactants(reactants)
372# Products is tuple of tuples (one tuple per product set)
373for product_set in products:
374 for product in product_set:
375 # Sanitize product
376 Chem.SanitizeMol(product)
377```
379**Reaction Features:**
380- Atom mapping preserves specific atoms between reactants and products
381- Dummy atoms in products are replaced by corresponding reactant atoms
382- "Any" bonds inherit bond order from reactants
383- Stereo handling depends on mapped atoms and reaction stereo annotations; test
384 representative stereoisomers. Products are unsanitized and may duplicate across
385 matches. `RunReactants(..., maxProducts=1000)` defaults to a cap; hitting it may
386 mean truncation. A graph transform does not predict reagents, yield, feasibility,
387 selectivity, or a balanced experimental reaction.
389**Reaction Similarity:**
391```python
392# Generate reaction fingerprints
393fp = AllChem.CreateDifferenceFingerprintForReaction(rxn)
395# Compare reactions
396similarity = DataStructs.TanimotoSimilarity(fp1, fp2)
397```

7. Chemical Reactions

Reaction SMARTS:

from rdkit.Chem import AllChem

# Define reaction using SMARTS: reactants >> products
rxn = AllChem.ReactionFromSmarts('[C:1]=[O:2]>>[C:1][O:2]')  # Carbonyl bond-order transform

# Apply reaction to molecules
reactants = (mol1,)
products = rxn.RunReactants(reactants)

# Products is tuple of tuples (one tuple per product set)
for product_set in products:
    for product in product_set:
        # Sanitize product
        Chem.SanitizeMol(product)

Reaction Features:

  • Atom mapping preserves specific atoms between reactants and products
  • Dummy atoms in products are replaced by corresponding reactant atoms
  • "Any" bonds inherit bond order from reactants
  • Stereo handling depends on mapped atoms and reaction stereo annotations; test representative stereoisomers. Products are unsanitized and may duplicate across matches. RunReactants(..., maxProducts=1000) defaults to a cap; hitting it may mean truncation. A graph transform does not predict reagents, yield, feasibility, selectivity, or a balanced experimental reaction.

Reaction Similarity:

# Generate reaction fingerprints
fp = AllChem.CreateDifferenceFingerprintForReaction(rxn)

# Compare reactions
similarity = DataStructs.TanimotoSimilarity(fp1, fp2)
Raw lines 358 to 398
399### 8. 2D and 3D Coordinate Generation
401**2D Coordinate Generation:**
403```python
404from rdkit.Chem import AllChem
406# Depict a copy: Compute2DCoords clears existing conformers by default
407depiction = Chem.Mol(mol)
408AllChem.Compute2DCoords(depiction)
410# Align molecule to template structure
411template = Chem.MolFromSmiles('c1ccccc1')
412AllChem.Compute2DCoords(template)
413AllChem.GenerateDepictionMatching2DStructure(mol, template)
414```
416**3D Coordinate Generation and Conformers:**
418```python
419from rdkit import Chem
420from rdkit.Chem import AllChem
422mol3d = Chem.AddHs(Chem.MolFromSmiles('CCCO'))
423params = AllChem.ETKDGv3()
424params.randomSeed = 42
425params.numThreads = 1
426params.trackFailures = True
427conf_ids = list(AllChem.EmbedMultipleConfs(mol3d, numConfs=5, params=params))
428if not conf_ids:
429 raise RuntimeError(f"Embedding failed: {params.GetFailureCounts()}")
430if not AllChem.MMFFHasAllMoleculeParams(mol3d):
431 raise ValueError("MMFF parameters unavailable for this chemistry")
432results = AllChem.MMFFOptimizeMoleculeConfs(mol3d, numThreads=1, maxIters=500)
433# Conformer IDs are identifiers, not necessarily dense list positions.
434energies = {cid: energy for cid, (status, energy) in zip(conf_ids, results)
435 if status == 0}
436if not energies:
437 raise RuntimeError("No converged MMFF conformer")
438best_id = min(energies, key=energies.get)
439print(best_id, energies[best_id]) # energy in kcal/mol; coordinates in angstrom
441```
443Single-conformer optimization returns 0 for convergence, 1 for an iteration limit;
444MMFF additionally returns -1 if setup fails. UFF needs its own
445`UFFHasAllMoleculeParams` check. Do not silently switch force fields and compare
446energies. These are local force-field energies, not free energies, conformer
447populations, binding affinities, or guarantees of global minima. For RMSD,
448`AllChem.GetConformerRMS(mol, id1, id2)` aligns and mutates conformers by default;
449use a copy when retaining original coordinates. RMSD is in angstrom and depends
450on atom mapping, H inclusion, and symmetry treatment.
452**Constrained Embedding:**
454```python
455# Embed with part of molecule constrained to specific coordinates
456embedded = AllChem.ConstrainedEmbed(mol, core_mol) # core must have 3D coordinates
457```

8. 2D and 3D Coordinate Generation

2D Coordinate Generation:

from rdkit.Chem import AllChem

# Depict a copy: Compute2DCoords clears existing conformers by default
depiction = Chem.Mol(mol)
AllChem.Compute2DCoords(depiction)

# Align molecule to template structure
template = Chem.MolFromSmiles('c1ccccc1')
AllChem.Compute2DCoords(template)
AllChem.GenerateDepictionMatching2DStructure(mol, template)

3D Coordinate Generation and Conformers:

from rdkit import Chem
from rdkit.Chem import AllChem

mol3d = Chem.AddHs(Chem.MolFromSmiles('CCCO'))
params = AllChem.ETKDGv3()
params.randomSeed = 42
params.numThreads = 1
params.trackFailures = True
conf_ids = list(AllChem.EmbedMultipleConfs(mol3d, numConfs=5, params=params))
if not conf_ids:
    raise RuntimeError(f"Embedding failed: {params.GetFailureCounts()}")
if not AllChem.MMFFHasAllMoleculeParams(mol3d):
    raise ValueError("MMFF parameters unavailable for this chemistry")
results = AllChem.MMFFOptimizeMoleculeConfs(mol3d, numThreads=1, maxIters=500)
# Conformer IDs are identifiers, not necessarily dense list positions.
energies = {cid: energy for cid, (status, energy) in zip(conf_ids, results)
            if status == 0}
if not energies:
    raise RuntimeError("No converged MMFF conformer")
best_id = min(energies, key=energies.get)
print(best_id, energies[best_id])  # energy in kcal/mol; coordinates in angstrom

Single-conformer optimization returns 0 for convergence, 1 for an iteration limit; MMFF additionally returns -1 if setup fails. UFF needs its own UFFHasAllMoleculeParams check. Do not silently switch force fields and compare energies. These are local force-field energies, not free energies, conformer populations, binding affinities, or guarantees of global minima. For RMSD, AllChem.GetConformerRMS(mol, id1, id2) aligns and mutates conformers by default; use a copy when retaining original coordinates. RMSD is in angstrom and depends on atom mapping, H inclusion, and symmetry treatment.

Constrained Embedding:

# Embed with part of molecule constrained to specific coordinates
embedded = AllChem.ConstrainedEmbed(mol, core_mol)  # core must have 3D coordinates
Raw lines 399 to 458
459### 9. Molecular Visualization
461**Basic Drawing:**
463```python
464from rdkit.Chem import Draw
466# Draw single molecule to PIL image
467img = Draw.MolToImage(mol, size=(300, 300))
468img.save('molecule.png')
470# Draw to file directly
471Draw.MolToFile(mol, 'molecule.png')
473# Draw multiple molecules in grid
474mols = [mol1, mol2, mol3, mol4]
475img = Draw.MolsToGridImage(mols, molsPerRow=2, subImgSize=(200, 200))
476```
478**Highlighting Substructures:**
480```python
481# Highlight substructure match
482query = Chem.MolFromSmarts('c1ccccc1')
483match = mol.GetSubstructMatch(query)
485img = Draw.MolToImage(mol, highlightAtoms=match)
487# Custom highlight colors
488highlight_colors = {atom_idx: (1, 0, 0) for atom_idx in match} # Red
489img = Draw.MolToImage(mol, highlightAtoms=match,
490 highlightAtomColors=highlight_colors)
491```
493**Customizing Visualization:**
495```python
496from rdkit.Chem.Draw import rdMolDraw2D
498# Create drawer with custom options
499drawer = rdMolDraw2D.MolDraw2DCairo(300, 300)
500opts = drawer.drawOptions()
502# Customize options
503opts.addAtomIndices = True
504opts.addStereoAnnotation = True
505opts.bondLineWidth = 2
507# Draw molecule
508drawer.DrawMolecule(mol)
509drawer.FinishDrawing()
511# Save to file
512with open('molecule.png', 'wb') as f:
513 f.write(drawer.GetDrawingText())
514```
516**Jupyter Notebook Integration:**
518```python
519# Enable inline display in Jupyter
520from rdkit.Chem.Draw import IPythonConsole
522# Customize default display
523IPythonConsole.ipython_useSVG = True # Use SVG instead of PNG
524IPythonConsole.molSize = (300, 300) # Default size
526# Molecules now display automatically
527mol # Shows molecule image
528```
530**Visualizing Fingerprint Bits:**
532```python
533# Show what molecular features a fingerprint bit represents
534from rdkit.Chem import Draw
535from rdkit.Chem import rdFingerprintGenerator
537# For Morgan fingerprints
538additional_output = rdFingerprintGenerator.AdditionalOutput()
539additional_output.AllocateBitInfoMap()
540morgan_gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
541fp = morgan_gen.GetFingerprint(mol, additionalOutput=additional_output)
542bit_info = additional_output.GetBitInfoMap()
544# Draw environment for specific bit
545img = Draw.DrawMorganBit(mol, bit_id, bit_info)
546```

9. Molecular Visualization

Basic Drawing:

from rdkit.Chem import Draw

# Draw single molecule to PIL image
img = Draw.MolToImage(mol, size=(300, 300))
img.save('molecule.png')

# Draw to file directly
Draw.MolToFile(mol, 'molecule.png')

# Draw multiple molecules in grid
mols = [mol1, mol2, mol3, mol4]
img = Draw.MolsToGridImage(mols, molsPerRow=2, subImgSize=(200, 200))

Highlighting Substructures:

# Highlight substructure match
query = Chem.MolFromSmarts('c1ccccc1')
match = mol.GetSubstructMatch(query)

img = Draw.MolToImage(mol, highlightAtoms=match)

# Custom highlight colors
highlight_colors = {atom_idx: (1, 0, 0) for atom_idx in match}  # Red
img = Draw.MolToImage(mol, highlightAtoms=match,
                      highlightAtomColors=highlight_colors)

Customizing Visualization:

from rdkit.Chem.Draw import rdMolDraw2D

# Create drawer with custom options
drawer = rdMolDraw2D.MolDraw2DCairo(300, 300)
opts = drawer.drawOptions()

# Customize options
opts.addAtomIndices = True
opts.addStereoAnnotation = True
opts.bondLineWidth = 2

# Draw molecule
drawer.DrawMolecule(mol)
drawer.FinishDrawing()

# Save to file
with open('molecule.png', 'wb') as f:
    f.write(drawer.GetDrawingText())

Jupyter Notebook Integration:

# Enable inline display in Jupyter
from rdkit.Chem.Draw import IPythonConsole

# Customize default display
IPythonConsole.ipython_useSVG = True  # Use SVG instead of PNG
IPythonConsole.molSize = (300, 300)   # Default size

# Molecules now display automatically
mol  # Shows molecule image

Visualizing Fingerprint Bits:

# Show what molecular features a fingerprint bit represents
from rdkit.Chem import Draw
from rdkit.Chem import rdFingerprintGenerator

# For Morgan fingerprints
additional_output = rdFingerprintGenerator.AdditionalOutput()
additional_output.AllocateBitInfoMap()
morgan_gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
fp = morgan_gen.GetFingerprint(mol, additionalOutput=additional_output)
bit_info = additional_output.GetBitInfoMap()

# Draw environment for specific bit
img = Draw.DrawMorganBit(mol, bit_id, bit_info)
Raw lines 459 to 547
548### 10. Molecular Modification
550**Adding/Removing Hydrogens:**
552```python
553# Add explicit hydrogens
554mol_h = Chem.AddHs(mol)
556# Remove explicit hydrogens
557mol = Chem.RemoveHs(mol_h)
558```
560**Kekulization and Aromaticity:**
562```python
563# Convert aromatic bonds to alternating single/double
564Chem.Kekulize(mol)
566# Set aromaticity
567Chem.SetAromaticity(mol)
568```
570**Replacing Substructures:**
572```python
573# Replace substructure with another structure
574query = Chem.MolFromSmarts('c1ccccc1') # Benzene
575replacement = Chem.MolFromSmiles('C1CCCCC1') # Cyclohexane
577new_mol = Chem.ReplaceSubstructs(mol, query, replacement)[0]
578```
580**Neutralizing Charges:**
582```python
583# Remove formal charges by adding/removing hydrogens
584from rdkit.Chem.MolStandardize import rdMolStandardize
586# Using Uncharger
587uncharger = rdMolStandardize.Uncharger()
588mol_neutral = uncharger.uncharge(mol)
589```

10. Molecular Modification

Adding/Removing Hydrogens:

# Add explicit hydrogens
mol_h = Chem.AddHs(mol)

# Remove explicit hydrogens
mol = Chem.RemoveHs(mol_h)

Kekulization and Aromaticity:

# Convert aromatic bonds to alternating single/double
Chem.Kekulize(mol)

# Set aromaticity
Chem.SetAromaticity(mol)

Replacing Substructures:

# Replace substructure with another structure
query = Chem.MolFromSmarts('c1ccccc1')  # Benzene
replacement = Chem.MolFromSmiles('C1CCCCC1')  # Cyclohexane

new_mol = Chem.ReplaceSubstructs(mol, query, replacement)[0]

Neutralizing Charges:

# Remove formal charges by adding/removing hydrogens
from rdkit.Chem.MolStandardize import rdMolStandardize

# Using Uncharger
uncharger = rdMolStandardize.Uncharger()
mol_neutral = uncharger.uncharge(mol)
Raw lines 548 to 590
591### 11. Working with Molecular Hashes and Standardization
593**Molecular Hashing:**
595```python
596from rdkit.Chem import rdMolHash
598# Generate Murcko scaffold hash
599scaffold_hash = rdMolHash.MolHash(mol, rdMolHash.HashFunction.MurckoScaffold)
601# Canonical SMILES hash
602canonical_hash = rdMolHash.MolHash(mol, rdMolHash.HashFunction.CanonicalSmiles)
604# Regioisomer abstraction (not a general stereo-removal operation)
605regio_hash = rdMolHash.MolHash(mol, rdMolHash.HashFunction.Regioisomer)
606```
608**Randomized SMILES:**
610```python
611# Generate random SMILES representations (for data augmentation)
612from rdkit.Chem import MolToRandomSmilesVect
614random_smiles = MolToRandomSmilesVect(mol, numSmiles=10, randomSeed=42)
615```

11. Working with Molecular Hashes and Standardization

Molecular Hashing:

from rdkit.Chem import rdMolHash

# Generate Murcko scaffold hash
scaffold_hash = rdMolHash.MolHash(mol, rdMolHash.HashFunction.MurckoScaffold)

# Canonical SMILES hash
canonical_hash = rdMolHash.MolHash(mol, rdMolHash.HashFunction.CanonicalSmiles)

# Regioisomer abstraction (not a general stereo-removal operation)
regio_hash = rdMolHash.MolHash(mol, rdMolHash.HashFunction.Regioisomer)

Randomized SMILES:

# Generate random SMILES representations (for data augmentation)
from rdkit.Chem import MolToRandomSmilesVect

random_smiles = MolToRandomSmilesVect(mol, numSmiles=10, randomSeed=42)
Raw lines 591 to 616
617### 12. Pharmacophore and 3D Features
619**Pharmacophore Features:**
621```python
622from rdkit.Chem import ChemicalFeatures
623from rdkit import RDConfig
624import os
626# Load feature factory
627fdef_path = os.path.join(RDConfig.RDDataDir, 'BaseFeatures.fdef')
628factory = ChemicalFeatures.BuildFeatureFactory(fdef_path)
630# Get pharmacophore features
631features = factory.GetFeaturesForMol(mol)
633for feat in features:
634 print(feat.GetFamily(), feat.GetType(), feat.GetAtomIds())
635```

12. Pharmacophore and 3D Features

Pharmacophore Features:

from rdkit.Chem import ChemicalFeatures
from rdkit import RDConfig
import os

# Load feature factory
fdef_path = os.path.join(RDConfig.RDDataDir, 'BaseFeatures.fdef')
factory = ChemicalFeatures.BuildFeatureFactory(fdef_path)

# Get pharmacophore features
features = factory.GetFeaturesForMol(mol)

for feat in features:
    print(feat.GetFamily(), feat.GetType(), feat.GetAtomIds())
Raw lines 617 to 635