知乎链接:
https://zhuanlan.zhihu.com/p/477136180
前几天有需求要绘制一种势能面的示意图,类似教科书上标出一阶鞍点
、 局域极小点
那种示意图。
这种图正规绘制需要大量的单点计算,并用软件描面画成。但是实际上,我无法计算出如此多的单点来绘制一张图,毕竟在一般的计算有机工作中,单是定位过渡态就够普通鼠标侠喝一壶了,更别说选CV扫描并绘图了。时间更不允许,当我需要画图时,往往意味着科研需要的数据已经满足需求了,我再要求提供给我更多数据也不大可能得到。
因此只能利用现用的计算数据合理规划着绘制,也就是说我只能依据目前算出的几个结构的数据,或者IRC上的点进行绘制。
经朋友启发,组织了一个简易的流程,来画这种简易的示意图。
Blender是一款开源的建模软件:
Blender is a free and open-source 3D computer graphics software toolset used for creating animated films, visual effects, art, 3D printed models, motion graphics, interactive 3D applications, virtual reality, and computer games. Blender's features include 3D modelling, UV unwrapping, texturing, raster graphics editing, rigging and skinning, fluid and smoke simulation, particle simulation, soft body simulation, sculpting, animating, match moving, rendering, motion graphics, video editing, and compositing.
4. 点击上图中的 物体模式
改为编辑模式
随后左边多出一竖列按钮,鼠标移到上面会显示名称,点击环切
同时编辑模式
字样下方出现切割次数
,建议改为10
或者8
,9
5. 鼠标移到添加的平面上,会出现一根黄线,点击,垂直各切一次
6. 鼠标左键长按该列第一个按钮,选中刷选
7. 平面上切出的格子点上按住鼠标移动,选中
8. 按 G
便可以自由移动选中的点,建议再按Z
限制其只改变Z坐标,按住鼠标中键可改变视角。
(当然,我们可以使用调整
,直接捏出想要的形状,但是我们目的不是在Blender中捏出,只是利用其捏出基本形貌,后利用坐标在常用的科研作图软件中插值制作出符合审美的示意图,所以我建议使用刷选
并只改变Z值,你也可以衰减编辑
, 但我觉得操作单个点舒服,而且只变Z值的话,xy坐标均匀,后续代码拟合曲面效果会好)
这些点应该严格按照已有的数据绘制出相对高低,峰、谷、鞍面合理处理位置。
9. 使用鼠标拖动,竖直改变对象的形貌,大致捏出基本形貌,该低的地方低,该高的地方高,哪儿是鞍点那儿就捏成鞍部。
比如我可以随手捏出这么一个ts
连接两个minimum
的示意图,看起来比较丑陋
捏平面
物体模式
,鼠标拖拉选中势能草面仅导出选中的物体
几何数据只选三角面
很幸运,.obj
是文本可以进行文本解析,也可以用windows自带的3D查看器
打开3D查看器
3. 提取坐标
打开git bash然后输入grep "v " surface.obj |awk '{print
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import make_interp_spline
from sklearn.kernel_ridge import KernelRidge
from scipy import interpolate
with open("surface.xyz","r") as f:
s = f.read()
xyz = s.splitlines()
x = []
y = []
z = []
for inf in range(len(xyz)):
x.append(float(xyz[inf].split()[0]))
y.append(float(xyz[inf].split()[1]))
z.append(float(xyz[inf].split()[2]))
print(max(x),min(x),max(y),min(y),max(z),min(z))
dim = int(np.sqrt(len(xyz)))
X = np.asarray(x).reshape([dim, dim])
Y = np.asarray(y).reshape([dim, dim])
Z = np.asarray(z).reshape([dim, dim])
#fig = plt.figure()
#ax = plt.axes(projection='3d')
#plt.axis('off')
#ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1, cmap = plt.get_cmap('gnuplot'))
#plot.show()
x_0 = np.linspace(min(x),max(x),200)
y_0 = np.linspace(min(y),max(y),200)
X_0, Y_0 = np.meshgrid(x_0, y_0,indexing='ij')
f = interpolate.Rbf(X, Y, Z, function='multiquadric',smooth=0.3)
Z_1 = f(X_0, Y_0)
Z_1.shape
fig = plt.figure()
ax = plt.axes(projection='3d')
plt.axis('off')
ax.plot_surface(X_0, Y_0, Z_1, rstride = 1, cstride = 1, color="#cccccc")
plt.show()
在被注释的 plt.show()
那里会展示最初捏出的草图形貌,不过在Blender里就能看到它就不需要matplotlib
渲染了
代码默认生成200x200的图,使用Rbf来拟合曲面。一切都可以改。代码就在上面
也可以保存坐标使用MatLab、Origin等专业软件进一步处理。
刚才随意搓的示意图感觉还凑合,还是有些别扭,可能需要再调一调。
刚才的示意图
之前画的几个图:
首先是捏的草图
最后调cmap = plt.get_cmap('gnuplot')
着色的示意图