Expand description
自穿检测 —— 链有没有从环里穿过去、两根键有没有交叉。
§为什么非有这一条不可
精修阶段有个决定:力场里放全部 N² 对原子,而不是像 RDKit 那样只放
u − l ≤ 5.0 的对。理由是被滤掉的那些(实测占 16.8%,柔性大分子上最高 51%)
正是拓扑上远、几何上可能撞在一起的对 —— 也就是自穿会发生的地方。
但那个改动的目的一直没有对应的判据。 收益证明不了,也防不住后续改动把它 弄丢。判据一(越界)看不见自穿:链穿过环时,每一对原子的距离都可以完全合法 —— 穿过去的那根键与环上的键不共享原子,它们的距离约束只有一条很松的 vdW 下限, 而“穿过“与“贴着“在两两距离上几乎没有区别。
所以要直接量几何:线段与线段、线段与环面。
§两个量
- 键-键穿插:两根互相够远的键(不共享原子,且拓扑距离 >
RIGID_TOPO), 算两条线段的最短距离。贴得太近就是穿了 —— 阈值不是拍的,见CROSS_TOL。 - 环穿刺:每个环按质心扇形三角化,数有多少条键的线段穿过任一三角形。
§环穿刺数的是交点的奇偶,不是“有没有交点“
扇形面片合起来是一张以环为边界的曲面,线段与它交奇数次才是从一侧走到
了另一侧。头一版用 .any(),数的是“有没有交点“ —— 环非凸时会假阳性:
质心可能落在环外,扇形面片于是把凹口也铺上了,链从凹口穿过 z 平面会与内外
两片各交一次,相交 2 次、mod-2 环绕数为 0(拓扑上没穿过去),.any()
照样报真。
判据 月牙环的凹口不算穿刺 钉的就是这个:18 元月牙环(外弧 r=4、内弧
r=2.6,θ∈[−150°,150°],18 个顶点的质心落在 r≈0.22 的空洞里、也就是环外),
链在半径 0.9 处竖直穿过。改回 .any() 它当场红。
这个改动在当前语料上一条都没改:feasibility(large.smi 与
hard.smi)、threading_oracle(smoke.bounds.jsonl)三份输出逐字节相同,
conformer_oracle 除耗时那一行外也相同。语料里的环都小到基本是凸的,
真正会踩的是柔性大环和精修之前的嵌入坐标 —— 所以覆盖全靠那条手写判据,
差分语料在这件事上是空的。
两个都是确定的,复杂度 O(键²),56 原子的分子上几千次运算,可忽略。
Structs§
- Threading
- 一个构型的自穿账。
Constants§
- CROSS_
TOL - 两根不共享原子的键靠到多近就算穿插(Å)。
- RIGID_
TOPO - 两根键的原子集之间,拓扑距离小到多少就不再算作可能互穿。
Functions§
- detect
- 数一个构型里的自穿。
- segment_
distance - 两条线段之间的最短距离。
- segment_
hits_ triangle - 线段有没有穿过三角形(Möller–Trumbore,带线段参数的夹紧)。