前言
最近在寫文章需要繪制一些一維的能量曲線(energy profile)和抽象的二維和三維的網(wǎng)格來表示晶體用來描述自己的算法,于是自己在之前的腳本的基礎(chǔ)上進(jìn)行了整改寫成了只提供接口的Python庫,基本思想就是封裝了matplotlib中相關(guān)接口,方便快速搭建和定制自己的能量曲線和網(wǎng)格結(jié)構(gòu), 代碼托管在GitHub上并上傳至PyPI。對于研究晶體材料的同學(xué)如果想通過python來繪制簡單的晶格圖像可以參考一下。
GitHub地址:https://github.com/PytLab/catplot/
PyPI地址:https://pypi.python.org/pypi/catplot/
正文
首先還是介紹一下這個程序的用途,目前主要是提供三個主要的模塊來繪制三方面的內(nèi)容:
1. 繪制抽象的二維網(wǎng)格結(jié)構(gòu)
catplot提供了豐富的接口用來定制所需要的任何二維網(wǎng)格并進(jìn)行周期性擴展,如下圖是一個通過當(dāng)個重復(fù)單元擴展出來的抽象(100)晶面的二維網(wǎng)格結(jié)構(gòu):
?

2. 繪制抽象的三維網(wǎng)格結(jié)構(gòu)
同理只不過這次是在三維畫布中進(jìn)行繪制并進(jìn)行重復(fù)單元的周期性擴展,擴展的效果如下圖:
?

3. 通過插值算法實現(xiàn)繪制”順滑”的energy profile
?

實現(xiàn)過程基本是通過對matplotlib提供的繪圖組件和接口進(jìn)一步封裝成可以快速搭建上面三個類型圖像的組件。
采用二次插值結(jié)合樣條插值方法繪制 energy profile
energy profile可以理解成在勢能面(Potential Energy Surface)上沿著某個特定的方向(反應(yīng)坐標(biāo)方向)上能量的變化,
下面我就上一個簡單的例子來畫一條順滑的energy profile, 更多具體的例子我都已經(jīng)jupyter notebook的形式放在的github上(https://github.com/PytLab/catplot/tree/master/examples)
# 從catplot中導(dǎo)入繪制所需的組件: 畫布 和 線
from catplot.ep_components.ep_canvas import EPCanvas
from catplot.ep_components.ep_lines import ElementaryLine
# 創(chuàng)建一個用于繪制energy profile的畫布
canvas = EPCanvas()
# 創(chuàng)建一條能量曲線,提供的三個值分別是三個狀態(tài)下的能量數(shù)值
line = ElementaryLine([0.0, 1.2, 0.8])
# 將這條線添加到畫布中
canvas.add_line(line)
# 繪制
canvas.draw()
canvas.figure.show()
插值方法
為了能將能量最高點沿著橫坐標(biāo)任意位置移動,我先將頂點的兩邊用二次函數(shù)進(jìn)行插值,獲取兩個不同的二次函數(shù)形式,然后根據(jù)二次函數(shù)的形式在左右兩邊插上5個點,為了能讓分開插值的兩部分看起來連續(xù),在將上面的10個新插的點和之前的3個點進(jìn)行一次spline插值即可。
# 頂點兩側(cè)進(jìn)行二次插值的算法
def quadratic_connect_interp(x1, y1, x2, y2):
????A = np.matrix([[x1**2, x1, 1],
?????????????????? [x2**2, x2, 1],
?????????????????? [2*x2, 1, 0]])
????b = np.matrix([[y1], [y2], [0]])
????x = A.I * b
????x.shape = (1, -1)
????a, b, c = x.tolist()[0]
????poly_func = lambda x: a*x**2 + b*x + c
????return poly_func
?

與插值相關(guān)的方法參考:https://github.com/PytLab/catplot/blob/master/catplot/interpolate.py
豐富的接口
除了上面最簡單的例子,catplot還提供了豐富的接口來定制和操作energy profile,比如拼接,合并,平移,添加陰影、改變顏色, 輔助線, 修改畫布大小,導(dǎo)出插值數(shù)據(jù)等等。具體的例子參考:https://github.com/PytLab/catplot/tree/master/examples
?

繪制二維和三維抽象網(wǎng)格
晶格中的原子和鍵在catplot中被抽象成圖中的node和edge,這樣我們就可以通過創(chuàng)建圖中的node和edge的方式搭建我們網(wǎng)格的重復(fù)單元,之后可以通過重復(fù)單元的擴展方法來將其擴展成nxn或者nxnxn的網(wǎng)格。
實現(xiàn)的基本方法就是通過matplotlib提供的Line2D, Arrow和scatter相關(guān)的接口來將相應(yīng)node和edge的數(shù)據(jù)添加到maptlotlib的二維或者三維畫布中然后進(jìn)行繪制和顯示。下面給分別給出兩個繪制正交網(wǎng)格的繪制方法:
繪制5×5的二維網(wǎng)格
notebook版可以參見:https://github.com/PytLab/catplot/blob/master/examples/grid_2d_examples/expand_supercell.ipynb
創(chuàng)建nodes和edges
from catplot.grid_components.nodes import Node2D
from catplot.grid_components.edges import Edge2D
nodes, edges = [], []
# 創(chuàng)建重復(fù)單元中的nodes和edge
top = Node2D([0.0, 0.0], size=800, color="#2A6A9C")
t1 = Node2D([0.0, 1.0])
t2 = Node2D([1.0, 0.0])
nodes.append(top)
# 鏈接這三個node的edges
e1 = Edge2D(top, t1, width=4)
e2 = Edge2D(top, t2, width=4)
edges.extend([e1, e2])
# 中間的nodes
bridge1 = Node2D([0.0, 0.5], style="s", size=600, color="#5A5A5A", alpha=0.6)
bridge2 = Node2D([0.5, 0.0], style="s", size=600, color="#5A5A5A", alpha=0.6)
b1 = bridge1.clone([0.5, 0.5])
b2 = bridge2.clone([0.5, 0.5])
nodes.extend([bridge1, bridge2])
# 連接他們的edges
e1 = Edge2D(bridge1, b1)
e2 = Edge2D(bridge1, bridge2)
e3 = Edge2D(bridge2, b2)
e4 = Edge2D(b1, b2)
edges.extend([e1, e2, e3, e4])
# 正中間的node
h = Node2D([0.5, 0.5], style="h", size=700, color="#5A5A5A", alpha=0.3)
nodes.append(h)
好了,現(xiàn)在我們就創(chuàng)建一個重復(fù)單元中的所需的所有元素,可以繪制一下看看效果了
from catplot.grid_components.grid_canvas import Grid2DCanvas
from catplot.grid_components.supercell import SuperCell2D
canvas = Grid2DCanvas()
# 將上面的元素放到supercell中,后面我們將一supercell為單位進(jìn)行展開
supercell = SuperCell2D(nodes, edges)
# 繪制效果
canvas.add_supercell(supercell)
canvas.draw()
canvas.figure
?

OK, 重復(fù)單元已經(jīng)搭建成功,可以以他為單位進(jìn)行擴展了, 下面我們將其沿著x和y軸方向各進(jìn)行5次重復(fù)擴展。
# 很簡單,就一行代碼
expanded_supercell = supercell.expand(5, 5)
來看看效果:
canvas_big = Grid2DCanvas(figsize=(30, 20), dpi=60)??# 定制畫布大小
canvas_big.add_supercell(expanded_supercell)
canvas_big.draw()
canvas_big.figure
?

是不是很直觀和簡單呢?
繪制三維網(wǎng)格
繪制三維網(wǎng)格,catplot中我都寫了與二維繪制中相對應(yīng)的類和接口,這里就不贅述了,可以參考項目中的examples:https://github.com/PytLab/catplot/tree/master/examples/grid_3d_examples/expand_3d_supercell.ipynb
是不是只能畫正交的網(wǎng)格?
怎么可能,雖然所有的坐標(biāo)都是在分?jǐn)?shù)坐標(biāo)系中定義的,但是在SuperCell類中我添加了分?jǐn)?shù)坐標(biāo)到笛卡爾坐標(biāo)的轉(zhuǎn)化,從而可以使得catplot繪制任意的網(wǎng)格。來個例子就知道了:
# 創(chuàng)建nodes和edges的代碼與上面的部分完全相同
...
# 但是我們在定義supercell的時候可以修改cell_vectors參數(shù)來是重復(fù)單元發(fā)生形變
supercell = SuperCell2D(nodes, edges, cell_vectors=[[1.0, 0.0],
????????????????????????????????????????????????????[0.5, 1.0]])
canvas.add_supercell(supercell)
canvas.draw()
canvas.figure.show()
來我們看看這時候的重復(fù)單元是什么樣子:
?

然后我們再將其進(jìn)行一次3×3的擴展看看
expanded_supercell = supercell.expand(3, 3)
canvas_big = Grid2DCanvas(figsize=(30, 20), dpi=60)
canvas_big.add_supercell(expanded_supercell)
canvas_big.draw()
canvas_big.figure.show()
?

所以基本上現(xiàn)在所有類型的晶格都可以通過CatPlot來繪制了。
總結(jié)
本來catplot這個庫最初是自己用matplotlib來繪圖的小腳本,由于現(xiàn)在寫論文的情況下需要靈活的繪制網(wǎng)格圖,所以進(jìn)行了重寫,現(xiàn)在寫成了一個封裝了matplotlib的python庫方便使用者可以快速搭建自己想要的網(wǎng)格圖和繪制漂亮的energy profile。代碼和具體使用的notebook格式的例子均開源并放到了github上 (https://github.com/PytLab/catplot),歡迎有需要的童鞋參考和使用。