107

OpenBabel 转格式:PDB、SDF、MOL2、PDBQT 怎么互转

分子格式转换看似简单,实则处处是坑。这篇讲清各格式的信息含量、转换中的信息丢失与正确做法。

格式转换是计算化学中最容易被低估的错误来源:一次不当的转换可能悄无声息地改变分子的键级、电荷、立体化学或质子化态,而后续所有计算都基于这个错误的分子。

各格式的信息含量

格式 三维坐标 键级 电荷 立体化学
SMILES 隐式
SDF / MOL 可显可隐
MOL2 有(含部分电荷) 通常显式
PDB 隐含于坐标 常缺失
PDBQT 有(部分电荷) 隐含 仅极性氢
XYZ 隐含 显式

核心原则:只能从信息多的格式转到信息少的格式,反向转换必然涉及推断。从 PDB 或 XYZ 恢复键级,是靠几何猜测的——这是最主要的错误来源

OpenBabel 基本用法

# 安装
conda install -c conda-forge openbabel
# 或 apt install openbabel / brew install open-babel

# 基本转换
obabel input.sdf -O output.mol2

# 加氢(按 pH)
obabel input.sdf -O output.sdf -p 7.4

# 删氢
obabel input.sdf -O output.sdf -d

# 生成三维坐标
obabel input.smi -O output.sdf --gen3d

# 能量最小化
obabel input.sdf -O output.sdf --minimize --ff MMFF94 --steps 500

# 批量分割多分子文件
obabel ligands.sdf -O out.sdf -m

# 只输出满足条件的分子(过滤)
obabel input.sdf -O output.sdf --filter "MW<500"

# 查看支持的格式
obabel -L formats

关键的坑

# 坑一:--gen3d 的质量不如 RDKit ETKDG
#   OpenBabel 的三维生成较粗糙
#   → 生成构象建议用 RDKit(见 279)

# 坑二:-p 的质子化预测不可靠
#   OpenBabel 的 pH 模型很简单
#   → 药物分子的质子化建议用 Dimorphite-DL(见 046)

# 坑三:PDB → SDF 的键级推断会出错
#   典型错误:
#     芳香环被识别成交替单双键(凯库勒式)或反之
#     羧酸/酰胺的键级错误
#     磷酸/硫酸基团的键级错误
#   → 从 PDB 恢复配体,正确做法是用模板:

from rdkit import Chem

template = Chem.MolFromSmiles("CC(=O)Nc1ccc(O)cc1")  # 已知的正确结构
pdb_mol = Chem.MolFromPDBFile("ligand.pdb", removeHs=False)
# 用模板赋予正确的键级
fixed = Chem.AllChem.AssignBondOrdersFromTemplate(template, pdb_mol)

# 坑四:立体化学在无键级的格式中容易丢失
#   PDB/XYZ 中手性由坐标隐含,
#   转换时如果键级推断错了,手性判断也会错
#   → 转换后必须检查:
#      Chem.FindMolChiralCenters(mol, useLegacyImplementation=False)

# 坑五:静默失败
#   OpenBabel 遇到问题常给出警告而非报错,
#   继续输出一个错误的分子
#   → 必须检查输出,不能假设成功

什么时候用 OpenBabel,什么时候不用

任务 推荐
SDF ↔ MOL2 ↔ PDB 的坐标转换 OpenBabel 可以
处理罕见格式(CIF、CML 等) OpenBabel(格式支持最广)
批量分割/合并文件 OpenBabel
SMILES → 三维构象 RDKit ETKDG
质子化态处理 Dimorphite-DL
分子标准化 RDKit / ChEMBL Structure Pipeline(见 046《分子标准化》
转 PDBQT Meeko(见 106《Meeko》
PDBQT → SDF Meeko mk_export
PDB 中提取配体并恢复键级 RDKit + 模板

OpenBabel 的定位是「格式支持最广的通用转换器」,而非「每个任务的最佳工具」。

转换后的验证

from rdkit import Chem

def verify_conversion(before_path, after_path):
    """转换前后的一致性检查"""
    a = Chem.MolFromMolFile(before_path)
    b = Chem.MolFromMolFile(after_path)
    if a is None or b is None:
        return "解析失败"

    checks = {}
    # 1) 分子式是否一致(最基本的检查)
    from rdkit.Chem.rdMolDescriptors import CalcMolFormula
    checks["formula"] = (CalcMolFormula(a), CalcMolFormula(b))

    # 2) 规范 SMILES 是否一致(最严格的检查)
    checks["smiles"] = (Chem.MolToSmiles(a), Chem.MolToSmiles(b))

    # 3) 净电荷
    checks["charge"] = (Chem.GetFormalCharge(a), Chem.GetFormalCharge(b))

    # 4) 手性中心
    checks["chiral"] = (
        Chem.FindMolChiralCenters(a, useLegacyImplementation=False),
        Chem.FindMolChiralCenters(b, useLegacyImplementation=False),
    )

    diffs = {k: v for k, v in checks.items() if v[0] != v[1]}
    return diffs if diffs else "一致"

# 在批量流程中,这个检查应该自动化并记录
# 任何不一致都应该报警而非静默通过

Python 接口

# OpenBabel 的 Python 绑定
from openbabel import pybel

# 读取
mol = next(pybel.readfile("sdf", "input.sdf"))
print(mol.molwt, mol.formula)
print(mol.write("smi").strip())

# 计算描述符
print(mol.calcdesc(["logP", "TPSA", "MW"]))

# 转换并写出
mol.write("mol2", "output.mol2", overwrite=True)

# 批量处理
out = pybel.Outputfile("sdf", "processed.sdf", overwrite=True)
for mol in pybel.readfile("smi", "input.smi"):
    mol.make3D(forcefield="mmff94", steps=100)
    out.write(mol)
out.close()

# 但在 Python 中,RDKit 通常是更好的选择
# OpenBabel 的价值主要在命令行的格式覆盖面

关键要点

  • 只能从信息多的格式转到信息少的——反向转换必然涉及推断,是主要错误来源;
  • 从 PDB 恢复配体应该用 RDKit 的模板方法,而非直接推断键级;
  • OpenBabel 的 --gen3d-p 质量不如 RDKit 与 Dimorphite-DL;
  • 转换后必须验证(规范 SMILES、净电荷、手性),OpenBabel 常静默失败。

延伸资源