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

smarts_patterns.md

177 lines · skills/rdkit/references/smarts_patterns.md at 5f2bc03

Open on GitHub
1# SMARTS patterns and their limits
2
3These are motif queries tested with RDKit 2026.03.6. A matched subgraph is not
4molecular identity, a pKa prediction, toxicity evidence, or evidence that a
5reaction will occur. Choose charge/tautomer/stereo policy before matching and
6validate positives **and near negatives** for the dataset. SMARTS and SMILES are
7different query languages even when a string is legal in both.
8

SMARTS patterns and their limits

These are motif queries tested with RDKit 2026.03.6. A matched subgraph is not molecular identity, a pKa prediction, toxicity evidence, or evidence that a reaction will occur. Choose charge/tautomer/stereo policy before matching and validate positives and near negatives for the dataset. SMARTS and SMILES are different query languages even when a string is legal in both.

Raw lines 1 to 8
9## Functional groups
11The following neutral-state motifs cover common examples; they are not exhaustive
12classifications of all resonance forms, organometallics, or charged species.
13`[#6]` includes both aromatic and aliphatic carbon. `C` and `[C]` both mean
14aliphatic carbon in SMARTS; lowercase `c` means aromatic carbon.
16| Motif | SMARTS | Positive SMILES | Near negative SMILES |
17| --- | --- | --- | --- |
18| Alcohol on saturated C | `[OX2H1][CX4]` | `CCO` | `CC(=O)O` |
19| Primary alcohol, excluding methanol | `[CX4H2][OX2H1]` | `CCO` | `CC(O)C` |
20| Secondary alcohol | `[CX4H1][OX2H1]` | `CC(O)C` | `CCO` |
21| Tertiary alcohol | `[CX4H0]([OX2H1])([#6])([#6])[#6]` | `CC(C)(C)O` | `CC(O)C` |
22| Phenol | `c[OX2H1]` | `Oc1ccccc1` | `COc1ccccc1` |
23| Aldehyde with carbon substituent | `[#6][CX3H1]=O` | `CC=O` | `CC(=O)C` |
24| Ketone | `[#6][CX3](=O)[#6]` | `CC(=O)C` | `CC(=O)O` |
25| Carbonyl | `[CX3]=O` | `CC=O` | `CCO` |
26| Carboxylic acid | `[CX3](=O)[OX2H1]` | `CC(=O)O` | `CC(=O)[O-]` |
27| Carboxylate | `[CX3](=O)[O-]` | `CC(=O)[O-]` | `CC(=O)O` |
28| Ester motif | `[CX3](=O)[OX2H0][#6]` | `CC(=O)OC` | `CC(=O)O` |
29| Amide | `[CX3](=O)[NX3]` | `CC(=O)N` | `CCN` |
30| Neutral amine motif | `[NX3;$(N-[#6]);!$(N-C=O);!$(N-S(=O)=O);!$(N-P=O);!$(N=*)]` | `CCN` | `CC(=O)N` |
31| Primary amine motif | `[NX3H2;!$(N-C=O);!$(N-S(=O)=O);!$(N-P=O)]` | `CN` | `CNC` |
32| Ether excluding ester O | `[#6][OX2H0;!$(O-C=O)][#6]` | `COC` | `CC(=O)OC` |
33| Alkyl halide | `[CX4][F,Cl,Br,I]` | `CCCl` | `Clc1ccccc1` |
34| Aryl halide | `c[F,Cl,Br,I]` | `Clc1ccccc1` | `CCCl` |
35| Nitrile | `C#N` | `CC#N` | `CCN` |
36| Nitro | `[N+](=O)[O-]` | `C[N+](=O)[O-]` | `CN` |
37| Thiol | `[#6][SX2H1]` | `CCS` | `CSC` |
38| Sulfide | `[#6][SX2H0][#6]` | `CSC` | `CS(=O)C` |
39| Sulfoxide | `[#6][SX3](=O)[#6]` | `CS(=O)C` | `CS(=O)(=O)C` |
40| Sulfone | `[#6][SX4](=O)(=O)[#6]` | `CS(=O)(=O)C` | `CS(=O)C` |
42An ester motif may also match a larger derivative such as an anhydride; an
43amine motif needs further exclusions to classify unusual N–N/N–O compounds.
44Ammonium, deprotonated alcohols, and aromatic `[nH]` need separate rules. The
45bundled filter exposes a subset of these patterns; its library names describe
46motifs, not universally disjoint chemical classes.

Functional groups

The following neutral-state motifs cover common examples; they are not exhaustive classifications of all resonance forms, organometallics, or charged species. [#6] includes both aromatic and aliphatic carbon. C and [C] both mean aliphatic carbon in SMARTS; lowercase c means aromatic carbon.

MotifSMARTSPositive SMILESNear negative SMILES
Alcohol on saturated C[OX2H1][CX4]CCOCC(=O)O
Primary alcohol, excluding methanol[CX4H2][OX2H1]CCOCC(O)C
Secondary alcohol[CX4H1][OX2H1]CC(O)CCCO
Tertiary alcohol[CX4H0]([OX2H1])([#6])([#6])[#6]CC(C)(C)OCC(O)C
Phenolc[OX2H1]Oc1ccccc1COc1ccccc1
Aldehyde with carbon substituent[#6][CX3H1]=OCC=OCC(=O)C
Ketone[#6][CX3](=O)[#6]CC(=O)CCC(=O)O
Carbonyl[CX3]=OCC=OCCO
Carboxylic acid[CX3](=O)[OX2H1]CC(=O)OCC(=O)[O-]
Carboxylate[CX3](=O)[O-]CC(=O)[O-]CC(=O)O
Ester motif[CX3](=O)[OX2H0][#6]CC(=O)OCCC(=O)O
Amide[CX3](=O)[NX3]CC(=O)NCCN
Neutral amine motif[NX3;$(N-[#6]);!$(N-C=O);!$(N-S(=O)=O);!$(N-P=O);!$(N=*)]CCNCC(=O)N
Primary amine motif[NX3H2;!$(N-C=O);!$(N-S(=O)=O);!$(N-P=O)]CNCNC
Ether excluding ester O[#6][OX2H0;!$(O-C=O)][#6]COCCC(=O)OC
Alkyl halide[CX4][F,Cl,Br,I]CCClClc1ccccc1
Aryl halidec[F,Cl,Br,I]Clc1ccccc1CCCl
NitrileC#NCC#NCCN
Nitro[N+](=O)[O-]C[N+](=O)[O-]CN
Thiol[#6][SX2H1]CCSCSC
Sulfide[#6][SX2H0][#6]CSCCS(=O)C
Sulfoxide[#6][SX3](=O)[#6]CS(=O)CCS(=O)(=O)C
Sulfone[#6][SX4](=O)(=O)[#6]CS(=O)(=O)CCS(=O)C

An ester motif may also match a larger derivative such as an anhydride; an amine motif needs further exclusions to classify unusual N–N/N–O compounds. Ammonium, deprotonated alcohols, and aromatic [nH] need separate rules. The bundled filter exposes a subset of these patterns; its library names describe motifs, not universally disjoint chemical classes.

Raw lines 9 to 47
48## Rings and scaffolds
50| Motif | SMARTS |
51| --- | --- |
52| Benzene | `c1ccccc1` |
53| Cyclohexane | `C1CCCCC1` |
54| Pyridine | `n1ccccc1` |
55| Pyrrole, allowing N-substitution | `n1cccc1` |
56| Furan | `o1cccc1` |
57| Thiophene | `s1cccc1` |
58| Imidazole | `n1cncc1` |
59| Pyrimidine | `n1cnccc1` |
60| Thiazole | `n1ccsc1` |
61| Oxazole | `n1ccoc1` |
62| Naphthalene | `c1ccc2ccccc2c1` |
63| Indole with N–H | `c1ccc2[nH]ccc2c1` |
64| Benzimidazole with N–H | `c1ccc2[nH]cnc2c1` |
65| Biphenyl | `c1ccccc1-c2ccccc2` |
66| Piperazine | `N1CCNCC1` |
67| Piperidine | `N1CCCCC1` |
68| Morpholine | `N1CCOCC1` |
70A ring SMARTS can match a fused or substituted structure; it is not an exact
71scaffold identity test. Use Murcko scaffold extraction or a defined graph
72identity policy when that is the scientific question.
74`[r6]` means an atom whose **smallest ring** has size 6. `[r{12-}]` means smallest
75ring size at least 12, not a general detector for every large cycle in a fused or
76bridged graph. `[R]` means any ring atom; `[R2]` depends on the perceived ring set.
77`[R]~[R]` merely joins two ring atoms and also matches within one ring. Use
78`[*]@[*]` for a ring bond and `[R]!@[R]` for a non-ring bond between ring atoms
79(e.g. biphenyl). A symmetrized ring count is not a count of distinct ring systems.

Rings and scaffolds

MotifSMARTS
Benzenec1ccccc1
CyclohexaneC1CCCCC1
Pyridinen1ccccc1
Pyrrole, allowing N-substitutionn1cccc1
Furano1cccc1
Thiophenes1cccc1
Imidazolen1cncc1
Pyrimidinen1cnccc1
Thiazolen1ccsc1
Oxazolen1ccoc1
Naphthalenec1ccc2ccccc2c1
Indole with N–Hc1ccc2[nH]ccc2c1
Benzimidazole with N–Hc1ccc2[nH]cnc2c1
Biphenylc1ccccc1-c2ccccc2
PiperazineN1CCNCC1
PiperidineN1CCCCC1
MorpholineN1CCOCC1

A ring SMARTS can match a fused or substituted structure; it is not an exact scaffold identity test. Use Murcko scaffold extraction or a defined graph identity policy when that is the scientific question.

[r6] means an atom whose smallest ring has size 6. [r{12-}] means smallest ring size at least 12, not a general detector for every large cycle in a fused or bridged graph. [R] means any ring atom; [R2] depends on the perceived ring set. [R]~[R] merely joins two ring atoms and also matches within one ring. Use [*]@[*] for a ring bond and [R]!@[R] for a non-ring bond between ring atoms (e.g. biphenyl). A symmetrized ring count is not a count of distinct ring systems.

Raw lines 48 to 80
81## Atom and bond primitives
83| Query | Meaning |
84| --- | --- |
85| `[*]` | Any atom, including explicit H |
86| `[!#1]` | Any non-hydrogen atom |
87| `[#6]` | Carbon, aromatic or aliphatic |
88| `[!#6;!#1]` | Neither carbon nor hydrogen |
89| `[a]` / `[A]` | Aromatic / aliphatic atom |
90| `[D2]` | Two explicit neighboring atoms |
91| `[X4]` | Total connectivity four, including hydrogens |
92| `[H1]` | Atom with one attached hydrogen (not hydrogen element identity) |
93| `[#1]` | An explicit hydrogen atom |
94| `[^2]` | RDKit extension for sp2 hybridization |
95| `[CX3]` | Aliphatic carbon with connectivity three; not a universal sp2 query |
96| `[+1]` / `[-1]` | Exactly the specified formal charge |
97| `C-C`, `C=C`, `C#C` | Specified single, double, triple bond |
98| `c:c` | Aromatic bond |
99| `[*]~[*]` | Any bond |
100| `[C;D2]([#6])[#6]` | Aliphatic C with exactly two explicit neighbors, both carbon |
102`[!C]` also matches aromatic carbon; it does not mean “not element carbon”.
103Hydrogen syntax is context-sensitive: use atomic numbers for element exclusion.
104SMARTS `@` in a **bond** query is ring membership; atom stereo uses a different
105meaning. Omitting a bond generally permits single or aromatic bonds.

Atom and bond primitives

QueryMeaning
[*]Any atom, including explicit H
[!#1]Any non-hydrogen atom
[#6]Carbon, aromatic or aliphatic
[!#6;!#1]Neither carbon nor hydrogen
[a] / [A]Aromatic / aliphatic atom
[D2]Two explicit neighboring atoms
[X4]Total connectivity four, including hydrogens
[H1]Atom with one attached hydrogen (not hydrogen element identity)
[#1]An explicit hydrogen atom
[^2]RDKit extension for sp2 hybridization
[CX3]Aliphatic carbon with connectivity three; not a universal sp2 query
[+1] / [-1]Exactly the specified formal charge
C-C, C=C, C#CSpecified single, double, triple bond
c:cAromatic bond
[*]~[*]Any bond
[C;D2]([#6])[#6]Aliphatic C with exactly two explicit neighbors, both carbon

[!C] also matches aromatic carbon; it does not mean “not element carbon”. Hydrogen syntax is context-sensitive: use atomic numbers for element exclusion. SMARTS @ in a bond query is ring membership; atom stereo uses a different meaning. Omitting a bond generally permits single or aromatic bonds.

Raw lines 81 to 106
107## Stereo queries
109Stereo matching is disabled by default. A lone `[C@]` is not a reliable test for
110“any chiral atom”; use stereo perception to enumerate potential or specified
111centers. `@`/`@@` encode local neighbor order, not absolute R/S labels.
113```python
114from rdkit import Chem
115query = Chem.MolFromSmarts('C[C@H](O)F')
116same = Chem.MolFromSmiles('C[C@H](O)F')
117opposite = Chem.MolFromSmiles('C[C@@H](O)F')
118assert same.HasSubstructMatch(query, useChirality=True)
119assert not opposite.HasSubstructMatch(query, useChirality=True)
120assert opposite.HasSubstructMatch(query) # stereo ignored
122# E/Z examples need stereo-aware matching as well.
123trans = Chem.MolFromSmarts('C/C=C/C')
124cis = Chem.MolFromSmiles(r'C/C=C\C')
125assert not cis.HasSubstructMatch(trans, useChirality=True)
126```

Stereo queries

Stereo matching is disabled by default. A lone [C@] is not a reliable test for “any chiral atom”; use stereo perception to enumerate potential or specified centers. @/@@ encode local neighbor order, not absolute R/S labels.

from rdkit import Chem
query = Chem.MolFromSmarts('C[C@H](O)F')
same = Chem.MolFromSmiles('C[C@H](O)F')
opposite = Chem.MolFromSmiles('C[C@@H](O)F')
assert same.HasSubstructMatch(query, useChirality=True)
assert not opposite.HasSubstructMatch(query, useChirality=True)
assert opposite.HasSubstructMatch(query)  # stereo ignored

# E/Z examples need stereo-aware matching as well.
trans = Chem.MolFromSmarts('C/C=C/C')
cis = Chem.MolFromSmiles(r'C/C=C\C')
assert not cis.HasSubstructMatch(trans, useChirality=True)
Raw lines 107 to 127
128## Pharmacophores and chemical alerts
130Use `ChemicalFeatures.BuildFeatureFactory` with `BaseFeatures.fdef` for named
131RDKit donor/acceptor features. `[O,N]` is not an acceptor definition: protonated
132amines and amide N are counterexamples. Graph features are not proof of binding
133or metal coordination; geometric feature positions require coordinates.
135Acyl chloride `C(=O)Cl`, epoxide `C1OC1`, catechol `Oc1ccccc1O`, rhodanine
136`O=C1CSC(=S)N1`, and Michael-acceptor motif `C=CC(=O)` are illustrative structural
137alerts. Their presence does not establish toxicity or assay interference.
138The helper's legacy `pains` key contains only five hand-selected motifs; use the
139actual RDKit catalogue when requesting PAINS:
141```python
142from rdkit import Chem
143from rdkit.Chem import FilterCatalog
144params = FilterCatalog.FilterCatalogParams()
145params.AddCatalog(FilterCatalog.FilterCatalogParams.FilterCatalogs.PAINS)
146catalog = FilterCatalog.FilterCatalog(params)
147mol = Chem.MolFromSmiles('CCO')
148alerts = [entry.GetDescription() for entry in catalog.GetMatches(mol)]
149assert alerts == []
150```
152Record the catalogue/version and individual hit descriptions. “No alert” is not
153an all-clear safety conclusion. False positives and context dependence require
154scientific review.

Pharmacophores and chemical alerts

Use ChemicalFeatures.BuildFeatureFactory with BaseFeatures.fdef for named RDKit donor/acceptor features. [O,N] is not an acceptor definition: protonated amines and amide N are counterexamples. Graph features are not proof of binding or metal coordination; geometric feature positions require coordinates.

Acyl chloride C(=O)Cl, epoxide C1OC1, catechol Oc1ccccc1O, rhodanine O=C1CSC(=S)N1, and Michael-acceptor motif C=CC(=O) are illustrative structural alerts. Their presence does not establish toxicity or assay interference. The helper's legacy pains key contains only five hand-selected motifs; use the actual RDKit catalogue when requesting PAINS:

from rdkit import Chem
from rdkit.Chem import FilterCatalog
params = FilterCatalog.FilterCatalogParams()
params.AddCatalog(FilterCatalog.FilterCatalogParams.FilterCatalogs.PAINS)
catalog = FilterCatalog.FilterCatalog(params)
mol = Chem.MolFromSmiles('CCO')
alerts = [entry.GetDescription() for entry in catalog.GetMatches(mol)]
assert alerts == []

Record the catalogue/version and individual hit descriptions. “No alert” is not an all-clear safety conclusion. False positives and context dependence require scientific review.

Raw lines 128 to 155
156## Compile first; fail on invalid requests
158```python
159from rdkit import Chem
161def matches(smiles, smarts, use_chirality=False):
162 query = Chem.MolFromSmarts(smarts)
163 if query is None or query.GetNumAtoms() == 0:
164 raise ValueError('Invalid or empty SMARTS')
165 mol = Chem.MolFromSmiles(smiles)
166 if mol is None or mol.GetNumAtoms() == 0:
167 raise ValueError('Invalid or empty SMILES')
168 return mol.HasSubstructMatch(query, useChirality=use_chirality)
170assert matches('CC(=O)C', '[#6][CX3](=O)[#6]')
171assert not matches('CC(=O)O', '[#6][CX3](=O)[#6]')
172```
174For all matches, `GetSubstructMatches` defaults to `uniquify=True` and
175`maxMatches=1000`. Hitting the cap does not establish a complete enumeration.
176The returned indices refer to the current molecule's atom order, which may differ
177from a reparsed canonical SMILES or an edited molecule.

Compile first; fail on invalid requests

from rdkit import Chem

def matches(smiles, smarts, use_chirality=False):
    query = Chem.MolFromSmarts(smarts)
    if query is None or query.GetNumAtoms() == 0:
        raise ValueError('Invalid or empty SMARTS')
    mol = Chem.MolFromSmiles(smiles)
    if mol is None or mol.GetNumAtoms() == 0:
        raise ValueError('Invalid or empty SMILES')
    return mol.HasSubstructMatch(query, useChirality=use_chirality)

assert matches('CC(=O)C', '[#6][CX3](=O)[#6]')
assert not matches('CC(=O)O', '[#6][CX3](=O)[#6]')

For all matches, GetSubstructMatches defaults to uniquify=True and maxMatches=1000. Hitting the cap does not establish a complete enumeration. The returned indices refer to the current molecule's atom order, which may differ from a reparsed canonical SMILES or an edited molecule.

Raw lines 156 to 177