Contents
Fitting a 1-D PES using Python
Data set:
# Ar2 MP2 interaction energies
# Units: R : Angstrom
# E : cm^{-1}
#
# --------Using mid-bonds---
# R aDZ-noCP aTZ-noCP aQZ-noCP aDZ-CP aTZ-CP aQZ-CP
3.000 849.203 683.451 617.179 693.597 646.098 620.624
3.250 205.203 106.749 81.093 114.204 94.645 82.514
3.500 -14.944 -75.276 -82.840 -66.813 -76.199 -81.462
3.650 -63.415 -105.688 -109.144 -97.067 -104.068 -107.120
3.750 -78.828 -110.712 -112.906 -102.235 -108.261 -110.366
3.850 -85.975 -108.984 -110.509 -100.761 -106.027 -107.494
4.000 -87.113 -99.871 -100.789 -92.066 -96.351 -97.263
4.250 -76.570 -79.596 -79.416 -72.337 -75.132 -75.668
4.500 -61.796 -60.937 -59.607 -54.213 -55.757 -56.110
4.750 -48.339 -45.960 -44.074 -39.975 -40.728 -40.908
5.000 -37.453 -34.430 -32.644 -29.365 -29.747 -29.781
5.500 -22.077 -19.352 -18.364 -16.188 -16.262 -16.180
6.000 -12.663 -11.106 -10.662 -9.304 -9.278 -9.233import math
import matplotlib.pyplot as plt
import numpy as np
import scipy.optimize as opt
unit_bohr2angstrom = 0.529177249
unit_hartree2cm = 219474.631371
def func_LJ(x,a,c):
# The Lennard-Jones model
return a/x**12 - c/x**6
def func_BM(x,a,b,c):
# The Born-Mayer model
#return a*np.exp(-b*x) - c/x**6
return np.exp(-b*(x-a)) - c/x**6
data = np.transpose(np.loadtxt('Ar2_MP2.txt'))
rvals = data[0]/unit_bohr2angstrom # convert from Angstrom to Bohr
evals = data[1]/unit_hartree2cm # convert from cm-1 to Hartree
# Standard deviations all set to 1.0
sigma = sigma = np.full(rvals.size,1.0)
print("sigma array: \n")
print(sigma)
# LJ fit
# =
# Starting values for parameters
x0=np.array([1.0,1.0])
# Do the fitting:
popt, pcov = opt.curve_fit(func_LJ,rvals,evals,p0=x0,sigma=sigma)
#
# popt : optimized parameters
# pcov : covariance matrix
print("\n\n")
print(" a = %12.4f c = %12.4f \n" % (popt[0],popt[1]))
print("Covariance matrix: \n")
ndim = pcov.shape[0]
for i in range(0,ndim):
for j in range(0,ndim):
pcov[i][j] = pcov[i][j]/np.sqrt(pcov[i][i]*pcov[j][j])
print(pcov)
print("\n\n")
#
# Plot
plt.plot(rvals,evals,'b-', label='data',marker='o')
plt.plot(rvals, func_LJ(rvals, *popt), 'g--', label='fit')
plt.show()
# BM fit
# =
# Starting values for parameters
x0=np.array([1.0,1.0,1.0])
# Do the fitting:
popt, pcov = opt.curve_fit(func_BM,rvals,evals,p0=x0,sigma=sigma)
#
# popt : optimized parameters
# pcov : covariance matrix
print("\n\n")
print(" a = %12.4f b = %12.4f c = %12.4f \n" % (popt[0],popt[1],popt[2]))
print("Covariance matrix: \n")
ndim = pcov.shape[0]
for i in range(0,ndim):
for j in range(0,ndim):
pcov[i][j] = pcov[i][j]/np.sqrt(pcov[i][i]*pcov[j][j])
print(pcov)
print("\n\n")
#
# Plot
plt.plot(rvals,evals,'b-', label='data',marker='o')
plt.plot(rvals, func_BM(rvals, *popt), 'g--', label='fit')
plt.show()
# Now try a weighting of exp(-(e-e0)/kT)
e0 = 80.0/unit_hartree2cm # both in units of evals
kT = 400.0/unit_hartree2cm
#sigma = np.exp(-np.abs(evals-e0)/kT)
sigma = np.exp(-np.abs(evals/e0))
print("sigma array: \n")
print(sigma)
# LJ fit
# =
# Starting values for parameters
x0=np.array([1.0,1.0])
# and re-fit
popt, pcov = opt.curve_fit(func_LJ,rvals,evals,p0=x0,sigma=sigma)
#
# popt : optimized parameters
# pcov : covariance matrix
print("\n\n")
print(" a = %12.4f c = %12.4f \n" % (popt[0],popt[1]))
print("Covariance matrix: \n")
ndim = pcov.shape[0]
for i in range(0,ndim):
for j in range(0,ndim):
pcov[i][j] = pcov[i][j]/np.sqrt(pcov[i][i]*pcov[j][j])
print(pcov)
print("\n\n")
#
# Plot
plt.plot(rvals,evals,'b-', label='data',marker='o')
plt.plot(rvals, func_LJ(rvals, *popt), 'g--', label='fit')
plt.show()
Covarianc
The fitting function **curve_fit** writes out not only the solution, but also the //covariance matrix//. This tells us how the parameters are correlated. To find out more about this have a look at this site. Here are a few examples of the //correlation matrix// which is defined in terms of the covariance matrix as follows: $$
- {\rm Corr}(x,y) = {\rm Cov}(x,y)/(\sigma_x \sigma_y)
$$ where the standard deviations are themselves part of the covariance matrix: $$
- \sigma_x = \sqrt{{\rm Cov}(x,x)}.
$$
Here's the correlation matrix for a fit to the Lennard-Jones function form: $$
V_{\rm LJ}(r) = \frac{A}{r{12}} - \frac{C}{r6}
$$
Correlation matrix: [[ 1. 0.9299438] [ 0.9299438 1. ]]
Ignore the $1.0$ terms on the diagonal. We are interested in the off-diagonal terms. For parameters that are not correlated these will be zero. Here we have off-diagonal terms nearly $+1$, so we conclude that the two parameters in the LJ function are heavily correlated, and will tend to change in the same way.
Conversely, here is the correlation matrix for the Born-Mayer function defined as: $$
- V_{\rm BM}(r) = \exp{(-\alpha(r-r_0))} - \frac{C}{r^6},
$$ with three free parameters: $\alpha$, $r_0$ and $C$.
Correlation matrix: [[ 1. 0.9904921 -0.87938874] [ 0.9904921 1. -0.93439446] [-0.87938874 -0.93439446 1. ]]
Here we see a mixed picture: $\alpha$ is heavily correlated with $r_0$. On the other hand, the correlation with $C$ is slightly weaker.
