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

Суть этой работы состоит в определении констант ковалентных взаимодействий для молекулярной механики на основе квантово-химических расчётов. Нам предоставлена оптимизированная структура этана в виде z-matrix (указаны значения длин связейб углов, торсионных углов):

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 5 1.08439 111.200 120 H 2 1 5 1.08439 111.200 -120 * '''

Наша цель состоит в том, что бы рассчитать 20 разных длинн связи с шагом 0.02 ангстрема для расчёта энергии в ORCA с разными значениями по длине одной из связей (C-C). Создаем функцию для запуска ORCA:

In [12]:
import subprocess, os
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]
    lines=out.splitlines()
    for line in lines:
        if "FINAL SINGLE POINT ENERGY" in line:
            energy=line.split()[4]
            return float(energy)

Согласно данному нам файлу длина С-С связи 1.52986 ангстрема. Расчитаем энергию для длины связи от 1.32986 до 1.72986.

In [3]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize 
In [26]:
energies=[]
bonds=[]
bond0=1.52986
for i in range(-10,11):
    bond=bond0+i*0.02
    bonds.append(bond)
    #создаем z-матрицу
    inp = '''!HF RHF 6-31G
* int 0 1
C 0 0 0 0 0 0 
C 1 0 0 %6.5f 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
*
'''
    energies.append(run_orca(inp %bond))
print bonds
print energies
[1.32986, 1.34986, 1.36986, 1.38986, 1.4098600000000001, 1.42986, 1.44986, 1.46986, 1.48986, 1.50986, 1.52986, 1.54986, 1.56986, 1.58986, 1.60986, 1.62986, 1.6498599999999999, 1.66986, 1.68986, 1.70986, 1.72986]
[-79.045965587732, -79.05360304748, -79.060094932943, -79.06554485476, -79.070047523843, -79.073689505281, -79.076549916355, -79.078701061159, -79.080208995167, -79.081134076599, -79.081531431378, -79.081451404936, -79.080939963093, -79.080039062387, -79.078786990807, -79.077218675992, -79.075365973146, -79.073257919932, -79.070920984592, -79.068379281351, -79.065654773153]

Строим графики зависимости энергии от длины С-С связи:

In [33]:
x_o=np.array(bonds)
y_o=np.array(energies)

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.3,1.75)
plt.ylabel('Energy')
plt.xlabel('C-C bond length, angstrem')
plt.show()
Optimized params: [  0.63992018   1.55699284 -79.08219403]

Зависимость энергии от длины С-С связи может быть апроксимирована параболой с минимум, соответствующим длине связи в оптимизированной структуре этана - 1.55699284 ангстрем.

Проделаем аналогичные операции для валентного угла HCС, его значения должны изменяться от 109.2° до 113.2° с шагом 0.2°.

In [35]:
energies_angle=[]
angles=[]
angle0=111.2
for i in range(-10,11):
    angle=angle0+i*0.2
    angles.append(angle)
    #создаем z-матрицу
    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 %6.3f 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
*
'''
    energies_angle.append(run_orca(inp %angle))
print angles
print energies_angle
[109.2, 109.4, 109.60000000000001, 109.8, 110.0, 110.2, 110.4, 110.60000000000001, 110.8, 111.0, 111.2, 111.4, 111.60000000000001, 111.8, 112.0, 112.2, 112.4, 112.60000000000001, 112.8, 113.0, 113.2]
[-79.197410483135, -79.197441312582, -79.197468918124, -79.197493268892, -79.197514349552, -79.197532160195, -79.197546703804, -79.197557983845, -79.197566003856, -79.197570767364, -79.197572277885, -79.197570538888, -79.197565553819, -79.197557326093, -79.197545859096, -79.197531156182, -79.197513220676, -79.197492055893, -79.197467665094, -79.197440051511, -79.197409218365]
In [38]:
x_o=np.array(angles)
y_o=np.array(energies_angle)

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(109,113.4)
plt.ylabel('Energy')
plt.xlabel('Angle HCC')
plt.show()
Optimized params: [  4.06112693e-05   1.11195140e+02  -7.91975723e+01]

Зависимость энергии от величины угла НСС получилось идеально апроксимировать параболой с минимум, соответствующим углу в оптимизированной структуре - 1.11195140e+02°.

Проделываем аналогичные операции для торсионного угла HCСH, его значения должны изменяться от -180° до 180° с шагом 12°.

In [39]:
energies_tors_angle=[]
tors_angles=[]
tors_angle0=0
for i in range(-15,16):
    tors_angle=tors_angle0+i*12
    tors_angles.append(tors_angle)
    #создаем z-матрицу
    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 %i
H 2 1 6 1.08439 111.200 120
H 2 1 6 1.08439 111.200 -120
*
'''   
    energies_tors_angle.append(run_orca(inp %tors_angle))
print tors_angles
print energies_tors_angle
[-180, -168, -156, -144, -132, -120, -108, -96, -84, -72, -60, -48, -36, -24, -12, 0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180]
[-79.197572395834, -79.197137699075, -79.195996511441, -79.194579710958, -79.193428553474, -79.192987702649, -79.193428552912, -79.194579709508, -79.195996509904, -79.197137698941, -79.197572392766, -79.197137699058, -79.195996511441, -79.194579710958, -79.193428553474, -79.192987702649, -79.193428552912, -79.194579709509, -79.195996509904, -79.197137698941, -79.197572392766, -79.197137699058, -79.195996511441, -79.194579710958, -79.193428553474, -79.192987702649, -79.193428552912, -79.194579709509, -79.195996509904, -79.197137698941, -79.197572392766]
In [58]:
x_o=np.array(tors_angles)
y_o=np.array(energies_tors_angle)


fitfunc = lambda p, x: np.sin(x*p[0]+p[1])*p[2]+p[3] # Target function
errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function

p0 = [1,1, -79, 0] # 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(-185,185)
plt.ylabel('Energy')
plt.xlabel('Torsion angle HCCH')
plt.show()
Optimized params: [  9.94834578e-01   1.57079660e+00   2.29205079e-03  -7.91952842e+01]

Зависимость энергии от торсионного угла НССН апроксимируется функцией синуса. Функция имеет 4 минимума: -180°, -60°, 60°, 180°, которые соответствуют различным парам водорода в структуре этана.

Увеличим шаг до 0.1 ангстрема при расчёте связи, длина связи будет варьировать в пределах 0.52986 до 2.52986. Построим зависимость.

In [59]:
energies_2=[]
bonds_2=[]
bond0=1.52986
for i in range(-10,11):
    bond=bond0+i*0.1
    bonds_2.append(bond)
    #создаем z-матрицу
    inp = '''!HF RHF 6-31G
* int 0 1
C 0 0 0 0 0 0 
C 1 0 0 %6.5f 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
*
'''
    energies_2.append(run_orca(inp %bond))
print bonds_2
print energies_2
[0.52986, 0.62986, 0.72986, 0.8298599999999999, 0.9298599999999999, 1.02986, 1.1298599999999999, 1.22986, 1.32986, 1.42986, 1.52986, 1.62986, 1.72986, 1.82986, 1.9298600000000001, 2.02986, 2.12986, 2.22986, 2.32986, 2.42986, 2.52986]
[-73.376057336679, -75.600038674293, -76.986925190757, -77.840020402069, -78.364137030277, -78.683902686411, -78.875495245128, -78.986233920144, -79.045965568718, -79.07368949118, -79.081531419723, -79.077218664924, -79.065654757297, -79.049930677904, -79.031982697254, -79.013023521421, -78.993820247811, -78.974866462702, -78.956483580267, -78.938878296987, -78.922175247055]
In [90]:
x_o=np.array(bonds_2)
y_o=np.array(energies_2)

fitfunc = lambda p, x: p[0]*(pow(x,p[2])-pow(x,p[3])) + p[1] # Target function
errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function

p0 = [0.5,-78, -3, -6] # 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(0.5,2.55)
plt.ylabel('Energy')
plt.xlabel('C-C bond length, angstrem')
plt.show()
Optimized params: [ 54.86995812 -78.53164959  -1.83377971  -1.78607697]

Зависимость энергии от длины С-С связи с шагом в 0,1 ангстрем можно апроксимировать функцией x^(-1.83377971)-x^(-1.78607697).