生物分子相互作用的随机模拟

生物分子相互作用的随机模拟:模拟生物分子(如蛋白质和配体)之间的随机结合和解离过程。使用 Python 的随机数生成功能,根据分子的结合常数和解离常数,模拟分子在溶液中的相互作用。统计不同时间点结合的分子数量和游离的分子数量,分析分子浓度、温度等因素对相互作用的影响。通过模拟多次实验,评估相互作用的可靠性和变异性。

import numpy as np
import matplotlib.pyplot as plt
from typing import List, Tuple, Dict
import pandas as pd
import os
from matplotlib.colors import LinearSegmentedColormap

# 添加字体设置以确保中文正常显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "sans-serif"]
plt.rcParams["axes.unicode_minus"] = False  # 确保负号正确显示


class BiomolecularSimulator:
    """生物分子相互作用随机模拟类"""

    def __init__(self, protein_conc: float, ligand_conc: float,
                 k_on: float, k_off: float, volume: float = 1.0,
                 temperature: float = 298.15, seed: int = None):
        """
        初始化生物分子模拟参数

        参数:
            protein_conc: 蛋白质初始浓度 (μM)
            ligand_conc: 配体初始浓度 (μM)
            k_on: 结合速率常数 (μM^-1·s^-1)
            k_off: 解离速率常数 (s^-1)
            volume: 反应体积 (L)
            temperature: 温度 (K)
            seed: 随机数生成器种子
        """
        # 转换浓度为分子数
        avogadro = 6.022e23  # 阿伏伽德罗常数
        self.initial_protein = int(protein_conc * 1e-6 * avogadro * volume)
        self.initial_ligand = int(ligand_conc * 1e-6 * avogadro * volume)
        self.k_on = k_on * 1e-6  # 转换为分子数单位
        self.k_off = k_off
        self.volume = volume
        self.temperature = temperature
        self.boltzmann = 1.38e-23  # 玻尔兹曼常数 (J/K)

        # 初始化随机数生成器
        self.rng = np.random.default_rng(seed)

        # 模拟结果存储
        self.simulation_results = []

    def calculate_reaction_rates(self, protein: int, ligand: int, complex: int) -> Tuple[float, float]:
        """
        计算结合和解离反应的速率

        参数:
            protein: 当前蛋白质分子数
            ligand: 当前配体分子数
            complex: 当前复合物分子数

        返回:
            结合速率和解离速率
        """
        binding_rate = self.k_on * protein * ligand
        dissociation_rate = self.k_off * complex
        return binding_rate, dissociation_rate

    def run_simulation(self, max_time: float, record_interval: float = 0.1) -> Dict:
        """
        运行 Gillespie 随机模拟

        参数:
            max_time: 最大模拟时间 (s)
            record_interval: 记录数据的时间间隔 (s)

        返回:
            包含模拟结果的字典
        """
        # 初始化分子数和时间
        protein = self.initial_protein
        ligand = self.initial_ligand
        complex = 0
        time = 0.0

        # 记录初始状态
        next_record_time = 0.0
        time_points = [time]
        protein_counts = [protein]
        ligand_counts = [ligand]
        complex_counts = [complex]

        # 主模拟循环
        while time < max_time:
            # 计算反应速率
            binding_rate, dissociation_rate = self.calculate_reaction_rates(protein, ligand, complex)
            total_rate = binding_rate + dissociation_rate

            # 如果没有可能的反应,退出循环
            if total_rate <= 0:
                break

            # 确定下一个反应的时间
            dt = self.rng.exponential(1.0 / total_rate)
            time += dt

            # 记录数据
            while time >= next_record_time and next_record_time <= max_time:
                time_points.append(next_record_time)
                protein_counts.append(protein)
                ligand_counts.append(ligand)
                complex_counts.append(complex)
                next_record_time += record_interval

            # 确定发生的反应类型
            if self.rng.random() < binding_rate / total_rate:
                # 发生结合反应
                protein -= 1
                ligand -= 1
                complex += 1
            else:
                # 发生解离反应
                protein += 1
                ligand += 1
                complex -= 1

        # 确保记录到最大时间点
        if time_points[-1] < max_time:
            time_points.append(max_time)
            protein_counts.append(protein)
            ligand_counts.append(ligand)
            complex_counts.append(complex)

        # 转换为浓度 (μM)
        avogadro = 6.022e23
        protein_conc = np.array(protein_counts) / (avogadro * self.volume) * 1e6
        ligand_conc = np.array(ligand_counts) / (avogadro * self.volume) * 1e6
        complex_conc = np.array(complex_counts) / (avogadro * self.volume) * 1e6

        # 计算结合分数
        if self.initial_protein > 0:
            binding_fraction = complex_conc / (protein_conc + complex_conc)
        else:
            binding_fraction = np.zeros_like(complex_conc)

        # 存储结果
        result = {
            'time': np.array(time_points),
            'protein_concentration': protein_conc,
            'ligand_concentration': ligand_conc,
            'complex_concentration': complex_conc,
            'binding_fraction': binding_fraction
        }

        self.simulation_results.append(result)
        return result

    def run_multiple_simulations(self, num_simulations: int, max_time: float,
                                 record_interval: float = 0.1) -> List[Dict]:
        """
        运行多次独立模拟

        参数:
            num_simulations: 模拟次数
            max_time: 最大模拟时间 (s)
            record_interval: 记录数据的时间间隔 (s)

        返回:
            包含所有模拟结果的列表
        """
        all_results = []
        for i in range(num_simulations):
            result = self.run_simulation(max_time, record_interval)
            all_results.append(result)
            print(f"完成模拟 {i + 1}/{num_simulations}")
        return all_results

    def analyze_concentration_effect(self, protein_concentrations: List[float],
                                     ligand_concentration: float, num_simulations: int = 1,
                                     max_time: float = 10.0) -> Dict:
        """
        分析蛋白质浓度对结合的影响

        参数:
            protein_concentrations: 蛋白质浓度列表 (μM)
            ligand_concentration: 配体浓度 (μM)
            num_simulations: 每个浓度的模拟次数
            max_time: 最大模拟时间 (s)

        返回:
            包含不同浓度下结合分数的结果
        """
        results = {}

        for conc in protein_concentrations:
            # 更新蛋白质浓度
            avogadro = 6.022e23
            self.initial_protein = int(conc * 1e-6 * avogadro * self.volume)

            # 运行多次模拟
            conc_results = []
            for _ in range(num_simulations):
                result = self.run_simulation(max_time)
                conc_results.append(result['binding_fraction'][-1])  # 最终结合分数

            # 计算平均值和标准差
            results[conc] = {
                'mean': np.mean(conc_results),
                'std': np.std(conc_results),
                'all_values': conc_results
            }

        # 恢复原始蛋白质浓度
        self.initial_protein = int(self.initial_protein * 1e-6 * avogadro * self.volume)

        return results

    def analyze_temperature_effect(self, temperatures: List[float],
                                   num_simulations: int = 1, max_time: float = 10.0) -> Dict:
        """
        分析温度对结合的影响

        参数:
            temperatures: 温度列表 (K)
            num_simulations: 每个温度的模拟次数
            max_time: 最大模拟时间 (s)

        返回:
            包含不同温度下结合分数的结果
        """
        results = {}

        for temp in temperatures:
            # 更新温度
            self.temperature = temp

            # 运行多次模拟
            temp_results = []
            for _ in range(num_simulations):
                result = self.run_simulation(max_time)
                temp_results.append(result['binding_fraction'][-1])  # 最终结合分数

            # 计算平均值和标准差
            results[temp] = {
                'mean': np.mean(temp_results),
                'std': np.std(temp_results),
                'all_values': temp_results
            }

        # 恢复原始温度
        self.temperature = 298.15

        return results

    def calculate_equilibrium_constant(self, results: Dict) -> float:
        """
        从模拟结果计算平衡常数

        参数:
            results: 单次模拟的结果

        返回:
            平衡常数 Kd (μM)
        """
        # 获取最终浓度
        final_protein = results['protein_concentration'][-1]
        final_ligand = results['ligand_concentration'][-1]
        final_complex = results['complex_concentration'][-1]

        # 计算 Kd = [P][L]/[PL]
        if final_complex > 0:
            kd = (final_protein * final_ligand) / final_complex
        else:
            kd = float('inf')

        return kd


class SimulationAnalyzer:
    """模拟结果分析和可视化类"""

    @staticmethod
    def plot_concentration_time_course(results: Dict, title: str = "分子浓度随时间变化",
                                       save_path: str = None):
        """
        绘制浓度随时间变化的曲线

        参数:
            results: 模拟结果字典
            title: 图表标题
            save_path: 保存图像的路径,为None则不保存
        """
        time = results['time']
        protein = results['protein_concentration']
        ligand = results['ligand_concentration']
        complex = results['complex_concentration']

        plt.figure(figsize=(10, 6))
        plt.plot(time, protein, 'b-', label='蛋白质')
        plt.plot(time, ligand, 'r-', label='配体')
        plt.plot(time, complex, 'g-', label='复合物')

        plt.xlabel('时间 (s)')
        plt.ylabel('浓度 (μM)')
        plt.title(title)
        plt.legend()
        plt.grid(True)

        if save_path:
            plt.savefig(save_path)
            plt.close()
        else:
            plt.show()

    @staticmethod
    def plot_binding_fraction(results: Dict, title: str = "结合分数随时间变化",
                              save_path: str = None):
        """
        绘制结合分数随时间变化的曲线

        参数:
            results: 模拟结果字典
            title: 图表标题
            save_path: 保存图像的路径,为None则不保存
        """
        time = results['time']
        binding_fraction = results['binding_fraction']

        plt.figure(figsize=(10, 6))
        plt.plot(time, binding_fraction, 'm-')

        plt.xlabel('时间 (s)')
        plt.ylabel('结合分数')
        plt.title(title)
        plt.grid(True)

        if save_path:
            plt.savefig(save_path)
            plt.close()
        else:
            plt.show()

    @staticmethod
    def plot_multiple_simulations(all_results: List[Dict], title: str = "多次模拟结果比较",
                                  save_path: str = None):
        """
        绘制多次模拟的结合分数曲线

        参数:
            all_results: 所有模拟结果的列表
            title: 图表标题
            save_path: 保存图像的路径,为None则不保存
        """
        plt.figure(figsize=(10, 6))

        # 创建自定义颜色映射
        colors = plt.cm.viridis(np.linspace(0, 1, len(all_results)))

        for i, result in enumerate(all_results):
            time = result['time']
            binding_fraction = result['binding_fraction']
            plt.plot(time, binding_fraction, color=colors[i], alpha=0.7,
                     label=f'模拟 {i + 1}')

        plt.xlabel('时间 (s)')
        plt.ylabel('结合分数')
        plt.title(title)
        plt.grid(True)
        plt.legend()

        if save_path:
            plt.savefig(save_path)
            plt.close()
        else:
            plt.show()

    @staticmethod
    def plot_concentration_effect(results: Dict, title: str = "蛋白质浓度对结合的影响",
                                  save_path: str = None):
        """
        绘制蛋白质浓度对结合分数的影响

        参数:
            results: 浓度分析结果
            title: 图表标题
            save_path: 保存图像的路径,为None则不保存
        """
        concentrations = sorted(results.keys())
        mean_values = [results[conc]['mean'] for conc in concentrations]
        std_values = [results[conc]['std'] for conc in concentrations]

        plt.figure(figsize=(10, 6))
        plt.errorbar(concentrations, mean_values, yerr=std_values, fmt='o-', capsize=5)

        plt.xlabel('蛋白质浓度 (μM)')
        plt.ylabel('平衡结合分数')
        plt.title(title)
        plt.grid(True)

        if save_path:
            plt.savefig(save_path)
            plt.close()
        else:
            plt.show()

    @staticmethod
    def plot_temperature_effect(results: Dict, title: str = "温度对结合的影响",
                                save_path: str = None):
        """
        绘制温度对结合分数的影响

        参数:
            results: 温度分析结果
            title: 图表标题
            save_path: 保存图像的路径,为None则不保存
        """
        temperatures = sorted(results.keys())
        mean_values = [results[temp]['mean'] for temp in temperatures]
        std_values = [results[temp]['std'] for temp in temperatures]

        plt.figure(figsize=(10, 6))
        plt.errorbar(temperatures, mean_values, yerr=std_values, fmt='o-', capsize=5)

        plt.xlabel('温度 (K)')
        plt.ylabel('平衡结合分数')
        plt.title(title)
        plt.grid(True)

        if save_path:
            plt.savefig(save_path)
            plt.close()
        else:
            plt.show()


def run_simulation_example():
    """运行模拟示例"""
    # 创建输出目录
    if not os.path.exists('simulation_results'):
        os.makedirs('simulation_results')

    # 初始化模拟器 (典型的蛋白质-配体相互作用参数)
    simulator = BiomolecularSimulator(
        protein_conc=1.0,  # 蛋白质浓度 (μM)
        ligand_conc=5.0,  # 配体浓度 (μM)
        k_on=1.0e3,  # 结合速率常数 (μM^-1·s^-1)
        k_off=0.1,  # 解离速率常数 (s^-1)
        volume=1.0e-15,  # 体积 (L, 相当于1飞升)
        temperature=298.15,  # 温度 (K)
        seed=42  # 随机数种子,保证结果可重复
    )

    # 1. 运行单次模拟
    print("运行单次模拟...")
    single_result = simulator.run_simulation(max_time=10.0, record_interval=0.01)

    # 计算平衡常数
    kd = simulator.calculate_equilibrium_constant(single_result)
    print(f"计算得到的平衡常数 Kd = {kd:.4f} μM")
    print(f"理论 Kd = {simulator.k_off / simulator.k_on * 1e6:.4f} μM")

    # 可视化单次模拟结果
    analyzer = SimulationAnalyzer()
    analyzer.plot_concentration_time_course(
        single_result,
        title="蛋白质-配体结合的浓度变化",
        save_path="simulation_results/concentration_time_course.png"
    )
    analyzer.plot_binding_fraction(
        single_result,
        title="蛋白质-配体结合分数变化",
        save_path="simulation_results/binding_fraction.png"
    )

    # 2. 运行多次模拟,评估变异性
    print("\n运行多次模拟以评估变异性...")
    num_simulations = 10
    all_results = simulator.run_multiple_simulations(
        num_simulations=num_simulations,
        max_time=10.0,
        record_interval=0.1
    )

    # 计算平均平衡常数
    kd_values = [simulator.calculate_equilibrium_constant(result) for result in all_results]
    print(f"多次模拟得到的平均 Kd = {np.mean(kd_values):.4f} ± {np.std(kd_values):.4f} μM")

    # 可视化多次模拟结果
    analyzer.plot_multiple_simulations(
        all_results,
        title=f"{num_simulations}次独立模拟的结合分数比较",
        save_path="simulation_results/multiple_simulations.png"
    )

    # 3. 分析蛋白质浓度的影响
    print("\n分析蛋白质浓度的影响...")
    protein_concentrations = [0.1, 0.5, 1.0, 2.0, 5.0, 10.0]  # μM
    conc_results = simulator.analyze_concentration_effect(
        protein_concentrations=protein_concentrations,
        ligand_concentration=5.0,  # 保持配体浓度恒定
        num_simulations=3,
        max_time=10.0
    )

    # 可视化浓度影响
    analyzer.plot_concentration_effect(
        conc_results,
        title="蛋白质浓度对平衡结合分数的影响",
        save_path="simulation_results/concentration_effect.png"
    )

    # 4. 分析温度的影响
    print("\n分析温度的影响...")
    temperatures = [273.15, 283.15, 293.15, 303.15, 313.15]  # K (0°C 到 40°C)
    temp_results = simulator.analyze_temperature_effect(
        temperatures=temperatures,
        num_simulations=3,
        max_time=10.0
    )

    # 可视化温度影响
    analyzer.plot_temperature_effect(
        temp_results,
        title="温度对平衡结合分数的影响",
        save_path="simulation_results/temperature_effect.png"
    )

    print("\n模拟完成!结果已保存到 simulation_results 目录")


if __name__ == "__main__":
    run_simulation_example()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值