%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import optimize
ORCA_PATH = "./orca/orca" # На kodomo не грузится numpy (???) + мало памяти.
import subprocess
from IPython.display import Image, display
def run_orca(inp):
with open('orca.inp', 'w') as outfile:
outfile.write(inp)
with subprocess.Popen(f"{ORCA_PATH} orca.inp", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
out=p.communicate()[0].decode()
for line in out.splitlines():
if 'FINAL SINGLE POINT' in line:
return float(line.strip().split()[4])
return np.nan
input_orca = '''!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
*
'''
run_orca(input_orca)
Сделаем шаблоны файлов
bond = '''!HF RHF 6-31G
* int 0 1
C 0 0 0 0 0 0
C 1 0 0 {:.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
*
'''
angle = '''!MP2 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 {:.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
*'''
tors = '''!MP2 6-311G**
* 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 {:d}
H 2 1 6 1.08439 111.200 120
H 2 1 6 1.08439 111.200 -120
*'''
Посчитаем минимум для C-C связи
bonds = np.linspace(1.3, 1.7, 21)
bonds
run_orca_bonds = np.vectorize(lambda x: run_orca(bond.format(x)), otypes=[np.float])
bonds_e = run_orca_bonds(bonds)
bonds_e
Отрисуем результаты и зафитим функцию по ним.
#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=(bonds, bonds_e))
print("Optimized params:", p1)
#Plot it
plt.plot(bonds, bonds_e, "x", c='darkred', alpha=0.5)
plt.plot(bonds, fitfunc(p1, bonds), "r-", c='red')
#plt.xlim(1,2)
plt.show()
В статье GAFF указана длина связи 1.526. У нас - 1.552, но с учетом кривого фитирования, скорее всего, должно получаться ~= 1.54
Аналогичную операцию повторим для углов
angles = np.linspace(109.2, 113.2, 21)
angles
run_orca_angles = np.vectorize(lambda x: run_orca(angle.format(x)), otypes=[np.float])
angles_e = run_orca_angles(angles)
def angle_potential(x, k, f0, a):
return k / 2 * (x - f0) ** 2 + a
#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, -8] # Initial guess for the parameters
p1, success = optimize.leastsq(errfunc, p0[:], args=(angles, angles_e))
print("Optimized params:", p1)
#Plot it
plt.plot(angles, angles_e, "x", c='darkred', alpha=0.5)
plt.plot(angles, fitfunc(p1, angles), "r-", c='red')
#plt.xlim(1,2)
plt.show()
Зафитировалось здесь лучше. Получили минимум в 111.0. В статье не указан.
torss = np.linspace(-180, 180, 31).astype(int)
torss
run_orca_torss = np.vectorize(lambda x: run_orca(tors.format(x)), otypes=[np.float])
torss_e = run_orca_torss(torss)
plt.plot(torss, torss_e, "x", c='darkred', alpha=0.5)
Кажется, можно приблизить синусоидой.
fitfunc = lambda p, x: p[0] * (1 + np.cos(p[1] * np.pi * np.radians(x) - p[2])) + p[3] # Target function
errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function
p0 = [0, 1, -1, -1] # Initial guess for the parameters
p1, success = optimize.leastsq(errfunc, p0[:], args=(torss, torss_e))
print("Optimized params:", p1)
#Plot it
plt.plot(torss, torss_e, "x", c='darkred', alpha=0.5)
good_torss = np.linspace(-180, 180, 361)
plt.plot(good_torss, fitfunc(p1, good_torss), "r-", c='red')
#plt.xlim(1,2)
plt.show()
Ура, получилось! Следовательно, значения минимумов соответствуют -180, -60, 60, 180 - т.е. анти-конформациям этана. Довольно неплохо.
И проделаем для длины связи с бОльшим шагом и на большем диапазоне.
bonds_l = np.linspace(1.0, 3.0, 21)
bonds_l
bonds_le = run_orca_bonds(bonds_l)
bonds_le
plt.plot(bonds_l, bonds_le, "x", c='darkred', alpha=0.5)
Я бы сказал, похоже на a/x^n + kx + b
#function is f(x)=k(b-x)^2 + a
fitfunc = lambda p, x: p[0] / np.power(x, p[1]) + p[2] * x + p[3] # Target function
errfunc = lambda p, x, y: fitfunc(p, x) - y # Error function
p0 = [1, 1, 1, 1] # Initial guess for the parameters
p1, success = optimize.leastsq(errfunc, p0[:], args=(bonds_l, bonds_le))
print("Optimized params:", p1)
#Plot it
plt.plot(bonds_l, bonds_le, "x", c='darkred', alpha=0.5)
plt.plot(bonds_l, fitfunc(p1, bonds_l), "r-", c='red')
#plt.xlim(1,2)
plt.show()
Может, и похоже. С 4 степенями свободы-то трудно сделать, чтобы плохо вышло.
from scipy.optimize import fmin
fmin(lambda x: fitfunc(p1, x), 1.5)
С этой не очень точной оптимизацией получилось примерно 1.55. Примерно так же, как в прошлый раз.