RDKit识别桥头原子及判断Bredt规则违反的代码是否存在失效场景?
嘿,看起来你已经把Bredt规则的基础判断逻辑实现得挺扎实了,不过确实有几个容易踩坑的边缘场景可能会让你的代码失效,我来帮你梳理一下,顺便给点修复建议:
首先说说你的代码当前存在的明显问题
双键方向判断不全
你现在判断桥头原子是否参与烯烃双键时,只检查了该原子作为双键的BeginAtom的情况,但如果双键的另一端是BeginAtom、桥头原子是EndAtom,这段代码就会漏掉这个双键!比如桥头原子idx是2,双键连接的是1和2,bond.GetBeginAtomIdx()是1,这时候你的条件就不成立,会错误地认为这个桥头原子没有双键。修复这个很简单,用RDKit的
bond.GetOtherAtom(atom)方法直接获取双键的另一端原子,不用纠结Begin/End:is_part_of_alkene = any( bond.GetBondType() == Chem.BondType.DOUBLE and bond.GetOtherAtom(atom).GetSymbol() == "C" for bond in atom.GetBonds() )误把螺环中心原子当成桥头原子
你的代码把所有属于≥2个环的原子都视为桥头原子,但螺环的中心原子(比如螺[4.4]壬烷的中心碳)虽然属于两个环,但它是螺原子,不属于桥环的桥头,Bredt规则根本不适用于螺环系统。如果输入螺环且中心原子有双键,你的代码会错误地判断为违反Bredt规则,但实际上这种情况和Bredt规则无关。解决办法是先获取螺原子集合,筛选桥头原子时排除它们:
spiro_atoms = set(Chem.GetSpiroAtoms(molecule)) bridgehead_atoms = [ atom_idx for atom_idx, count in atom_ring_counts.items() if count > 1 and atom_idx not in spiro_atoms and molecule.GetAtomWithIdx(atom_idx).GetSymbol() == "C" # 只关注碳桥头,符合Bredt规则的适用范围 ]
还有一些容易忽略的边缘场景
多环(≥3环)系统的复杂情况
你的代码会把属于3个及以上环的碳也视为桥头原子,但在三环及更复杂的多环系统中,Bredt规则的判断不能只看每个环的大小——有时候即使所有环都小于8,桥头双键也可能稳定存在,或者反之。比如一些笼状化合物,桥头的张力可能被其他环抵消,这时候你的简单“全环<8则违反”逻辑就不准确了。杂原子桥头的特殊情况
你的代码目前没有限制桥头原子必须是碳,但Bredt规则主要针对碳碳双键的桥头情况。如果桥头是氮等杂原子且有双键,你的代码不会处理,但如果你的需求只关注碳桥头,那可以在筛选桥头原子时加上GetSymbol() == "C"的条件(就像上面修复的那样)。环的定义依赖SSSR的局限性
RDKit的RingInfo.AtomRings()返回的是最小环系统(SSSR),有些复杂桥环的直观环可能不在SSSR里,这时候统计的环大小可能和你预期的不一样。比如某些稠环+桥环的组合,SSSR的环划分可能会影响桥头原子的环数统计,不过这种情况比较少见,一般基础应用场景下影响不大。
修改后的完整代码示例
from rdkit import Chem def check_bredts_rule(molecule): ring_info = molecule.GetRingInfo() atom_rings = ring_info.AtomRings() # 获取螺原子集合,排除桥环以外的情况 spiro_atoms = set(Chem.GetSpiroAtoms(molecule)) # 统计每个原子属于多少个环 atom_ring_counts = {atom.GetIdx(): 0 for atom in molecule.GetAtoms()} for ring in atom_rings: for atom_idx in ring: atom_ring_counts[atom_idx] += 1 # 筛选符合条件的桥环碳桥头原子:属于≥2个环、非螺原子、是碳 bridgehead_atoms = [ atom_idx for atom_idx, count in atom_ring_counts.items() if count > 1 and atom_idx not in spiro_atoms and molecule.GetAtomWithIdx(atom_idx).GetSymbol() == "C" ] for atom_idx in bridgehead_atoms: atom = molecule.GetAtomWithIdx(atom_idx) # 检查是否存在碳碳双键 is_part_of_alkene = any( bond.GetBondType() == Chem.BondType.DOUBLE and bond.GetOtherAtom(atom).GetSymbol() == "C" for bond in atom.GetBonds() ) if not is_part_of_alkene: continue # 获取包含该桥头原子的所有环的大小 ring_sizes = [len(ring) for ring in atom_rings if atom_idx in ring] # 所有环大小都小于8则违反Bredt规则 if all(size < 8 for size in ring_sizes): return 'violate' return 'no violate'
这个修改后的版本应该能覆盖你之前测试的所有案例,同时避免上面提到的几个失效场景。
备注:内容来源于stack exchange,提问作者user28594497

