核苷酸化学结构可视化技术解析

本文详细介绍了使用RDKit化学信息学库生成核苷酸化学结构图像的技术方案,包括分子图构建、单体连接算法、图像旋转优化和前端交互实现,为生物技术软件平台提供化学结构可视化功能。

核苷酸化学结构的可视化指南

帕西西兰(首个获得FDA批准的siRNA药物!)的乘客链,工具提示显示其中一个修饰核苷酸的化学结构。

引言

Benchling的使命是释放生物技术的威力。Benchling提供了一个被20万+科学家使用的平台,用于集中数据、改进协作和洞察,并加速科学发现的进程。今年夏天,我作为软件工程实习生加入了Benchling的修饰生物制剂和化学团队。该团队的目标之一是让Benchling具备化学感知能力,包括向DNA和RNA序列添加化学修饰,并在Benchling上创建新的小分子实体类型。

在我的实习项目中,我致力于让我们的产品对化学家更加友好。我们的一些客户希望能够可视化他们正在处理的物质的实际化学结构,因为他们通常无法通过分子名称或其化学结构的字符串表示来识别分子。通过可视化的化学结构,他们可以更轻松地识别和设计分子。

为了满足化学家的需求,我们希望我们的产品能够展示化学结构图像。这将使化学家能够更轻松地在Benchling中工作,而以前他们不得不使用多种工具来设计单体、核苷酸或序列。在我的项目中,我构建了一个功能,用于展示DNA和RNA序列中单体和核苷酸的化学结构。

✨ 我们最终的交互式、连接、方向一致的核苷酸图像! ✨

在深入探讨我为DNA和RNA序列生成化学结构图像的技术细节之前,这里有一些关于修饰生物制剂的背景知识。

背景

什么是核苷酸?

核苷酸是由碱基、糖和磷酸单体连接在一起组成的分子。DNA和RNA是由许多核苷酸键合在一起的长链。

在Benchling中,单体是具有连接点的分子,可以与其他单体键合。我喜欢将单体视为拼图碎片,将连接点视为决定它们可以在哪里连接的凸起。在它们特定的连接点将单体连接在一起就创建了一个核苷酸。

一个核苷酸(如这个单磷酸鸟苷)由糖、碱基和磷酸组成。

从工程的角度来看,单体可以与相同组分类型(糖、碱基或磷酸)的其他单体互换。Benchling优雅地将单体建模为具有化学结构和单体类型的实体;将核苷酸建模为由每种类型的一个单体组成的实体。

尝试交换单体!

SMILES 🙂

SMILES(简化分子线性输入系统)是一种基于字符串的表示法,表示分子的化学结构。该表示法可以编码原子标签和立体化学,但不能编码分子的2D旋转。

环丙沙星的化学结构和SMILES规范(修改自 https://commons.wikimedia.org/wiki/File:SMILES.png

单体的SMILES还通过用R基团编号标记一组原子来指定它们的连接点。Benchling中三种不同类型的单体具有不同数量的连接点。糖单体具有R1、R2和R3。碱基只有R1,而磷酸具有R1和R2。

三个天然单体的SMILES和结构,其封端基团被高亮显示。

连接点的封端基团是单体中的官能团(几乎总是H或OH),如果没有其他单体连接到该连接点,则使用该基团。在上图中,糖的标记为R3的OH是糖的R3封端基团,碱基的标记为R1的H是其R1封端基团。当形成核苷酸时,我们用单体之间的单一化学键替换这些封端基团。

生成核苷酸图像的算法

我们使用RDKit,一个开源化学信息学库,来渲染化学结构图像。

对于单个核苷酸,碱基的R1连接到糖的R3,磷酸的R1连接到糖的R2。如果我们想将多个核苷酸连接在一起,我们会将第一个核苷酸的磷酸的R2连接到下一个核苷酸的糖的R1。

碱基、糖和磷酸单体,其连接点被标记,R基团被高亮显示。

将单体连接在一起需要一些简单的图操作:

1. 从SMILES创建单体分子图

第一步是从单体的SMILES字符串创建分子图,并标记单体的连接点。RDKit将分子表示为图数据结构,其中原子是顶点,键是边。

我们使用RDKit将每个单体的SMILES转换为分子图。RDKit还允许我们通过获取它们分子图的不相交并集将两个分子组合成一个不连通的图。

我们的下一步是通过在其不连通的组件之间添加一条边来使核苷成为一个连通图。

2. 连接核苷(碱基 + 糖)

核苷是碱基和糖键合在一起形成的。要创建核苷,我们必须用单体之间的单一键替换碱基R1和糖R3连接点上的封端基团。

根据定义,单体封端基团通过单键连接到单体的其余部分。由于恰好有一个键(边)连接到封端基团,我们知道连接点是封端基团原子的唯一相邻原子(顶点)。

将三个单体连接成核苷酸的阶段。左:所有三个单体,碱基的R1和糖的R3封端基团已移除。中:核苷,R2封端基团和磷酸R1封端基团已移除。右:完全连接的核苷酸。

如果封端基团包含不止一个原子怎么办?

如果封端基团是羟基(OH),我们怎么知道要移除两个原子中的哪一个?像其他化学信息学库一样,RDKit使用氢抑制,意味着像[OH:2]或[NH2:3]中的H这样的隐式氢不包含在分子图中。该库在渲染时可以确定性地计算连接到一个原子的氢的数量。从代码的角度来看,封端基团原子只是氧。

此外,SMILES规范不允许具有相同原子标签的多个原子,保证最多只有一个原子被标记为任何R基团编号。例如,写[H:1]和[CH:3]是合法的,它们只有一个非抑制原子。但标记具有多个非H原子的组分如[CC:2]或[C(=O)OH:1]是非法的。这个约束是可以接受的,因为在实践中,我们客户的单体只有H和OH封端基团。

如果氢被抑制了,你怎么找到单独的氢封端基团?

RDKit不抑制显式H。当我们在单体的SMILES字符串中包含一个标记的氢原子[H:1]时,RDKit确实将该氢包含在分子图中。

3. 连接核苷酸(核苷 + 磷酸)

最后,我们用相同的过程将磷酸连接到核苷。我们移除磷酸R1和糖R2封端基团,并在它们之间添加一个单键。

处理边缘情况

这个过程对于具有所有三个单体的核苷酸效果很好,但在一些特殊情况下,核苷酸并不具有所有三个单体。

简并碱基

是生物学家表示具有多个碱基可能性(A/T/G/C)的碱基的方式,这些碱基可能是多种中的一种,而不是正常的A/T/G/C。由于简并碱基没有单一的结构,我们干脆不渲染碱基。

空末端磷酸

是指序列中最后一个核苷酸没有连接的磷酸。大多数寡核苷酸的生物用途不包括末端3’或5’磷酸。具有末端磷酸的序列要求科学家特别向他们的寡核苷酸制造商请求磷酸化寡核苷酸。

Benchling拥有各种具有不同科学需求的客户。我们的目标是尽可能灵活地支持他们。当渲染缺少单体的核苷酸时,我们显示部分渲染的化学结构以及结构不完整的消息。

具有空末端磷酸的化学结构图像。

旋转核苷酸

默认情况下,RDKit定向生成的核苷酸以最大化图像的纵横比。根据单体的大小和糖的键角,磷酸可能在碱基的右侧或左侧。这使得科学家更难直接比较核苷酸结构,因为他们必须在脑海中旋转整个图像。

在下表中,三个核苷酸除了它们的碱基单体外是相同的。在第二行中更容易发现差异,那里的核苷酸以一致的方向排列。

每行具有相同的核苷酸——只有一个单体被改变。当核苷酸具有一致的方向时,更容易找到差异。尝试改变顶行中单体的旋转!

你为什么选择以这种方式定向它?

一致性很重要,但特定的方向只是惯例。我们查看了文献中核苷酸的描绘,以了解科学家习惯的方式。大多数教科书将3’磷酸定位在碱基的左侧,5’末端磷酸在右侧。Benchling使用3’磷酸建模核苷酸,因此我们将它们显示在左侧。

一个教科书级别的核苷酸示例。

三角学 🤝 化学

有趣的事实:这个实习项目是我自高中以来第一次使用反正切。

1. 寻找质心

寻找每个单体的质心。

我们的第一步是找到每个单体的质心(算术平均值)。幸运的是,RDKit有方法可以找到分子图中每个原子的(x, y)坐标。由于我们跟踪每个原子最初属于哪个单体,我们可以在连接核苷酸的图中找到每个单体的质心。

请注意,单体的视觉质心与其物理质心无关,因为(1)这只是3D结构的2D表示,并且(2)分子图不包括氢。

2. 寻找碱基和磷酸向量之间的逆时针角度

向量B和P之间的最小角度是叉积的大小除以向量的点积的反正切。我们需要逆时针角度,而不是最短的,所以如果角度为负,我们向角度加360°。

然后,我们找到相对于+x轴的“半角”。

左:从碱基向量到磷酸向量的逆时针角度。右:红色的半角。

3. 将半角向上旋转

旋转图像使半角指向上方(90°)确保磷酸在碱基的左侧。

左:核苷酸旋转使半角指向上方。右:最终旋转的核苷酸。

我们将旋转角度(以度为单位)传递给RDKit的分子绘图函数,该函数正确处理原子标签的旋转。然后我们将生成的SVG(包括着色的单体高亮!)发送到前端。

在前端渲染化学结构

Benchling在所有前端代码中使用React。我们制作了一些有趣的组件来渲染交互式结构。

自动SVG调整大小

当渲染化学结构SVG时,RDKit要求客户端指定生成图像的宽度和高度(自动调整大小在RDKit的后续版本中添加)。生成图像时,我们设置width=height=1000,这对于非正方形纵横比的结构效果不佳。

自动调整大小最大化了我们可用空间的使用。蓝色边框是SVG的边界框。

我们通过创建一个React钩子解决了纵横比问题,该钩子迭代<svg>的子元素,并将<svg>的viewBox属性设置为包含所有子元素BBox的最小矩形。在计算几何中,这是最小边界矩形问题!

交互式悬停高亮

我实习项目中另一个有趣的部分是当用户悬停在核苷酸图像上时高亮单体的子结构。这是<NucleotideImage>组件的过程:

  • 使用React组件<ChemicalStructureImage>渲染着色的核苷酸SVG。
  • 使用D3选择<ChemicalStructureImage><svg>中所有着色的路径和椭圆元素。
  • 使用颜色→单体类型的映射将选定的元素绑定到它们的单体类型。
  • 对于每个选定的元素,将它们的颜色设置为灰色,不透明度设置为0。
  • 在每个绑定的元素上添加mouseover/mouseout事件监听器。当悬停在一个元素上时,它也将每个具有相同单体类型的其他元素设置为不透明。

为了性能,我们记忆化<ChemicalStructureImage>,以便每个核苷酸图像只修改DOM一次。由于<svg>被记忆化,<NucleotideImage>将在每次<NucleotideImage>重新渲染时在同一个<svg>上运行回调。这种情况非常频繁地发生——每次用户鼠标悬停在核苷酸组件上时。我们的useCallback钩子必须是幂等的,这样它就不会通过多次修改其内容来破坏<svg>,或者由于陈旧的事件监听器导致内存泄漏。

性能

RDKit代码相当复杂,所以我担心我的实习项目会太慢。我们考虑了几种提高性能的替代方案,包括后端缓存、前端的RDKitJS和工具提示防抖。但事实证明,性能已经非常好了!

显示网络请求每个主要组件所花费时间的简化火焰图。

从客户端的角度来看,获取核苷酸图像的整个浏览器请求非常快,大约90-180毫秒(第25-75百分位)。多亏了RDKit,生成实际的SVG只需要8毫秒!

总结

在整个实习期间,我有机会学习许多新技术,其中许多与我在Benchling期间想学习的内容一致。我进入这次实习时拥有更多前端开发经验,并表示有兴趣了解更多关于SQLAlchemy和GraphQL的知识,这是两种我没有经验的技术。我还深入研究了RDKit,并发现与开源软件进行这种程度的合作是有益的。最后,在整个Benchling实习期间,我得以跨多个工程垂直领域工作,包括全栈开发、调查端到端性能以及批判性地思考分析。

我们正在招聘

如果你有兴趣与我们合作构建安全的生物技术平台的未来,请查看我们的招聘页面或联系我们!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计