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

Импортировать необходимые модули

In [76]:
import subprocess
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize 
from numpy import cos, exp

Задать функцию, запускающую Orca для заданной молекулы и выдающую результат

In [2]:
def run_orca(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

Задать цикл, в котором для разных длин С-С связи c шагом 0,02 ангстрема будет считаться энергия и записывать результат в массив

In [45]:
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
*
''' # optimized ethane molecule
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)# change the CC bond length
    ini[3]=' '.join(ind)
    inp1='\n'.join(ini)
    s=run_orca(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[4])) # bond length
    y.append(float(s[k:k+100].split()[5])) # energy in meV

    

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

In [46]:
x_o=x
y_o=y

#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 = [16,1.52986, -2151] # Initial guess for the parameters
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))# optimization of function
print ("Optimized params:", p1)

#Plot 
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()
Optimized params: [ 1.75406241e+01  1.54848026e+00 -2.15509267e+03]

Как можно заметить, аппроксимирующая линия плохо ложится на точки, но формой более менее им соответствует. Минимум энергии находится на расстоянии 1,55 ангстрем, тогда как по данным GAFF оптимальная длина 1.526, причина в плохой аппроксимации.

Аналогично рассчитать энергии и пoстроить график для угла HCC.

In [47]:
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=run_orca(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[5]))
    y.append(float(s[k:k+100].split()[5]))
    

Построить график зависимости энергии молекулы от угла, аппроксимировать квадратичной функцией

In [50]:
x_o=x
y_o=y

fitfunc = lambda p, x: p[0]*pow(p[1]-x,2) + p[2] 
errfunc = lambda p, x, y: fitfunc(p, x) - y 
p0 = [0.001,111.2, -2151] 
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))
print ("Optimized params:", p1)

plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)
plt.xlim(105,115)
plt.show()
Optimized params: [ 1.10481873e-03  1.11195310e+02 -2.15507550e+03]

Здесь аппроксимирующая кривая вписалась лучше, чем в предыдущем случае. Оптимальное значение угла составило 111,2, что соответствует оптимальному значению.

Аналогично рассчитать энергии и построить график для поворота по торсионному углу CC

In [67]:
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=run_orca(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[6]))
    y.append(float(s[k:k+100].split()[5]))
    

В отличие от предыдущих случаев аппроксимирующая функция синусоидальная

In [70]:
x_o=x
y_o=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_o, y_o))
print ("Optimized params:", p1)
plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)

plt.xlim(-180,180)
plt.show()
Optimized params: [ 6.23702350e-02  3.00914624e-06  9.94834665e-01 -2.15501324e+03]

Аппроксимирующая функция имеет три минимума энергии, соответствующих трем заторможенным конформациям.

Рассчитать зависимость энергии от длины связи с шагом 0,1 ангстрема (таким образом интервал длин связи увеличивается в 5 раз). Построить график

In [75]:
x=[]
y=[]
for i in range(-10,10):
    ini=inp.splitlines()
    ind=ini[3].split()
    ind[4]='{:.5f}'.format(float(ind[4])+0.1*i)# change the CC bond length
    ini[3]=' '.join(ind)
    inp1='\n'.join(ini)
    s=run_orca(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[4]))
    y.append(float(s[k:k+100].split()[5]))
In [83]:
x_o=x
y_o=y

plt.plot(x_o,y_o,"ro",c='blue',alpha=0.5)
plt.xlim(0,3)
plt.show()

Как можно заметить, зависимость энергии от длины связи далека от квадратичной. При длине связи менее оптимальной для аппроксимации можно использовать экспоненциальную функцию, а при длине связи больше оптимальной - линейную.

Рассчитать зависимость энергии молекулы от величины угла НСС, также в увеличенном в 5 раз интервале

In [ ]:
x=[]
y=[]
for i in range(-10,10):
    ini=inp.splitlines()
    ind=ini[4].split()
    ind[5]='{:.3f}'.format(float(ind[5])+i)
    ini[4]=' '.join(ind)
    inp1='\n'.join(ini)
    s=run_orca(inp1)
    k=s.decode('utf-8').index('Total Energy')
    x.append(float(ind[5]))
    y.append(float(s[k:k+100].split()[5]))
In [74]:
x_o=x
y_o=y

fitfunc = lambda p, x: p[0]*pow(p[1]-x,2) + p[2] 
errfunc = lambda p, x, y: fitfunc(p, x) - y 
p0 = [0.001,111.2, -2151] 
p1, success = optimize.leastsq(errfunc, p0[:], args=(x_o, y_o))
print ("Optimized params:", p1)

plt.plot(x_o, y_o, "ro", x_o,fitfunc(p1,x_o),"r-",c='blue',alpha=0.5)
plt.xlim(100,125)
plt.show()
    
Optimized params: [ 1.11006934e-03  1.11245377e+02 -2.15507559e+03]

Что касается зависимости энергии от угла, то его можно аппроксимировать квадратичной функцией в широких пределах