Вычисление параметров молекулы этана для молекулярной механики

Для начала импортируем нобходимые библиотеки и инициализируем функцию, которая будет обращаться к процессу ORCA

In [1]:
import subprocess
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize 
from numpy import cos, exp
In [2]:
def killer_whale(inp):
    with open('orca.inp', 'w') as outfile:
        outfile.write(inp) 
    p = subprocess.Popen("/srv/databases/orca/orca orca.inp", 
                          shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out=p.communicate()[0]

    return out

Создадим входной файл для программы ORCA и посчитаем энергию С-С связи для разных длин связи (шаг 0.02)

In [3]:
inp = '''!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.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 6 1.08439 111.200 120
H 2 1 6 1.08439 111.200 -120
*
''' # оптимизированная молекула этана
x=[]
y=[]
for i in range(-10,10):
    ini=inp.splitlines()
    ind=ini[3].split()
    ind[4]='{:.5f}'.format(float(ind[4])+0.02*i) # цикл в котором мы подсчитываем энергию связей разной длины
    ini[3]=' '.join(ind)
    inp1='\n'.join(ini)
    s=killer_whale(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[4])) # длина связи
    y.append(float(s[k:k+100].split()[5])) # энергия в миллиэлектрон-вольтах

График зависимости энергии молекулы от длины С-С связи с аппроксимацией параболой

In [6]:
x_0=x
y_0=y

fitfunc = lambda p, x: p[0]*pow(p[1]-x,2) + p[2] #параметры аппроксимации  f(x)=k(b-x)^2 + a
p0 = [1,1, -79]
errfunc = lambda p, x, y: fitfunc(p, x) - y 
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_0, y_0))
print ("Optimized params:", p1)
plt.plot(x_0, y_0, "ro", x_0,fitfunc(p1,x_0),"r-",c='blue',alpha=0.5)
plt.show()
Optimized params: [ 1.75406459e+01  1.54848023e+00 -2.15509267e+03]

Полученный в ходе аппроксимации оптимальное значение длины связи близко к литературному, но сама линия на точки ложится плохо. Возможно, что на данном масштабе аппроксимация параболой еще адекватна, а на большем - нет.

Проведем такую же итерацию по разным значениям угла связи Н-С-С

In [10]:
x=[]
y=[]
for i in range(-10,10):
    ini=inp.splitlines()
    ind=ini[4].split()
    ind[5]='{:.3f}'.format(float(ind[5])+0.2*i)
    ini[4]=' '.join(ind)
    inp1='\n'.join(ini)
    s=killer_whale(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[5]))
    y.append(float(s[k:k+100].split()[5]))
x_0=x
y_0=y
    
fitfunc = lambda p, x: p[0]*pow(p[1]-x,2) + p[2] 
p0 = [1,1, -79]
errfunc = lambda p, x, y: fitfunc(p, x) - y 
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_0, y_0))
print ("Optimized params:", p1)
plt.plot(x_0, y_0, "ro", x_0,fitfunc(p1,x_0),"r-",c='green',alpha=0.5)
plt.show()
Optimized params: [ 1.10480506e-03  1.11195312e+02 -2.15507550e+03]

Видно, что аппроксимация вписывается лучше, оптимальное значение угла хорошо соответствует научному консенсусу.

Производим аналогичные расчеты для торсионного угла. Аппроксимация будет тригонометрической

In [13]:
x=[]
y=[]
for i in range(0,361,12):
    ini=inp.splitlines()
    ind=ini[7].split()
    ind[6]=str(int(ind[6])-i)
    ini[7]=' '.join(ind)
    inp1='\n'.join(ini)
    s=killer_whale(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[6]))
    y.append(float(s[k:k+100].split()[5]))

x_0=x
y_0=y  
fitfunc = lambda p, x: p[0]*cos((x-p[1])*p[2]) + p[3] 
errfunc = lambda p, x, y: fitfunc(p, x) - y 
p0 = [0.1,0,1,-2155] 
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_0, y_0))
print ("Optimized params:", p1)
plt.plot(x_0, y_0, "ro", x_0,fitfunc(p1,x_0),"r-",c='blue',alpha=0.5)
plt.show()
Optimized params: [ 6.23702350e-02  3.00914624e-06  9.94834665e-01 -2.15501324e+03]

Молекула этана обладает трехсторонней симметрией, что видно из графика энергии. Минимумы энергии у заторможенных конформаций, а максимумы - у заслоненных

Дополним мой практикум расчетом энергии связи в более широких пределах

In [31]:
x=[]
y=[]
for i in range(-10,20):
    ini=inp.splitlines()
    ind=ini[3].split()
    ind[4]='{:.5f}'.format(float(ind[4])+0.1*i)
    ini[3]=' '.join(ind)
    inp1='\n'.join(ini)
    s=killer_whale(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[4]))
    y.append(float(s[k:k+100].split()[5]))
x_0=x
y_0=y   
In [36]:
fitfunc = lambda p, x: np.multiply(p[0],pow(x,p[1])) +  np.multiply(p[2], pow(x,p[3])) - p[4]
p0 = [1, -12, -10, -2, 79]
errfunc = lambda p, x, y: fitfunc(p, x) - y  
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_0, y_0), maxfev=15000)
print("Optimized params:", p1)

plt.plot(x_0, y_0, "ro", x_0,fitfunc(p1,x_0),"r-",c='orange',alpha=0.5)
plt.show()
    
Optimized params: [   2.48208785   -3.70059326   12.44019374   -3.70065499 2153.1318482 ]

На более широком масштабе аппрокисмировать надо полиномом