利用 Python 做出 Fitting of Morse Potential

Reference:
1. http://stackoverflow.com/questions/36312303/morse-potential-fit-using-python-and-curve-fit-from-scipy
2. Scipy- curve_fit


需要用到sci-py和matplotlib兩個元件
ubuntu下可以直接用以下指令安裝
sudo apt-get install python-scipy
sudo apt-get install python-matplotlib

Morse Potential



先用ORCA做Carbon monoxide的Scan,在B3LYP/TZVP level下, 從0.5 angstrom到2.5 angstrom, 每0.1 angstrom做一個點, 之後把能量值轉換為kcal/mol
Fitting結果:
如果把全部從0.5到2.5 angstrom都做的話,
得到的結果是 Bond Dissociation energy = 0.20 kcal/mol
Bond length = 1.85, a = 3.61556654
顯然數據完全就是錯的,若觀察一下得到的圖形可以發現,
因為bond-length小的時候上升幅度過快,大的時候又沒辦法得到太多數據點,
因此fit出來的圖形雖然很靠近我們的data點,但是fit出來的parameter完全是錯的


如果把feed的數據量減少的話,反而會完全fit不出來
如果嘗試把數據量密度增加,改為每0.05 angstrom做一個點,反而有某些點會無法算
SCF(DFT)中會有 large step...
看起來SCAN的數據是不好拿去feed給curve_fit的,資料量還不夠
而且在鍵長很長的狀況下,能量的計算也會有問題
此題除非有其他data來源,否則宣告無解...



Python Code 如下:
#import the module we need
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt

#input data
xdata=np.array([0.5,0.6, 0.7, 0.8, 0.9, 1.0 ,1.1 ,1.2 ,1.3 ,1.4 ,1.5 ,1.6 ,1.7 ,1.8 ,1.9 ,2.0 ,2.1 ,2.2 ,2.3 ,2.4 ,2.5])
ydata=[3507.22,1754.07,830.946,359.041,129.637,30.2853,0,5.40111,28.0713,57.9023,89.5626,120.368,149.144,175.434,199.172,220.438,239.392,256.226,271.12,284.256,295.789]

#set the real-line range
t=np.linspace(0.3,3)

def morse(x, D, a, Re, v):
    return (D * (np.exp(-2*a*(x-Re))-2*np.exp(-a*(x-Re))) + v)

#using Scipy module
#Parameter
#popt is optimal values for the parameters so that the sum of the squared error of f(xdata, *popt) - ydata is minimized
#pconv is the estimated covariance of popt. the diagonals provide the variance of the parameter estimate.

#p0 means Initial guess for the parameters. If none, then the initial values will all be 1.
#maxfev is the maximum iteration number

tstart = [60, 1, 3, 0]
popt, pcov = curve_fit(morse, xdata, ydata, p0 = tstart, maxfev=5000)
print popt #see the parameter of Morse_Potential

yfit = morse(t,popt[0], popt[1], popt[2], popt[3]) #generating the fitting y value

plt.plot(xdata, ydata,"ro") #plot the original data using 'ro' type
plt.plot(t, yfit) #plot the fitting result

plt.xlabel('angstrom')
plt.ylabel('kcal/mol')

plt.show()