HOOOS

批量用FoldX计算ProteinMPNN突变体结合自由能变化(ddG)的高效工作流

0 15 结构生物学札记 FoldX结合自由能
Apple

在蛋白质计算设计中,ProteinMPNN 凭借其强大的序列生成能力,能够快速给出成百上千个潜在的突变序列。然而,ProteinMPNN 无法直接给出物理意义上的结合自由能变化($\Delta\Delta G_{\text{bind}}$)。

为了筛选出真正高亲和力的突变体,我们通常需要借助经典力场工具 FoldX

本文将分享一套成熟的自动化工作流,指导你如何将 ProteinMPNN 的输出自动转化为 FoldX 的突变输入,并批量计算复合物的结合自由能变化 $\Delta\Delta G_{\text{bind}}$。


核心逻辑与工作流设计

计算结合自由能变化($\Delta\Delta G_{\text{bind}}$)与计算稳定性变化($\Delta\Delta G_{\text{stability}}$)在 FoldX 中的运行逻辑不同。

  1. 稳定性 $\Delta\Delta G_{\text{stability}}$:直接通过 BuildModel 命令变异后输出的 Average_MutantModel.fxout(或 Raw_ 文件)中的总能量差值获取。
  2. 结合能 $\Delta\Delta G_{\text{bind}}$:必须先使用 BuildModel 生成突变体的 3D 结构,然后对野生型(WT)和突变体(MT)的复合物结构分别运行 AnalyseComplex 命令。两者输出的 Interaction Energy 之差,才是真正的 $\Delta\Delta G_{\text{bind}}$。

完整管道设计如下:

[WT.pdb] -> FoldX RepairPDB -> [WT_Repair.pdb]
                                     |
[ProteinMPNN FASTA/JSON] --------> [Python 差异比对脚本] -> [individual_list.txt]
                                     |
                               FoldX BuildModel
                                     |
                            [Mutant PDBs] & [WT_Repair.pdb]
                                     |
                             FoldX AnalyseComplex
                                     |
                       [Python 结果提取] -> [ddG 排序报表]

步骤一:野生型(WT)结构修复

FoldX 对原子碰撞非常敏感。在进行任何突变之前,必须对原始 PDB 进行能量最小化(RepairPDB)。

准备一个文件夹 workspace,将 foldx 可执行文件、WT.pdb 放进去,运行:

# 修复 PDB 结构
./foldx --command=RepairPDB --pdb=WT.pdb

运行后会生成 WT_Repair.pdb,后续所有的突变和能量比对都必须基于这个修复后的结构。


步骤二:解析 ProteinMPNN 输出并生成突变文件

ProteinMPNN 通常会输出一个 .fa (FASTA) 文件,其中第一条是野生型序列,后续是设计出来的突变体序列。

我们需要写一个 Python 脚本,将这些设计序列与野生型序列进行比对,识别出突变位点,并翻译成 FoldX 识别的 individual_list.txt 格式。

警惕陷阱:ProteinMPNN 输出的 FASTA 索引(0, 1, 2...)与 PDB 文件中的真实残基编号(Residue Number,可能从 105 开始,或者中间有缺失)往往不一致。
下面的脚本假设你已经确保了 FASTA 序列与 PDB 链的残基是一一对应的。如果是复杂结构,建议使用 Biopython 读取 PDB 的真实 res_id 进行映射。

新建 generate_mutants.py

import os
import re

def parse_fasta(fasta_path):
    """解析 ProteinMPNN 的 FASTA 文件"""
    sequences = []
    headers = []
    with open(fasta_path, 'r') as f:
        current_seq = []
        for line in f:
            line = line.strip()
            if line.startswith('>'):
                if current_seq:
                    sequences.append("".join(current_seq))
                    current_seq = []
                headers.append(line)
            else:
                current_seq.append(line)
        if current_seq:
            sequences.append("".join(current_seq))
    return headers, sequences

def generate_foldx_mutants(wt_seq, mut_seq, chain_id, pdb_start_num=1):
    """
    对比 WT 和 Mutant 序列,生成 FoldX 格式的突变字符串
    例如: RA102D (野生Arg, A链, 102号位置, 突变为Asp)
    """
    mutations = []
    for i, (wt_aa, mut_aa) in enumerate(zip(wt_seq, mut_seq)):
        if wt_aa != mut_aa:
            pdb_pos = i + pdb_start_num  # 映射到 PDB 真实编号
            mut_str = f"{wt_aa}{chain_id}{pdb_pos}{mut_aa}"
            mutations.append(mut_str)
    return mutations

# 配置参数
fasta_file = "mpnn_output.fa"  # ProteinMPNN 输出路径
chain_to_mutate = "A"          # 发生突变的链 ID
pdb_start_residue = 1          # 该链在 PDB 中的起始残基编号

headers, seqs = parse_fasta(fasta_file)
wt_sequence = seqs[0]          # 第一条通常是 WT

# 写入 individual_list.txt 供 FoldX 批量读取
# 格式要求:每一行代表一个突变体,多个突变位点用逗号隔开,分号结尾。
with open("individual_list.txt", "w") as f_out:
    for idx, mut_seq in enumerate(seqs[1:], start=1):
        muts = generate_foldx_mutants(wt_sequence, mut_seq, chain_to_mutate, pdb_start_residue)
        if muts:
            mut_line = ",".join(muts) + ";"
            f_out.write(f"{mut_line}\n")
            print(f"Mutant {idx}: {mut_line}")
        else:
            # 写入空行或占位符以保持索引一致
            f_out.write(";\n")

步骤三:运行 FoldX BuildModel 产生突变体结构

FoldX 的 BuildModel 命令可以根据 individual_list.txt 批量对 WT_Repair.pdb 进行突变。

运行以下命令:

./foldx --command=BuildModel \
        --pdb=WT_Repair.pdb \
        --mutant-file=individual_list.txt \
        --numberOfRuns=1 \
        --output-file=mutant_models

注:--numberOfRuns=1 是为了提高运行速度(仅构建 1 个构象)。如果追求极高精度,可以设置为 3 或 5,最后取均值,但计算时间会成倍增加。

运行结束后,你的工作目录下会生成一系列命名形如 WT_Repair_1.pdb, WT_Repair_2.pdb ... 的文件,分别对应 individual_list.txt 中每一行的突变体。


步骤四:批量运行 AnalyseComplex 计算结合能

拿到突变体结构后,我们需要计算复合物中两条链(例如受体 A 和配体 B)之间的结合能。

首先,我们需要测定野生型的结合能:

./foldx --command=AnalyseComplex --pdb=WT_Repair.pdb --analyseComplexChains=A,B

这会生成一个 Summary_WT_Repair_AC.fxout 文件。

接着,我们写一个 Bash 脚本批量对所有生成的突变体运行 AnalyseComplex

#!/bin/bash

# 定义链的交互界面,例如 A 链和 B 链
CHAINS="A,B"
WT_NAME="WT_Repair"

# 循环处理所有突变体 PDB (假设你有 100 个突变体)
for pdb_file in WT_Repair_[0-9]*.pdb; do
    if [ -f "$pdb_file" ]; then
        echo "Processing $pdb_file..."
        # 运行 AnalyseComplex
        ./foldx --command=AnalyseComplex --pdb="$pdb_file" --analyseComplexChains=$CHAINS
    fi
done

运行该脚本后,每一个突变体 PDB 都会对应输出一个 Summary_WT_Repair_X_AC.fxout 文件(其中 X 是数字)。


步骤五:提取数据并计算 $\Delta\Delta G_{\text{bind}}$

每一个 Summary_*.fxout 文件的最后一行为计算结果。我们需要提取其中的 Interaction Energy 列(通常是第 6 列,视 FoldX 版本可能微调)。

以下 Python 脚本负责提取所有数据,计算 $\Delta\Delta G_{\text{bind}} = \Delta G_{\text{mutant}} - \Delta G_{\text{wt}}$ 并输出 CSV 报表:

import os
import pandas as pd

def get_interaction_energy(fxout_path):
    """从 AnalyseComplex 的 fxout 文件中提取 Interaction Energy"""
    if not os.path.exists(fxout_path):
        return None
    with open(fxout_path, 'r') as f:
        lines = f.readlines()
        for line in lines:
            if line.startswith("WT_Repair") or line.startswith("WT_"):
                parts = line.split('\t')
                # 交互能通常在第 6 列(索引为 5),请根据你生成的 fxout 实际表头核对
                try:
                    return float(parts[5])
                except ValueError:
                    continue
    return None

# 1. 获取野生型结合能
wt_energy = get_interaction_energy("Summary_WT_Repair_AC.fxout")
if wt_energy is None:
    raise FileNotFoundError("无法找到野生型的 AnalyseComplex 输出文件,请先运行野生型计算!")

print(f"Wild-Type Binding Energy: {wt_energy} kcal/mol")

# 2. 遍历并提取所有突变体结合能
results = []
# 假设我们有 100 个生成的突变体
for i in range(1, 1000):
    fxout_file = f"Summary_WT_Repair_{i}_AC.fxout"
    if os.path.exists(fxout_file):
        mt_energy = get_interaction_energy(fxout_file)
        if mt_energy is not None:
            ddg = mt_energy - wt_energy
            results.append({
                "Mutant_Index": i,
                "DG_bind_MT": mt_energy,
                "DG_bind_WT": wt_energy,
                "ddG_bind": ddg
            })

# 3. 输出并排序
df = pd.DataFrame(results)
# 结合能越负,说明结合越紧密。我们需要寻找 ddG_bind < 0 且绝对值尽可能大的突变体
df = df.sort_values(by="ddG_bind")
df.to_csv("protein_mpnn_foldx_ddg_results.csv", index=False)

print("\nTop 5 亲和力提升突变体:")
print(df.head(5))

避坑指南与高级优化

  1. 残基编号不一致(最经典灾难)
    ProteinMPNN 输出的 FASTA 没有 Chain 信息,而 FoldX 严重依赖链 ID(如 A)和 PDB 真实编号。如果你的 PDB 存在缺失残基(例如序列第 50 个残基在 PDB 里编号是 120),直接按索引递增会导致 FoldX 在错误的位点引入突变。务必在步骤二中使用 Biopython.PDB 模块来动态获取残基的真实 resseq
  2. 结合能变好,但稳定性崩溃了
    有时候,AnalyseComplex 算出来的 $\Delta\Delta G_{\text{bind}}$ 非常优秀(极负),但这是因为突变体在界面上强行形成了盐桥,而实际上这个突变严重破坏了单体本身的稳定性(导致蛋白根本无法折叠)。
    建议:在筛选时,不仅要看 $\Delta\Delta G_{\text{bind}}$,还要看 BuildModel 输出文件中的总能量变化 $\Delta\Delta G_{\text{stability}}$。只有**两者均不显著变差(甚至都变好)**的突变体才值得进行湿实验验证。
  3. 多核并行加速
    FoldX 本身是单线程运行的。如果你有上千个突变体,直接用 shell 循环会非常慢。建议使用 Bash 中的 xargsGNU Parallel 工具进行多进程并行加速:
    # 使用 GNU Parallel 并行运行 AnalyseComplex,-j 8 表示开 8 个线程
    ls WT_Repair_[0-9]*.pdb | parallel -j 8 "./foldx --command=AnalyseComplex --pdb={} --analyseComplexChains=A,B"
    

点评评价

captcha
健康