小分子 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")
相关推荐
我叫黑大帅42 分钟前
如何通过 Python 实现招聘平台自动投递
后端·python·面试
其实防守也摸鱼1 小时前
CTF密码学综合教学指南--第九章
开发语言·网络·python·安全·网络安全·密码学·ctf
砚底藏山河1 小时前
Python量化开发:2026最佳实时股票数据API接口推荐与对比
开发语言·windows·python
研究点啥好呢2 小时前
专为求职者开发的“面馆”!!!摆脱面试焦虑!!!
python·面试·开源·reactjs·求职招聘·fastapi
DFT计算杂谈3 小时前
自动化脚本一键绘制三元化合物相图
java·运维·服务器·开发语言·前端·python·自动化
EW Frontier3 小时前
6G ISAC新范式:基于智能漏波天线的Wi‑Fi通感一体化系统设计与实测【附MATLAB+python代码】
开发语言·python·matlab·music·isac·doa·wi‑fi
姚青&3 小时前
测试技术体系
java·python
易标AI4 小时前
标书智能体(五)——如何让弱模型也能稳定输出复杂json
人工智能·python·提示词·智能体·招投标
Cyber4K5 小时前
【Python专项】Nginx访问日志分析时间范围处理示例
开发语言·python·nginx
周末也要写八哥5 小时前
代码中的注释的重要性(二)
开发语言·python