利用PyMOL制作反应势能面示意动画

类别:    标签: 3d pymol python   阅读次数:   版权: (CC) BY-NC-SA

利用PyMOL内置的OpenGL制作了势能面反应的示意图, 具体制作过程容后再讲.

PyMOL是基于Python的分子可视化软件, 在结构生物学中使用十分广泛. 它的主要特色是对生物大分子显示效果好, 并自带一个高效的光线追踪渲染器, 能渲染出逼真的效果. 此外, PyMOL还支持脚本, 可用于精确控制显示, 并提供了一个基本的OpenGL的接口, 以便用户进行一些简单的三维动画设计.

三维建模软件有很多, 不同的软件适用于不同的领域. 最基础的如OpenGL之类, 需要你利用最基本的图元和场景选项一点一点地构建出整个场景. POV-Ray之类则侧重于光线追踪渲染, 并集成了许多可用的模型与场景, 更高级一些. Blender之类则是更通用更专业的三维建模软件, 并非专用于分子建模与可视化. 这样看起来, PyMOL算是处于OpenGL和Blender中间, 集成了POV-Ray的部分功能, 专门用于分子可视化的. 建模时它可以实时可视化, 调试很分方便, 因此对于一些简单的应用很合适.

PyMOL中, 三维模型被称作CGO(Compiled Graphics Objects), 可在其中引用一些OpenGL基本图元. 使用方法和OpenGL很类似, 但由于调用是基于Python的, 所以比直接使用OpenGL简单一些.

下面说说上面两个动画的制作方法.

首先我们需要一个二维势能面的模型. 这个模型最好能够具有一般势能面的特点, 并且包含各类驻点, 如极大点, 极小点, 鞍点. 这就要求势能面上至少有两个极大点. 经过比较, 我发现二元函数 $F(x,y)={\sin x \over x}+{\sin y \over y}$ 满足要求. 但此函数为周期函数, 所以最好加上一个线性项去掉周期性, 并适当调整大小. 最终我用的函数是 $F(x,y)=4({\sin x \over x}+{\sin y \over y})+0.1x$.

确定了势能函数的解析式就可以创建势能曲面了. 方法是最基本的剖分, 用三角面片对整个区间进行剖分, 再将剖分所得的三角面片组合起来. 值得注意的是剖分的方向和三角面片的法向.

至于曲面上球体运动的模拟, 可利用简单的线性步长方法. 若需要更加真实的效果, 则可利用分子动力学中的Verlet积分方法进行计算.

使用方法

  1. 运行代码: run PESdemo.py
  2. 背景设为白色: bg white
  3. 设定光线追踪: set ray_trace_frames=1
  4. 输出png图片: mpng PESdemo

渲染好的图片将输出为PESdemoXXXX.png, XXXX为编号. 利用这些图片就可以制作成动画.

支持动画的图片格式目前主要两种:

下面两个图就是这两种格式的对比

更具体的信息可参考下面的网文:

  1. 小牛犊APNG力挫老古董MNG
  2. APNG编辑制作工具

如果你要使用其他颜色映射方案的话, 可参考我的另一篇博文几种颜色映射方案的解析式.

代码

PESdemo.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# coding: utf-8

# ##############################################################################
# 2013-11-14 10:25:11 简单示例
# 2016-09-07 20:23:18 注释, 颜色映射
# ##############################################################################

import math
from pymol import cmd
from pymol.cgo import *

# 势能面函数
def Fxy(x,y):
	if x==0: Fx = 4
	else: Fx = 4*math.sin(x)/x+.1*x
	if y==0: Fy = 4
	else: Fy = 4*math.sin(y)/y
	return Fx+Fy

# 势能面函数法向
def dFxy(x,y):
	if x==0: dFx = 0
	else: dFx = 4*(x*math.cos(x)-math.sin(x))/x**2+0.1
	if y==0: dFy = 0
	else: dFy = 4*(y*math.cos(y)-math.sin(y))/y**2
	Rtmp = 1/math.sqrt(dFx*dFx+dFy*dFy+1)
	return -dFx*Rtmp, -dFy*Rtmp, Rtmp

def RGB(V, Vmin, Vmax):
	dV=Vmax-Vmin; x=(V-Vmin)/dV
	r=1; g=1; b=1
	if   x<0.25: r = 0;     g = 4*x
	elif x<0.50: r = 0;     b = 2-4*x
	elif x<0.75: r = 4*x-2; b = 0
	else:        g = 4-4*x; b = 0

	r=min(max(r,0), 1)
	g=min(max(g,0) ,1)
	b=min(max(b,0) ,1)
	return r, g, b

# 是否使用法向, 显示网格, 颜色映射, 使用Verlet方法计算轨迹
YesNorm=1; YesGrid=1; YesMap=0; YesTrj=1

Xini = 4.5 # 小球初始位置
Xmin = -7; Xmax = 5.5; dX = .5; Nx = int((Xmax-Xmin)/dX)
Ymin = -7; Ymax = 7;   dY = .5; Ny = int((Ymax-Ymin)/dY)

X = [ 0 for i in range(Nx) ]
Y = [ 0 for j in range(Ny) ]
Z = [ [0]*Ny for i in range(Nx) ]
Zx= [ [0]*Ny for i in range(Nx) ]
Zy= [ [0]*Ny for i in range(Nx) ]
Zz= [ [1]*Ny for i in range(Nx) ]

for i in range(Nx): X[i] = Xmin+dX*i
for j in range(Ny): Y[j] = Ymin+dY*j

for i in range(Nx):
	x=X[i]
	for j in range(Ny):
		y=Y[j]
		Z[i][j] = Fxy(x,y)
		if YesNorm: Zx[i][j], Zy[i][j], Zz[i][j] = dFxy(x,y)

# 获取极值, 用于颜色映射
Zmin=min(min(Z)); Zmax=max(max(Z))

PES = []
# 绘制网格
if YesGrid:
	PES.extend( [ COLOR,  0, 0, 0 ] )
	for i in range(Nx):
		PES.extend( [ BEGIN, LINE_STRIP ] )
		for j in range(0,Ny):
			PES.extend( [ NORMAL, Zx[i][j], Zy[i][j], Zz[i][j] ] )
			PES.extend( [ VERTEX, X[i], Y[j], Z[i][j] ] )
		PES.append( END )

	for j in range(Ny):
		PES.extend( [ BEGIN, LINE_STRIP ] )
		for i in range(Nx):
			PES.extend( [ NORMAL, Zx[i][j], Zy[i][j], Zz[i][j] ] )
			PES.extend( [ VERTEX, X[i], Y[j], Z[i][j] ] )
		PES.append( END )

# 绘制表面
PES.extend( [ COLOR, .19, .6, .83 ] )
for j in range(Ny-1):
	PES.extend( [ BEGIN, TRIANGLE_STRIP ] )
	for i in range(Nx):
		if YesMap:
			r, g, b=RGB(Z[i][j+1], Zmin, Zmax)
			PES.extend( [ COLOR,  r, g, b ] )
		PES.extend( [ NORMAL, Zx[i][j+1], Zy[i][j+1], Zz[i][j+1] ] )
		PES.extend( [ VERTEX, X[i], Y[j+1], Z[i][j+1] ] )
		if YesMap:
			r, g, b=RGB(Z[i][j], Zmin, Zmax)
			PES.extend( [ COLOR,  r, g, b ] )
		PES.extend( [ NORMAL, Zx[i][j], Zy[i][j], Zz[i][j] ] )
		PES.extend( [ VERTEX, X[i], Y[j], Z[i][j] ] )
	PES.append( END )

# 绘制路径
PES.extend( [ LINEWIDTH, 5 ] )
PES.extend( [ BEGIN, LINE_STRIP ] )
PES.extend( [ COLOR, 1., 0., .0 ] )
x = -Xini; y = -Xini
while y<=Xini:
	z = Fxy(x,y)
	dFx, dFy, dFz = dFxy(x,y)
	PES.extend( [ NORMAL, dFx, dFy, dFz ] )
	PES.extend( [ VERTEX, x, y, z ] )
	y = y+0.1
PES.append( END )

PES.extend( [ BEGIN, LINE_STRIP] )
PES.extend( [ COLOR, 0, 1., .0 ] )
x = -Xini; y = -Xini
while x<=Xini:
	z = Fxy(x,y)
	dFx, dFy, dFz = dFxy(x,y)
	PES.extend( [ NORMAL, dFx, dFy, dFz ] )
	PES.extend( [ VERTEX, x, y, z ] )
	x = x+0.1
PES.append( END )

# 绘制小球
x = -Xini; y = -Xini; z = Fxy(x,y)
PES.extend ( [ COLOR, 1, 1, 0 ] )
PES.extend ( [ SPHERE, x, y, z, .2 ] )
x = -Xini; y = Xini; z = Fxy(x,y)
PES.extend ( [ COLOR, 1, 0, 0 ] )
PES.extend ( [ SPHERE, x, y, z, .2 ] )
x = Xini; y = -Xini; z = Fxy(x,y)
PES.extend ( [ COLOR, 0, 1, 0 ] )
PES.extend ( [ SPHERE, x, y, z, .2 ] )

cmd.load_cgo(PES, 'PES')

# 模拟小球运行
Rsph = 0.5
if not YesTrj:
	x = -Xini
	for Ifrm in range(30):
		y = -Xini+Ifrm*0.3; z = Fxy(x,y)
		dFx, dFy, dFz = dFxy(x,y)
		SYS = [ COLOR, 1, 1-Ifrm/30., 0 ]
		SYS.extend( [ SPHERE, x+Rsph*dFx, y+Rsph*dFy, z+Rsph*dFz, Rsph ] )
		cmd.load_cgo(SYS, 'SYS', Ifrm)

	y = -Xini
	for Ifrm in range(30):
		x = -Xini+Ifrm*0.3; z = Fxy(x,y)
		dFx, dFy, dFz = dFxy(x,y)
		SYS = [ COLOR, 1-Ifrm/30., 1, 0 ]
		SYS.extend( [ SPHERE, x+Rsph*dFx, y+Rsph*dFy, z+Rsph*dFz, Rsph ] )
		cmd.load_cgo(SYS, 'SYS', Ifrm+30)
else:
	# 初始位置和速度
	x = -Xini; y=-Xini
	vx = 3.28; vy = 0;    Nfrm=130
	vx = 0;    vy = 3.15; Nfrm=120

	dt = 0.05; E0 = 0.5*(vx*vx+vy*vy)+Fxy(x,y)
	dFx0, dFy0, dFz0 = dFxy(x,y)
	for Ifrm in range(Nfrm):
		SYS = [ COLOR, 1, 1, 1 ]
		x = x + (vx + 0.5*dFx0*dt)*dt;
		y = y + (vy + 0.5*dFy0*dt)*dt;
		z = Fxy(x,y)
		dFx, dFy, dFz = dFxy(x,y)
		SYS.extend( [ SPHERE, x+Rsph*dFx, y+Rsph*dFy, z+Rsph*dFz, Rsph ] )
		cmd.load_cgo(SYS, 'SYS', Ifrm)

		vx = vx+0.5*(dFx0+dFx)*dt
		vy = vy+0.5*(dFy0+dFy)*dt
		E = 0.5*(vx*vx+vy*vy)
		Rtmp = math.sqrt(2.*abs(E0-z)/(vx*vx+vy*vy))
		vx = vx*Rtmp; vy = vy*Rtmp
		dFx0, dFy0 = dFx, dFy

cmd.reset()
cmd.zoom('PES', 1.0)
cmd.clip('far', -10.0)
cmd.turn('z', 30)
cmd.turn('x', -60)
cmd.mplay()
随意赞赏

微信

支付宝
◆本文地址: , 转载请注明◆
◆评论问题: https://jerkwin.herokuapp.com/category/3/博客, 欢迎留言◆


前一篇: 空间中两随机向量间夹角的概率密度分布
后一篇: 【转】那些小女孩的故事

访问人次(2015年7月 9日起): | 最后更新: 2017-09-26 07:50:25 UTC | 版权所有 © 2008 - 2017 Jerkwin