小分子 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")
相关推荐
lzqrzpt3 分钟前
临沂LED驱动电源制造工艺解析与工程选型避坑指南
python·制造
ai小陈4 分钟前
Python 3.12与CUDA 12.8镜像实战:启动GPU实例后的五项自检
开发语言·人工智能·python·深度学习·ai·gpu算力
Thomas.Sir8 分钟前
第48课:TensorFlow|TF模型线上部署入门【本地服务封装、接口快速开发】
人工智能·python·tensorflow
Bruce_Liuxiaowei11 分钟前
录屏片段无损合并:从 ffmpeg concat 原理到 Python 自动化脚本
python·ffmpeg·自动化
renzao_ai36 分钟前
本地 35B 大模型部署实战:Ollama 跑 Ornith-35B 全流程
python·llama·免费ai大模型
the局外人1 小时前
学习 FastAPI 的 Day 4:完成用户系统与接口联调(完结)
后端·python·fastapi
AIFQuant1 小时前
Python股票实时价格告警系统:WebSocket订阅与REST快照实战
开发语言·python·websocket·a股行情
名字还没想好☜1 小时前
Java Collectors.groupingBy 进阶:多级分组、下游收集器与统计聚合一次搞定
java·windows·后端·python·spring
2601_962078232 小时前
Python接口自动化流程(pytest)
python·pytest·接口自动化·测试框架·断言