Домашнее задание №5¶

In [15]:
#imports
import subprocess
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize 

Молекула этана:

\begin{equation*} C_{2}H_{6} \end{equation*}

Сначала зададим 20 значений длины связи C-C с шагом 0.02. Для каждого из случаев рассчитаем величину значения энергии с помощью ORCA. Затем на этих значениях восстановим функцию зависимости энергии от длины связи.

На вход ORCA будет подаваться шаблон, где будет изменено значение длины связи C-C.

In [16]:
inp_length = '''!HF RHF 6-31G
* int 0 1
C 0 0 0 0 0 0 
C 1 0 0 {length} 0 0 
H 1 2 0 1.08439 111.200 0
H 1 2 3 1.08439 111.200 120
H 1 2 3 1.08439 111.200 -120
H 2 1 3 1.08439 111.200 180
H 2 1 5 1.08439 111.200 120
H 2 1 5 1.08439 111.200 -120
*
'''

Функция по расчету энергии для входных файлов,где будет меняться значение длины связи|величины торсионного угла|величины валентного угла. Грубо говоря, функция возвращает иксы и игрики(величины энергии):

In [17]:
def energy(template,variable,begin,step,n):
    value=begin
    e=[]
    x=[]
    if (variable=="length"):
        for i in range(0,n):
            value=begin+i*step
            dupl=template.format(length=str(value))
            e.append(run_orca(dupl))
            x.append(value)
    elif (variable=="val_angle"):
        for i in range(0,n):
            value=begin+i*step
            dupl=template.format(val_angle=str(value))
            e.append(run_orca(dupl))
            x.append(value)
    elif (variable=="tors_angle"):
        for i in range(0,n):
            value=begin+i*step
            dupl=template.format(tors_angle_1=str(value),tors_angle_2=str(value-60),tors_angle_3=str(value+60))
            e.append(run_orca(dupl))
            x.append(value) 
    return e,x

Далее идет функция по заупуску ORCA, где для каждого входного файла в формате строки будет найдено значение энергии:

In [18]:
def run_orca(inp):
    with open('orca.inp', 'w') as outfile:
        outfile.write(inp)
    p = subprocess.Popen("/home/shad/progs/bin/orca orca.inp", 
                          shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out=p.communicate()[0]
    
    # extract energy: FINAL SINGLE POINT ENERGY'
    # and return it as float
    for s in out.splitlines():
        if s.startswith("FINAL SINGLE POINT ENERGY"):
            return(float(s[len("FINAL SINGLE POINT ENERGY"):]))
    

Рассчитаем величины энергий для входных файлов по длине связи С-С

In [19]:
#path="/home/shad/hse/mrPuk/hw5/length/"
e_length,x_length=energy(inp_length,variable="length",begin=1.52986-10*0.02,step=0.02,n=20)

Функция построения зависимостей (целевая функция - квадратичная):

In [20]:
def plot(x_o,y_o):
    
    #function is  f(x)=k(b-x)^2 + a
    fitfunc = lambda p, x: p[0]*pow(p[1]-x,2) + p[2] # Target function
    errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function

    p0 = [1,1, -79] # Initial guess for the parameters
    p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))
    print "Optimized params:", p1

    #Plot it
    plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)
    #plt.xlim(1,2)
    plt.show()
In [21]:
plot(x_length,e_length)
Optimized params: [  0.66824653   1.55389272 -79.08233322]

Как видно из вышеприведенного графика, аппроксимация оказалась вполне удачной. Теперь сделаем аналогичные действия для валентного угла:

In [22]:
inp_val_angle = '''!HF RHF 6-31G
* int 0 1
C 0 0 0 0 0 0 
C 1 0 0 1.52986 0 0 
H 1 2 0 1.08439 {val_angle} 0
H 1 2 3 1.08439 {val_angle} 120
H 1 2 3 1.08439 {val_angle} -120
H 2 1 3 1.08439 {val_angle} 180
H 2 1 5 1.08439 {val_angle} 120
H 2 1 5 1.08439 {val_angle} -120
*
'''
In [23]:
e_val_angle,x_val_angle=energy(inp_val_angle,variable="val_angle",begin=111.2-10*0.2,step=0.2,n=21)
In [24]:
plot(x_val_angle,e_val_angle)
Optimized params: [  3.31056999e-04   1.12270871e+02  -7.90819111e+01]

Как видим, аппроксимиция удалась. Теперь про торсионный угол:

In [25]:
inp_tors_angle = '''!HF RHF 6-31G
* int 0 1
C 0 0 0 0 0 0 
C 1 0 0 1.52986 0 0 
H 1 2 0 1.08439 111.2 0
H 1 2 3 1.08439 111.2 120
H 1 2 3 1.08439 111.2 -120
H 2 1 3 1.08439 111.2 {tors_angle_1}
H 2 1 5 1.08439 111.2 {tors_angle_2}
H 2 1 5 1.08439 111.2 {tors_angle_3}
*
'''
In [26]:
e_tors_angle,x_tors_angle=energy(inp_tors_angle,variable="tors_angle",begin=-180,step=12,n=31)
In [27]:
plot(x_tors_angle,e_tors_angle)
Optimized params: [  1.33468021e-08  -8.76029077e+01  -7.90827627e+01]

Как видим, на графике сверху не получилось аппроксимировать функцию. Скорее всего эта функция имеет зависимость от синуса и косинуса. Относительно минимумов функции - их три.

In [28]:
def plot_s_c(x_o,y_o):
    
    #function is  f(x)=a*sin(b*x)+c*cos(d*x)
    fitfunc = lambda p, x: p[0]*np.sin(p[1]*x) + p[2]*np.cos(p[3]*x)+p[4] # Target function
    errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function

    p0 = [1,1,1,1,-79] # Initial guess for the parameters
    p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))
    print "Optimized params:", p1

    #Plot it
    plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)
    #plt.xlim(1,2)
    plt.show()
In [29]:
plot_s_c(np.array(x_tors_angle),np.array(e_tors_angle))
Optimized params: [ -1.50760490e-03   9.94841114e-01  -1.00591864e-03   9.94840464e-01
  -7.90825389e+01]

Ну вот, все очень даже неплохо заапроксимировалось.

Теперь увеличим шаг до 0.1 ангстрема при расчете связи и построим зависимость:

In [30]:
e_length,x_length=energy(inp_length,variable="length",begin=1.52986-12*0.1,step=0.1,n=41)
plot(x_length,e_length)
Optimized params: [  1.2257014    2.78420346 -79.99536029]

Не совсем хорошо. Попробуем степенную функцию...

In [31]:
def plot_2(x_o,y_o):
    
    #function is  f(x)=a*(1/x)^b+c
    fitfunc = lambda p, x: p[0]*pow(1/x,p[1]) + p[2] # Target function
    errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function

    p0 = [1,1, -79] # Initial guess for the parameters
    p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))
    print "Optimized params:", p1

    #Plot it
    plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)
    #plt.xlim(1,2)
    plt.show()
In [32]:
plot_2(np.array(x_length),e_length)
Optimized params: [  1.04388752   2.42409416 -79.08175699]

Как видно, данную зависимость можно аппроксимировать степенной функцией. Кстати, кол-во точек в промежутке (0;1), которые берутся для построения зависимости играет важную роль: чем их больше, тем точнее кривая "ложится" на заданные значения. Но с другой стороны тенденция возрастания значений функции при большем "х" говорит о том, что тут может пригодится и другая функция. Попробуем Morse potential. Его формула следующая:

\begin{equation*} V(r)=D_{e}(1-e^{-a(r-r_{e})})^2 \end{equation*}

Используем ее при построении зависимости:

In [36]:
def plot_morse(x_o,y_o):
    
    #morse function
    fitfunc = lambda p, x: p[0]*pow(1-np.exp(-p[1]*(x-p[2])),2) + p[3] # Target function
    errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function

    p0 = [1,1, 1,-79] # Initial guess for the parameters
    p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))
    print "Optimized params:", p1

    #Plot it
    plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)
    #plt.xlim(1,2)
    plt.show()
In [37]:
plot_morse(np.array(x_length),e_length)
Optimized params: [  0.1900541    2.10019086   1.41583154 -79.03143879]

Как видно, аппроксимация стала еще лучше.

In [ ]: