342

用 OpenBabel 批量转格式:化学文件处理自动化

OpenBabel 批量转格式是化学文件处理的常备工具。这篇给出常用命令、批量脚本,以及什么时候该改用 RDKit。

化学工具生态里格式混乱是常态:一个工具只吃 MOL2,另一个输出 PDBQT,第三个要 XYZ。Open Babel(见 216《OpenBabel》)是处理这类问题的常备工具,支持一百多种格式互转。

安装

mamba install -c conda-forge openbabel
obabel -L formats | head -30      # 查看支持的格式
obabel -V

最常用的命令

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

# SMILES → 3D 结构
obabel -:"CC(=O)Oc1ccccc1C(=O)O" -O aspirin.sdf --gen3d
obabel -:"CC(=O)O" -O out.sdf --gen3d --best      # 更高质量但慢

# 按 pH 加氢(对接前常用)
obabel input.sdf -O output.sdf -p 7.4

# 加氢 / 去氢
obabel input.sdf -O with_h.sdf -h
obabel input.sdf -O no_h.sdf -d

# 去盐(只保留最大片段)
obabel input.smi -O clean.smi -r

# 按 InChI 去重
obabel library.sdf -O unique.sdf --unique

# 拆分多分子文件
obabel library.sdf -O mol_.sdf -m

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

# 构象搜索
obabel input.sdf -O confs.sdf --conformer --nconf 20 --score rmsd

# 转换时顺手过滤
obabel library.sdf -O filtered.sdf --filter "MW<500 && logP<5"

# 加上计算的属性
obabel input.sdf -O out.sdf --append "MW logP TPSA"

批量处理脚本

#!/bin/bash
# 批量把 SDF 转成对接用的 PDBQT,带失败统计

mkdir -p pdbqt logs
success=0; failed=0

for f in sdf/*.sdf; do
  name=$(basename "$f" .sdf)
  if obabel "$f" -O "pdbqt/${name}.pdbqt" -p 7.4 \
       2> "logs/${name}.log"; then
    success=$((success+1))
  else
    failed=$((failed+1))
    echo "FAILED: $name" >> logs/failures.txt
  fi
done

echo "成功 $success,失败 $failed"

# 大批量时用 GNU parallel 加速
# ls sdf/*.sdf | parallel -j 16 \
#   'obabel {} -O pdbqt/{/.}.pdbqt -p 7.4 2>/dev/null'

Python 接口

from openbabel import pybel

# 读取并处理
for mol in pybel.readfile("sdf", "library.sdf"):
    mol.addh()
    mol.make3D(forcefield="mmff94", steps=100)
    mol.localopt(forcefield="mmff94", steps=500)
    desc = mol.calcdesc(["logP", "TPSA", "MW"])
    print(mol.title, {k: round(v, 2) for k, v in desc.items()})
    mol.write("mol2", f"out/{mol.title}.mol2", overwrite=True)

# 格式转换的简写
mol = pybel.readstring("smi", "CC(=O)Oc1ccccc1C(=O)O")
mol.make3D()
print(mol.write("pdb"))

什么时候该改用 RDKit

这是使用 Open Babel 时最该有的判断。两者功能重叠,但可靠性不同:

任务 推荐 原因
格式转换(尤其冷门格式) Open Babel 格式覆盖面无可替代
分子标准化、去盐 RDKit MolStandardize 行为更明确可控
芳香性 / 键级感知 RDKit Open Babel 从 PDB 推断易出错
构象生成 RDKit ETKDGv3 质量更好
指纹与描述符 RDKit 实现更主流,与文献可比
子结构匹配 RDKit SMARTS 支持更完整
批量 pH 处理 两者皆可 要求高时用 Dimorphite-DL

务实结论:把 Open Babel 当格式转换器用,把化学逻辑交给 RDKit。

转换会丢失什么

# 不同格式携带的信息不同,转换是有损的

# SMILES  → 无 3D 坐标
# XYZ     → 无键级、无电荷
# PDB     → 无键级(靠距离推断)
# PDBQT   → 无键级,有 AutoDock 原子类型与部分电荷
# MOL2    → 有键级与电荷
# SDF     → 有键级、可带属性字段

# 建议:链路上始终保留一份权威的 SDF 或 SMILES 作为真相来源,
#      需要什么格式就从它转,而不是链式转换

# 反例(错误做法):
#   SDF → PDBQT → PDB → MOL2   ← 每次转换都可能丢信息,累积出错
# 正例:
#   SDF → PDBQT
#   SDF → MOL2
#   SDF → PDB

验证转换结果

from rdkit import Chem

def verify_conversion(original_smiles, converted_file, fmt="sdf"):
    """用 InChIKey 比对,确认转换没改变分子"""
    orig = Chem.MolFromSmiles(original_smiles)
    if fmt == "sdf":
        conv = Chem.SDMolSupplier(converted_file, removeHs=True)[0]
    elif fmt == "mol2":
        conv = Chem.MolFromMol2File(converted_file, removeHs=True)
    if conv is None:
        return False, "cannot_parse_converted"
    k1 = Chem.MolToInchiKey(orig)
    k2 = Chem.MolToInchiKey(conv)
    # 比较骨架层(前 14 位),忽略质子化差异
    return k1[:14] == k2[:14], f"{k1[:14]} vs {k2[:14]}"

ok, msg = verify_conversion("CC(=O)Oc1ccccc1C(=O)O", "aspirin.sdf")
print("转换正确" if ok else f"⚠ 转换后分子改变: {msg}")

常见坑与提示

  • 把 Open Babel 当格式转换器,化学逻辑交给 RDKit 更可靠
  • 避免链式转换,始终从一份权威的 SDF/SMILES 派生各种格式;
  • 从 PDB 推断键级容易出错,重要分子要用 InChIKey 验证;
  • 管线中锁定版本——不同版本的 pH 模型与芳香性感知有差异。

延伸资源