使用 ASE 拼接分子

在部分应用场景下,我们需要对两个分子片段进行拼接,例如锂电电解液数据库 LiBE

然而,当前并没有合适的拼接方法。下面是一些已有方法的调研结果:

  1. 在 LiBE 论文的附录里,作者使用 pymatgen 进行分子拼接。
  • 其思路是:将两分子整体平移到靠近的位置,互不成键,不考虑成键原子在分子中的位置。作者提到,后续用 薛定谔 套件 (Schrödinger Suite) + 力场 采样出一个初猜。
  • 简单说就是 pymatgen 只是让两分子靠近,真正拼接还得用力场。不行。
  1. 一番搜索后,在 这个案例 里,作者提到,使用 官能团取代 的思路可以实现 3D 层面的对接
  • 需要将需要对接的原子调整到分子中第二的位置,在分子中排序第一的位置添加一个虚原子,虚原子的位置要跟需要对接的原子成键
  • 使用官能团取代方法,消掉两个原子中新添加的虚原子,实现对接
  • 评估:3D 整体平移思想,仅保证需要对接位置的正确成键,可能会造成两分子重叠。需要添加虚原子,需要正确排序等,代码实现量中等
  1. 基于 rdkit 的方法
  • 基于官能团取代的方法:
    • 评估:两分子均需要用 smiles 表示,可能无法应用于部分自由基体系;源码是针对已知 dummy atom 的情况,而我们没有 dummy atom,面临与 pymatgen 相当的工程量
  • 基于 reactant 的方法:
    • 评估:使用 SMARTS 正确表示化学反应,需要用 SMILES 表示两分子,化学反应本身可能是遵循模版法
  1. 基于蛋白质拼接(dock)思路的方法:
  • 如果能顺利调用相关程序,工程量应该不大,但可能学习相关软件成本较高
  • 蛋白拼接精度要求很低

考虑以上种种问题,本人决定独自写一个拼接脚本,如下:

import ase
import numpy as np
from ase import Atoms, Atom
from ase.data import covalent_radii, atomic_numbers
from ase.io import read, write
from ase.neighborlist import build_neighbor_list, natural_cutoffs
from scipy.spatial import distance_matrix


def get_bond_length(atom_1_symbol: str, atom_2_symbol: str, skin: float = 0):
    return covalent_radii[atomic_numbers[atom_1_symbol]] + covalent_radii[atomic_numbers[atom_2_symbol]] + skin


def get_nearest_neighbor(a_molecule: ase.Atoms, atom_index: int, cutoff_mult: float = None, sort_flag: bool = True):
    if cutoff_mult:
        cutoffs = natural_cutoffs(atoms=a_molecule, mult=cutoff_mult)
        neighborList = build_neighbor_list(a_molecule, cutoffs=cutoffs, bothways=True, self_interaction=False)
    else:
        neighborList = build_neighbor_list(a_molecule, bothways=True, self_interaction=False)
    dok_matrix = neighborList.get_connectivity_matrix()
    dok_adj = dok_matrix[atom_index]
    adj_array = dok_adj.tocoo().col
    if sort_flag:
        dist = []
        for ii in adj_array:
            dist.append(np.linalg.norm(a_molecule.positions[atom_index] - a_molecule.positions[ii]))
        map_dict = dict(zip(dist, adj_array))
        sorted_dist = sorted(dist)
        sorted_neighbor_indexes = []
        for a_dist in sorted_dist:
            sorted_neighbor_indexes.append(map_dict[a_dist])
        return sorted_neighbor_indexes
    else:
        return adj_array


def swap_atoms(a_molecule: ase.Atoms, atom_index_1: int, atom_index_2: int):
    a_molecule_copy = a_molecule.copy()
    a_molecule[atom_index_1].position = a_molecule_copy[atom_index_2].position
    a_molecule[atom_index_1].symbol = a_molecule_copy[atom_index_2].symbol
    a_molecule[atom_index_2].position = a_molecule_copy[atom_index_1].position
    a_molecule[atom_index_2].symbol = a_molecule_copy[atom_index_1].symbol
    return a_molecule


def rm_and_sort_atoms(a_molecule: ase.Atoms, atom_index_to_rm: int):
    nearest_neighbor_index = get_nearest_neighbor(a_molecule=a_molecule, atom_index=atom_index_to_rm)[0]
    del a_molecule[atom_index_to_rm]
    if nearest_neighbor_index > atom_index_to_rm:
        nearest_neighbor_index = nearest_neighbor_index - 1
    a_molecule = swap_atoms(a_molecule=a_molecule, atom_index_1=0, atom_index_2=nearest_neighbor_index)
    return a_molecule


def get_addon_pos_by_sample(addon_symbol: str, tgt_atom_index: int, tgt_molecule: Atoms, sample_times: int = 1000,
                            skin: float = 0, cutoff_mult: float = 1.5):
    tgt_atom = tgt_molecule[tgt_atom_index]
    tgt_bond_length = get_bond_length(atom_1_symbol=addon_symbol, atom_2_symbol=tgt_atom.symbol, skin=skin)
    neighbor_indexes = get_nearest_neighbor(a_molecule=tgt_molecule, atom_index=tgt_atom_index, cutoff_mult=cutoff_mult)
    neighbor_positions = tgt_molecule.get_positions()[neighbor_indexes]

    min_rev_distance = 100000000
    for i in range(sample_times):
        theta = np.random.uniform(0, 2 * np.pi)
        phi = np.random.uniform(0, np.pi)
        x = np.sin(phi) * np.cos(theta)
        y = np.sin(phi) * np.sin(theta)
        z = np.cos(phi)
        new_atom_position = tgt_atom.position + tgt_bond_length * np.array([x, y, z])
        distances = distance_matrix(new_atom_position.reshape((1, -1)), neighbor_positions).reshape(-1)
        a_distance = (1 / distances).sum()

        if a_distance < min_rev_distance:
            min_rev_d_point = new_atom_position
            min_rev_distance = a_distance

    return min_rev_d_point, neighbor_indexes


def combine_2_mols(molecule_1: Atoms, molecule_2: Atoms, tgt_atom_1_index: int, tgt_atom_2_index: int,
                   sample_times: int = 1000, skin: float = 0, cutoff_mult: float = 1.5,
                   rotation_times: int = 3):
    # translation
    if len(molecule_1) > len(molecule_2):
        main_mol, sub_mol = molecule_1, molecule_2
        tgt_atom_main_index, tgt_atom_addon_index = tgt_atom_1_index, tgt_atom_2_index
    else:
        main_mol, sub_mol = molecule_2, molecule_1
        tgt_atom_main_index, tgt_atom_addon_index = tgt_atom_2_index, tgt_atom_1_index
    addon_atom = sub_mol[tgt_atom_addon_index]
    addon_position, neighbor_indexes = get_addon_pos_by_sample(addon_symbol=addon_atom.symbol,
                                                               tgt_atom_index=tgt_atom_main_index,
                                                               tgt_molecule=main_mol,
                                                               sample_times=sample_times, skin=skin,
                                                               cutoff_mult=cutoff_mult)
    translation_vec = addon_position - addon_atom.position
    sub_mol.translate(translation_vec)
    # rotate along with the nearest neighbors to minimize the fragments repulsion
    main_mol_positions = main_mol.get_positions()
    min_rev_distance = 100000000
    sub_mol_nb_indexes = get_nearest_neighbor(a_molecule=sub_mol, atom_index=tgt_atom_addon_index, cutoff_mult=cutoff_mult)
    for i in range(sample_times):
        sub_mol_copy = sub_mol.copy()
        for a_neighbor_index in sub_mol_nb_indexes[:rotation_times]:
            rotate_angle = np.random.uniform(0, 360)
            rotate_vec = addon_position - sub_mol_copy[a_neighbor_index].position
            sub_mol_copy.rotate(a=rotate_angle, v=rotate_vec, center=addon_position)
            sub_mol_copy_positions = sub_mol_copy.get_positions()
            frag_distances = distance_matrix(sub_mol_copy_positions, main_mol_positions)
            a_frag_distance = (1 / frag_distances).sum()
            if a_frag_distance < min_rev_distance:
                min_rev_distance = a_frag_distance
                final_sub_mol = sub_mol_copy.copy()

    # merge
    for an_atom in final_sub_mol:
        main_mol.append(an_atom)

    return main_mol


def combine_2_mols_with_dummy(molecule_1: Atoms, molecule_2: Atoms, dummy_atom_index_1: int, dummy_atom_index_2: int,
                              sample_times: int = 1000, skin: float = 0, cutoff_mult: float = 1.5,
                              rotation_times: int = 3):
    # remove two dummy atoms and sort the to-be-bonded atom to index 0
    mol_1_without_dummy = rm_and_sort_atoms(a_molecule=molecule_1, atom_index_to_rm=dummy_atom_index_1)
    mol_2_without_dummy = rm_and_sort_atoms(a_molecule=molecule_2, atom_index_to_rm=dummy_atom_index_2)
    combinend_mol = combine_2_mols(molecule_1=mol_1_without_dummy, molecule_2=mol_2_without_dummy,
                                   tgt_atom_1_index=0, tgt_atom_2_index=0, rotation_times=rotation_times,
                                   sample_times=sample_times, skin=skin, cutoff_mult=cutoff_mult)
    return combinend_mol


if __name__ == '__main__':
    from ase.visualize import view
    from ase.build.molecule import molecule

    main_mol = molecule('C7NH5')
    sub_mol = molecule('BDA')

    view(main_mol)
    view(sub_mol)

    final_mol = combine_2_mols_with_dummy(molecule_1=main_mol, molecule_2=sub_mol,
                                          dummy_atom_index_1=11, dummy_atom_index_2=6)


    view(final_mol)

效果如下:请添加图片描述

其核心思路 & 安装、使用方法如下:

How it works?

In short, it could be summarized into 2 steps:

  1. Translation:
    • The new bond length is determined by the summation of the covalent radii of the two atoms.
    • Set one atom as the central of a sphere, sample positions on this sphere and calculate the repulsion between the new addon to the whole molecule. (The repulsion is measured by the summation of the inverse distance.)
    • Choose the minimum repulsion position and translate the whole addon molecule as a rigid body.
  2. Rotation:
    • Set the bonded site as center and the nearest neighbor as rotation axes. Rotate the addon molecule as a rigid body
    • Sample rotation angles to minimize the repulsion between the addon molecule to the main molecule. (Again, the repulsion is measured by the summation of the inverse distance.)
    • Choose the minimum repulsion image and this is the final combination state.

Install

pip install combinemols3d

Usage

  1. Prepare your to-be-bonded molecules as ase.Atoms object. (Read ase documentation for format manipulation.)
  2. Determine which sites to be bonded together. (dummy sites)
  3. Combine them by eliminate the dummy sites.
from ase.visualize import view
from ase.build.molecule import molecule
from combinemols3d.CombineMols3D import combine_2_mols_with_dummy, combine_2_mols

main_mol = molecule('C7NH5')
sub_mol = molecule('BDA')

view(main_mol)
view(sub_mol)

final_mol = combine_2_mols_with_dummy(molecule_1=main_mol.copy(), molecule_2=sub_mol.copy(),
                                      dummy_atom_index_1=11, dummy_atom_index_2=6)
view(final_mol)

If you don’t want to eliminate the dummy sites, use the combine_2_mols instead:

final_mol = combine_2_mols(molecule_1=main_mol, molecule_2=sub_mol,
                                      tgt_atom_1_index=11, tgt_atom_2_index=6)
view(final_mol)

Other parameters are same for the two functions:

  • Conformation related:

    • skin: to get a proper bond length for the new bond. Here we adopt the covalent radii method. A skin parameter is added to finetune the bond length, though it is 0 by default.

      R ( A B ) = r ( A ) + r ( B ) + s k i n R(\mathrm{AB})=r(\mathrm{A})+r(\mathrm{B})+skin R(AB)=r(A)+r(B)+skin

    • cutoff_mult: there are several places to detect neighbors for a specific atom. By default, it is natural cutoff as follow equation:

      D e t e c t R a n g e ( A B ) = r ( A ) + r ( B ) + 0.3 DetectRange(\mathrm{AB})=r(\mathrm{A})+r(\mathrm{B})+0.3 DetectRange(AB)=r(A)+r(B)+0.3

      We can enlarge our search scope to multiply the natural cutoff as follow equation:

      D e t e c t R a n g e ( A B ) = c u t o f f m u l t ∗ ( r ( A ) + r ( B ) + 0.3 ) DetectRange(\mathrm{AB})=cutoff mult*(r(\mathrm{A})+r(\mathrm{B})+0.3) DetectRange(AB)=cutoffmult(r(A)+r(B)+0.3)

  • Performance related:

    • sample_times: how many times to sample addon positions in translation and angles in rotation.
    • rotation_times: In the rotation stage, we will rotate the whole addon molecule as a rigid body. The orientations of the addon site to its neighbors are taken as fixed rotation axes. How many neighbors to be considered could be performance critical.

如果你觉得这段代码有用,请到 github 仓库上给我一个星星:地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/541353.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分享2024高校专业建设思路及建设效果

广东泰迪智能科技股份有限公司成立于2013年&#xff0c;是一家专业从事大数据、人工智能等数据智能技术研发、咨询和培训的高科技企业&#xff0c;公司基于十余年的数据智能产业实践经验&#xff0c;构建“产、岗、课、赛、证、文”融通的特色应用型人才培养模式&#xff0c;助…

MQ:延迟队列

6.1场景&#xff1a; 1.定时发布文章 2.秒杀之后&#xff0c;给30分钟时间进行支付&#xff0c;如果30分钟后&#xff0c;没有支付&#xff0c;订单取消。 3.预约餐厅&#xff0c;提前半个小时发短信通知用户。 A -> 13:00 17:00 16:30 延迟时间&#xff1a; 7*30 * 60 *…

微信营销快捷回复和微信多开-微信UI自动化(.Net)

整理 | 小耕家的喵大仙 出品 | CSDN&#xff08;ID&#xff1a;lichao19897314&#xff09; Q Q | 978124155 关于项目背景和本软件的介绍 因为本人前期基于微信自动化这块编写了一些文章&#xff0c;所以最近想着将文章内容点合并后开发一款真正能帮助别人的软件&#xff0…

AI赋能档案开放审核:实战

关注我们 - 数字罗塞塔计划 - 为进一步推进档案开放审核工作提质增效&#xff0c;结合近几年的业务探索、研究及项目实践&#xff0c;形成了一套较为成熟、高效的AI辅助档案开放审核解决方案&#xff0c;即以“AI人工”的人机协同模式引领档案开放审机制创新&#xff0c;在档…

07.QT信号和槽-2

一、自定义信号和槽 在Qt中&#xff0c;允许⾃定义信号的发送⽅以及接收⽅&#xff0c;即可以⾃定义信号函数和槽函数。但是对于⾃定义的信号函数和槽函数有⼀定的书写规范。 1.基本语法 1.1 自定义信号 &#xff08;1&#xff09;⾃定义信号函数必须写到"signals"…

Windows不常见问题集

● 解决CACLS 禁止修改计算机名 管理员权限运行cmd&#xff1a;cacls %SystemRoot%\System32\netid.dll /grant administrators:f ● Excel 2010 AltTab組合鍵設置 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer&#xff0c;在該路徑建32字元DWO…

YOLOv8使用设备摄像头实时监测

代码如下&#xff1a; from ultralytics import YOLO import cv2 from cv2 import getTickCount, getTickFrequency yoloYOLO(./yolov8n.pt)#摄像头实时检测cap cv2.VideoCapture(0) while cap.isOpened():loop_start getTickCount() #记录循环开始的时间&#xff0c;用于计…

Cesium.js--》探秘Cesium背后的3D模型魔力—加载纽约模型

今天简单实现一个Cesium.js的小Demo&#xff0c;加强自己对Cesium知识的掌握与学习&#xff0c;先简单对这个开源库进行一个简单的介绍吧&#xff01; Cesium 是一个开源的地理空间可视化引擎&#xff0c;用于创建基于 Web 的三维地球应用程序。它允许开发人员在网页上呈现高度…

暴雨孙辉:做好服务器,但更要辟出技术落地之道

稳扎稳打一直是暴雨的风格&#xff0c;这在被访者孙辉的身上尽显。作为暴雨&#xff08;武汉暴雨信息发展有限公司&#xff09;中国区销售及市场副总裁&#xff0c;在谈及公司的技术发展与市场推广走势之时&#xff0c;孙辉沉稳、敏锐且逻辑清晰。 因在服务器领域起步很早&…

C#创建圆形窗体的方法:创建特殊窗体

目录 一、涉及到的知识点 1.OnPaint方法 2.将窗体设置为透明 &#xff08;1&#xff09;Form1的BackColor SystemColors.Control &#xff08;2&#xff09; Form1的背景色是某种颜色&#xff0c;比如BackColor SystemColors.White &#xff08;3&#xff09;加载资源…

TensorRT中的INT 8 优化

INT8 中的稀疏性&#xff1a;加速的训练工作流程和NVIDIA TensorRT 最佳实践 文章目录 INT8 中的稀疏性&#xff1a;加速的训练工作流程和NVIDIA TensorRT 最佳实践结构稀疏量化在 TensorRT 中部署稀疏量化模型的工作流程案例研究&#xff1a;ResNet-34要求第 1 步&#xff1a;…

简单工厂模式大解析:让代码创造更高效、更智能!

个人主页: danci_ &#x1f525;系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;简单工厂模式 简单工厂模式&#x…

传输层协议——UDP/TCP协议

目录 端口号 端口号范围 pidof UDP协议 UDP协议格式 UDP特点 UDP缓冲区 UDP的注意事项 基于UDP的应用层协议 TCP协议 TCP协议格式 序号与确认序号 窗口大小 6个标记位 紧急指针 确认应答机制 连接管理机制 三次握手 四次挥手 超时重传机制 流量控制 滑动…

java使用ShutdownHook优雅地停止服务

在Java程序中可以通过添加关闭钩子&#xff0c;实现在程序退出时关闭资源、平滑退出的功能。 使用Runtime.addShutdownHook(Thread hook)方法&#xff0c;可以注册一个JVM关闭的钩子&#xff0c;这个钩子可 这通常用于确保在应用程序退出时能够执行一些清理工作&#xff0c;比…

KVM + GFS 分布式存储

目录 一、案例分析 1.1、案例概述 1.2、案例前置知识点 1&#xff09;Glusterfs 简介 2&#xff09;Glusterfs 特点 1.3、案例环境 1&#xff09;案例环境 2&#xff09;案例需求 3&#xff09;案例实现思路 二、案例实施 2.1、安装部署 KVM 虚拟化平台 1&…

【Web】DASCTF 2023 0X401七月暑期挑战赛题解

目录 EzFlask MyPicDisk ez_cms ez_py 让俺看看401web题 EzFlask 进来直接给了源码 import uuidfrom flask import Flask, request, session from secret import black_list import jsonapp Flask(__name__) app.secret_key str(uuid.uuid4())def check(data):for i i…

亚远景科技-ASPICE 4.0-HWE硬件过程的范围 The Technical Scope of HW process

ASPICE 4.0中的HWE process是电气和电子硬件的技术范畴&#xff0c;涵盖了硬件工程中的需求分析、设计和验证活动&#xff0c;但不包括以下活动&#xff1a; 1. 系统级工程过程。既不包括机电一体MECHATRONIC&#xff0c;也不包括ECU特定电子控制单元的开发。 2. 硬件采购过程…

叉车载货出入库AI检测算法介绍及应用

随着物流行业的快速发展&#xff0c;叉车作为物流运输的重要设备&#xff0c;其安全性和效率性越来越受到人们的关注。然而&#xff0c;在实际操作中&#xff0c;由于人为因素和操作环境的复杂性&#xff0c;叉车事故时有发生&#xff0c;给企业和个人带来了巨大的损失。为了提…

部署Kafka集群图文详细步骤

1 集群规划 共三台虚拟机同处overlay网段&#xff0c;每台虚拟机部署一套kafka和zookeeper&#xff0c;kafka_manager安装其中一台虚拟机上即可。 HostnameIP addrPortListenerzk1docker-swarm分配2183:2181zk2docker-swarm分配2184:2181zk3docker-swarm分配2185:2181k1docke…

word从零基础到高手【办公】

第1课 - word基础操作快速入门第2课 - 让你效率10倍提升的快捷操作第3课 - word排版快速入门第4课 - 排版实战案例讲解第5课 - 搞定论文排版全过程第6课 - 让你的word更强大的神技第7课 - 提高工作效率必备的批量操作 资料截图如下: 发送: "word办公" 获取提取码