小分子 pdb准化为sdf

python 复制代码
import os
from rdkit import Chem
import subprocess


small_mol_path = "pdbs"

from rdkit.Chem import rdDetermineBonds

def load_pdb_with_adaptive_bonds(pdb_file):
    # ==========================================
    # STRATEGY 1: RDKit with Strict CONECT Rules
    # ==========================================
    # IMPORTANT: proximityBonding=False forces RDKit to ONLY use the CONECT block, 
    # preventing false bonds from compressed 3D coordinates.
    mol = Chem.MolFromPDBFile(pdb_file, removeHs=False, sanitize=False, proximityBonding=False)
    
    if mol is None:
        print(f"[{pdb_file}] Warning: Initial RDKit read failed.")
    else:
        mol.UpdatePropertyCache(strict=False)
        possible_charges = [0, 1, -1, 2, -2]
        
        for charge in possible_charges:
            try:
                mol_test = Chem.Mol(mol)
                rdDetermineBonds.DetermineBondOrders(mol_test, charge=charge)
                Chem.SanitizeMol(mol_test)
                # print(f"[{pdb_file}] Success via RDKit! Charge: {charge}")
                return mol_test
            except Exception:
                continue

    # ==========================================
    # STRATEGY 2: OpenBabel Fallback (Robust)
    # ==========================================
    # If RDKit's strict 3D geometry checker rejects the strained molecule,
    # fallback to OpenBabel's heuristic bond assigner.
    print(f"[{pdb_file}] RDKit bond determination failed with posssible_charges {possible_charges}. Falling back to OpenBabel...")
    
    base_name, ext = os.path.splitext(pdb_file)
    temp_mol2 = f"{base_name}_temp_ob.mol2"
    
    # Use OpenBabel to convert PDB to MOL2 (which hardcodes the bond orders)
    command = ["~/anaconda3/envs/UniMoMo/bin/obabel", pdb_file, "-O", temp_mol2]
    
    try:
        subprocess.run(command, check=True, capture_output=True)
        # Read the resulting MOL2 file back into RDKit
        fallback_mol = Chem.MolFromMol2File(temp_mol2, removeHs=False, sanitize=True)
        
        # Clean up temp file
        if os.path.exists(temp_mol2):
            os.remove(temp_mol2)
            
        if fallback_mol is not None:
            print(f"[{pdb_file}] Success via OpenBabel Fallback!")
            return fallback_mol
            
    except subprocess.CalledProcessError as e:
        print(f"[{pdb_file}] OpenBabel Fallback Error: {e.stderr.decode('utf-8')}")

    # If both strategies fail
    print(f"[{pdb_file}] CRITICAL: Could not process molecule via any method.")
    return None
        
def save_mol_to_sdf(mol, output_path):
    # Ensure the molecule object exists
    if mol is None:
        print("Molecule object is None, cannot save.")
        return
    
    # Create an SDWriter object
    writer = Chem.SDWriter(output_path)
    # Write the molecule to the file
    writer.write(mol)
    writer.close()
    # print(f"Successfully saved bond-complete molecule to {output_path}")    
python 复制代码
ligand_mol = load_pdb_with_adaptive_bonds("ligand_with_h.pdb")
save_mol_to_sdf(ligand_mol, "refined_ligand.sdf")
相关推荐
happylifetree2 分钟前
Python70:核心语法-函数进阶-案例2
python
王中阳Go30 分钟前
两个入口答案不一致先别调模型:6 行入口口径对账表(附脚本)
人工智能·后端·python·大语言模型·1024程序员节
BK贝壳42 分钟前
bool-hybrid-array 落盘我只认 .bha:用 Create_BHA / Ask_BHA 做布尔数组序列化
数据结构·python·numpy·1024程序员节
Sakura_j_h_1 小时前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
supabc1233 小时前
欧盟电子发票(XRechnung/Peppol/ZUGFeRD)踩坑记:用 Python 不装 Java 跑官方校验规则
xml·python·开源·电子发票·1024程序员节
ZhengEnCi3 小时前
AI Agent工具调用防护层怎么写-一份能直接跑的Python四层拦截实现
python·llm·agent
ZhengEnCi3 小时前
AI Agent工具误调用怎么解决-从参数校验到高危二次确认的四层工程防线
python·llm·agent
镜子AI4 小时前
镜子AI工程教程:用Python实现关键词布局检查器与语义改写查重(附完整代码)
人工智能·python·microsoft
小oo呆5 小时前
【学习心得】Python协程
python
信誓旦旦的程序猿6 小时前
【Python量化实战宝典 #22】涨跌停、连板情绪手册:情绪指标构建(Python 可跑)
python·1024程序员节·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口