Skip to content

Commit 365d28e

Browse files
Update 2D_multipole_inv.ipynb
1 parent b9be20b commit 365d28e

File tree

1 file changed

+67
-64
lines changed

1 file changed

+67
-64
lines changed

2D_multipole_inv.ipynb

Lines changed: 67 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,23 @@
2727
"metadata": {},
2828
"outputs": [],
2929
"source": [
30+
"# Import important libraries\n",
3031
"import math\n",
3132
"import numpy as np\n",
3233
"import pandas as pd\n",
3334
"from scipy.special import legendre\n",
3435
"import matplotlib.pyplot as plt"
3536
]
3637
},
38+
{
39+
"cell_type": "markdown",
40+
"id": "f14eaf97",
41+
"metadata": {},
42+
"source": [
43+
"#### read input file (PES) give separation (remove header such as r, theta, phi, etc)\n",
44+
"*The code assumes first column to be R (Radial Coordinate), 2nd to be theta (Angular coordinate) and 3rd column to be E(Potentials)*\n"
45+
]
46+
},
3747
{
3848
"cell_type": "code",
3949
"execution_count": 164,
@@ -42,8 +52,7 @@
4252
"outputs": [],
4353
"source": [
4454
"df_inp = pd.read_csv('nccn_he_NN.dat',header=None,sep='\\s+') # import file\n",
45-
"\n",
46-
"lm = 10 # lambda max "
55+
"lm = 10 # Difine lambda max "
4756
]
4857
},
4958
{
@@ -53,9 +62,10 @@
5362
"metadata": {},
5463
"outputs": [],
5564
"source": [
56-
"df_inp.sort_values(by = [ 0,1], inplace=True, ascending = True) # sort by R, theta\n",
57-
"df_inp[2] = (df_inp[2]+188.31099452)*219474.63 # convert to cm-1\n",
58-
"df_inp.reset_index(inplace=True, drop = True)"
65+
"df_inp.sort_values(by = [ 0,1], inplace=True, ascending = True) # sort by (R, theta) in ascending order\n",
66+
"E_inf = 188.31099452 # define E_infinity (Asymptotic Energy)\n",
67+
"df_inp[2] = (df_inp[2] - E_inf)*219474.63 # convert to cm-1\n",
68+
"df_inp.reset_index(inplace=True, drop = True) # Resetting index"
5969
]
6070
},
6171
{
@@ -65,13 +75,13 @@
6575
"metadata": {},
6676
"outputs": [],
6777
"source": [
68-
"nc=80\n",
69-
"ngm = 91\n",
70-
"px = np.zeros((ngm,lm)) # stores legendre polynomial\n",
71-
"f = np.zeros(ngm) # Ab initio energy \n",
72-
"R = np.zeros(nc) # distance R\n",
73-
"E = np.zeros(nc) # multipole expanded potentials\n",
74-
"df_out = pd.DataFrame() # dataframe stores V lambda"
78+
"nc=80 # number of Radial coordinates (Must be same for all angles)\n",
79+
"ngm = 91 # number of angular coordinates (90 for symmetric molecule)\n",
80+
"px = np.zeros((ngm,lm)) # Matrix to stores legendre coeffinients\n",
81+
"f = np.zeros(ngm) # Array to store part of ab initio energy \n",
82+
"R = np.zeros(nc) # Array for distance R\n",
83+
"E = np.zeros(nc) # Array to store multipole expanded potentials\n",
84+
"df_out = pd.DataFrame() # dataframe that stores final V lambda (Radial coefficients)"
7585
]
7686
},
7787
{
@@ -81,8 +91,13 @@
8191
"metadata": {},
8292
"outputs": [],
8393
"source": [
84-
"V_nf= np.zeros((nc,lm)) \n",
85-
"V_n= np.zeros(lm)"
94+
"V_nf= np.zeros((nc,lm)) # Numpy 2D array to store V_lambdas as they are calculated for each radial term\n",
95+
"V_n= np.zeros(lm) # Stores V_lambda for one radial term (Depreciated part of code no longer used!)\n",
96+
"symmetric = True # Verify if rigid rotor is symmetric (else put False)\n",
97+
"if symmetric:\n",
98+
" sym = 2\n",
99+
"else:\n",
100+
" sym = 1"
86101
]
87102
},
88103
{
@@ -92,11 +107,11 @@
92107
"metadata": {},
93108
"outputs": [],
94109
"source": [
95-
"for j2 in range (ngm): # angle\n",
96-
" for j3 in range (lm): # legendre\n",
97-
" pxc = legendre(j3*2) # *2 for symmetric molecule (data upto 90 degree); *1 otherwise\n",
98-
" ang = math.radians(j2)\n",
99-
" px[j2,j3]= pxc(math.cos(ang)) "
110+
"for j2 in range (ngm): # loop over anglular terms (considering 0-90 with 1 degree interval)\n",
111+
" for j3 in range (lm): # loop over legendre terms\n",
112+
" pxc = legendre(j3*sym) # Uses j3*2 for symmetric molecule (only even V_lambdas); and *1 otherwise\n",
113+
" ang = math.radians(j2) # convert angles to radians\n",
114+
" px[j2,j3]= pxc(math.cos(ang)) # store legendre coefficient for corrosponding angle and lambda (2D)"
100115
]
101116
},
102117
{
@@ -106,7 +121,7 @@
106121
"metadata": {},
107122
"outputs": [],
108123
"source": [
109-
"A_inv = np.linalg.pinv(px)"
124+
"A_inv = np.linalg.pinv(px) # take pseudo-inverse of px matrix (synonymus to least squares fit)"
110125
]
111126
},
112127
{
@@ -334,13 +349,13 @@
334349
],
335350
"source": [
336351
"for i in range (nc): # loop over all R \n",
337-
" ct = i*ngm\n",
338-
" f = df_inp[2][ct:ct+ngm] # sorted by R, theta (extracting V for one R at a time)\n",
339-
" V_n1 = A_inv.dot(f)\n",
340-
" V_nf[i,:] = V_n1\n",
341-
"a12 = np.arange(lm)\n",
342-
"df_Vnf = pd.DataFrame(V_nf, columns = a12*2)\n",
343-
"df_Vnf[8:]"
352+
" ct = i*ngm # extract start point. Since input dataframe is sorted by R and theta, \n",
353+
" f = df_inp[2][ct:ct+ngm] # potentials (V) are extracting for each R value at a time \n",
354+
" V_n1 = A_inv.dot(f) # A-inv * V gives Radial coefficients\n",
355+
" V_nf[i,:] = V_n1 # radial coefficients stored in 2D matrix\n",
356+
"a12 = np.arange(lm) # creates header for lambda terms\n",
357+
"df_Vnf = pd.DataFrame(V_nf, columns = a12*sym) # saves final matrix into dataframe with appropriate header\n",
358+
"df_Vnf[8:] # prints first 8 terms"
344359
]
345360
},
346361
{
@@ -361,27 +376,9 @@
361376
}
362377
],
363378
"source": [
364-
"min(df_Vnf[0])"
365-
]
366-
},
367-
{
368-
"cell_type": "code",
369-
"execution_count": 186,
370-
"id": "4a3b7cb3",
371-
"metadata": {},
372-
"outputs": [],
373-
"source": [
374-
"#df_Vnf = df_Vnf[8:][:]"
379+
"min(df_Vnf[0]) # prints minima for isotropic term"
375380
]
376381
},
377-
{
378-
"cell_type": "code",
379-
"execution_count": null,
380-
"id": "301d19e0",
381-
"metadata": {},
382-
"outputs": [],
383-
"source": []
384-
},
385382
{
386383
"cell_type": "code",
387384
"execution_count": 190,
@@ -400,7 +397,7 @@
400397
}
401398
],
402399
"source": [
403-
"x_dummy = np.arange(2.5,10.5,0.1)\n",
400+
"x_dummy = np.arange(2.5,10.5,0.1) # Radial coordinates for plotting data\n",
404401
"len(x_dummy)"
405402
]
406403
},
@@ -424,7 +421,7 @@
424421
}
425422
],
426423
"source": [
427-
"#Spline100\n",
424+
"# Plot raw data\n",
428425
"for i in range(0,lm):\n",
429426
" y_dummy = df_Vnf[i*2]\n",
430427
" # Plot the noisy exponential data\n",
@@ -442,8 +439,7 @@
442439
"metadata": {},
443440
"outputs": [],
444441
"source": [
445-
"# saving 2 datasets 1 from 2.5 and other from 3.3\n",
446-
"# add data points to check for wobbes/kinks\n",
442+
"# adding more data points to radial terms for extrapolation\n",
447443
"x_22=np.arange(2,2.5,0.1)\n",
448444
"x_3=np.append(x_22,x_dummy)\n",
449445
"x_2=np.array([12.5,12.6,12.7,13,15,20,50])\n",
@@ -457,8 +453,10 @@
457453
"metadata": {},
458454
"outputs": [],
459455
"source": [
456+
"# function to fit V_lambdas into series of slater functions A*exp(B*(-x))\n",
457+
"# 2 terms may underfit, 3 is enough (search good coefficients for B), 4 may overfit the data\n",
460458
"from scipy.optimize import curve_fit\n",
461-
"a,b,c,d,e,f,rmsx = np.zeros(20),np.zeros(20),np.zeros(20),np.zeros(20),np.zeros(20),np.zeros(20),np.zeros(20)\n",
459+
"a,b,c,d,e,f,rmsx = np.zeros(lm),np.zeros(lm),np.zeros(lm),np.zeros(lm),np.zeros(lm),np.zeros(lm),np.zeros(lm)\n",
462460
"def exp_fit(x, a,b,c):\n",
463461
" return a*np.exp(-1*x)+b*np.exp(-2*x)+c*np.exp(-3*x)#+ \\\n",
464462
" #d*np.exp(-4*x)#+e*np.exp(-5*x)+f*np.exp(-6*x)"
@@ -500,27 +498,33 @@
500498
}
501499
],
502500
"source": [
501+
"# change x and y limits as needed!\n",
502+
"x_i, x_f = -15, 10\n",
503+
"y_i, y_f = 2.5, 8\n",
504+
"# select inital range for analytical fit (leave high energy points for beter fit)!\n",
505+
"ini_val = 21 # starting point for fitting into function\n",
503506
"\n",
504-
"for i in range(0,lm):\n",
505-
" j=int(i)\n",
506-
" y_dummy = df_Vnf[j*2]\n",
507-
" parsx, covx = curve_fit(f=exp_fit, xdata=x_dummy[21:], ydata=y_dummy[21:], p0=[0,0,1000])\n",
507+
"# Fitting data into analytic function and plotting for visualization\n",
508+
"for j in range(0,lm):\n",
509+
" y_dummy = df_Vnf[j*sym]\n",
510+
" parsx, covx = curve_fit(f=exp_fit, xdata=x_dummy[ini_val:], ydata=y_dummy[ini_val:], p0=[0,0,1000])\n",
508511
" a[j],b[j],c[j] = parsx\n",
509512
" print(\"[a b c] coefficients: \", parsx)\n",
510-
" # Plot the fit data as an overlay on the scatter data\n",
513+
" # Scatter plot for radial coefficients (raw data points)\n",
511514
" plt.scatter(x_dummy, y_dummy,s=20, color='#00b3b3',label = 'no fit')\n",
515+
" # fitted curve (extended R range) as an overlay on the scatter (raw) data\n",
512516
" plt.plot(x_3, exp_fit(x_3, *parsx), linestyle='-.', linewidth=2, color='black', label = 'exp fit')\n",
513517
" plt.legend(loc=\"upper right\")\n",
514518
" plt.ylabel(\"Energy (cm^(-1))\")\n",
515519
" plt.xlabel(\"R (Ang)\")\n",
516520
" plt.axhline(y=0, color='grey', linestyle=':')\n",
517-
" plt.title(\"V_lambda = %d\" %(i))\n",
518-
" plt.ylim(-15, 10)\n",
519-
" plt.xlim(2.5, 8)\n",
521+
" plt.title(\"V_lambda = %d\" %(j*sym))\n",
522+
" plt.ylim(x_i, x_f)\n",
523+
" plt.xlim(y_i, y_f)\n",
520524
" plt.show()\n",
521-
" print('Double exponential RMSE = ',np.sqrt(np.average(np.power((exp_fit(x_dummy[21:], *parsx) \n",
522-
" - y_dummy[21:]),2))))\n",
523-
" rmsx[j]=np.sqrt(np.average(np.power((exp_fit(x_dummy[21:], *parsx) - y_dummy[21:]),2)))"
525+
" print('Double exponential RMSE = ',np.sqrt(np.average(np.power((exp_fit(x_dummy[ini_val:], *parsx) \n",
526+
" - y_dummy[ini_val:]),2))))\n",
527+
" rmsx[j]=np.sqrt(np.average(np.power((exp_fit(x_dummy[ini_val:], *parsx) - y_dummy[ini_val:]),2)))"
524528
]
525529
},
526530
{
@@ -552,13 +556,12 @@
552556
}
553557
],
554558
"source": [
555-
"#spline100 data\n",
556559
"# save output for each V lambdas as required by molscat!\n",
557560
"print('LAMBDA = 0,2,4,6,8,10,12,14,16,18,')\n",
558561
"print('NTERM = ', '3,'*10)\n",
559562
"print('NPOWER = ', '0,'*30)\n",
560563
"print('A = ')\n",
561-
"for j in range (10):\n",
564+
"for j in range (lm):\n",
562565
" print(a[j],',',b[j],',',c[j],',')\n",
563566
"print('E =', '-1,-2,-3,'*10)"
564567
]

0 commit comments

Comments
 (0)