|
27 | 27 | "metadata": {},
|
28 | 28 | "outputs": [],
|
29 | 29 | "source": [
|
| 30 | + "# Import important libraries\n", |
30 | 31 | "import math\n",
|
31 | 32 | "import numpy as np\n",
|
32 | 33 | "import pandas as pd\n",
|
33 | 34 | "from scipy.special import legendre\n",
|
34 | 35 | "import matplotlib.pyplot as plt"
|
35 | 36 | ]
|
36 | 37 | },
|
| 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 | + }, |
37 | 47 | {
|
38 | 48 | "cell_type": "code",
|
39 | 49 | "execution_count": 164,
|
|
42 | 52 | "outputs": [],
|
43 | 53 | "source": [
|
44 | 54 | "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 " |
47 | 56 | ]
|
48 | 57 | },
|
49 | 58 | {
|
|
53 | 62 | "metadata": {},
|
54 | 63 | "outputs": [],
|
55 | 64 | "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" |
59 | 69 | ]
|
60 | 70 | },
|
61 | 71 | {
|
|
65 | 75 | "metadata": {},
|
66 | 76 | "outputs": [],
|
67 | 77 | "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)" |
75 | 85 | ]
|
76 | 86 | },
|
77 | 87 | {
|
|
81 | 91 | "metadata": {},
|
82 | 92 | "outputs": [],
|
83 | 93 | "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" |
86 | 101 | ]
|
87 | 102 | },
|
88 | 103 | {
|
|
92 | 107 | "metadata": {},
|
93 | 108 | "outputs": [],
|
94 | 109 | "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)" |
100 | 115 | ]
|
101 | 116 | },
|
102 | 117 | {
|
|
106 | 121 | "metadata": {},
|
107 | 122 | "outputs": [],
|
108 | 123 | "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)" |
110 | 125 | ]
|
111 | 126 | },
|
112 | 127 | {
|
|
334 | 349 | ],
|
335 | 350 | "source": [
|
336 | 351 | "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" |
344 | 359 | ]
|
345 | 360 | },
|
346 | 361 | {
|
|
361 | 376 | }
|
362 | 377 | ],
|
363 | 378 | "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" |
375 | 380 | ]
|
376 | 381 | },
|
377 |
| - { |
378 |
| - "cell_type": "code", |
379 |
| - "execution_count": null, |
380 |
| - "id": "301d19e0", |
381 |
| - "metadata": {}, |
382 |
| - "outputs": [], |
383 |
| - "source": [] |
384 |
| - }, |
385 | 382 | {
|
386 | 383 | "cell_type": "code",
|
387 | 384 | "execution_count": 190,
|
|
400 | 397 | }
|
401 | 398 | ],
|
402 | 399 | "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", |
404 | 401 | "len(x_dummy)"
|
405 | 402 | ]
|
406 | 403 | },
|
|
424 | 421 | }
|
425 | 422 | ],
|
426 | 423 | "source": [
|
427 |
| - "#Spline100\n", |
| 424 | + "# Plot raw data\n", |
428 | 425 | "for i in range(0,lm):\n",
|
429 | 426 | " y_dummy = df_Vnf[i*2]\n",
|
430 | 427 | " # Plot the noisy exponential data\n",
|
|
442 | 439 | "metadata": {},
|
443 | 440 | "outputs": [],
|
444 | 441 | "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", |
447 | 443 | "x_22=np.arange(2,2.5,0.1)\n",
|
448 | 444 | "x_3=np.append(x_22,x_dummy)\n",
|
449 | 445 | "x_2=np.array([12.5,12.6,12.7,13,15,20,50])\n",
|
|
457 | 453 | "metadata": {},
|
458 | 454 | "outputs": [],
|
459 | 455 | "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", |
460 | 458 | "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", |
462 | 460 | "def exp_fit(x, a,b,c):\n",
|
463 | 461 | " return a*np.exp(-1*x)+b*np.exp(-2*x)+c*np.exp(-3*x)#+ \\\n",
|
464 | 462 | " #d*np.exp(-4*x)#+e*np.exp(-5*x)+f*np.exp(-6*x)"
|
|
500 | 498 | }
|
501 | 499 | ],
|
502 | 500 | "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", |
503 | 506 | "\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", |
508 | 511 | " a[j],b[j],c[j] = parsx\n",
|
509 | 512 | " 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", |
511 | 514 | " 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", |
512 | 516 | " plt.plot(x_3, exp_fit(x_3, *parsx), linestyle='-.', linewidth=2, color='black', label = 'exp fit')\n",
|
513 | 517 | " plt.legend(loc=\"upper right\")\n",
|
514 | 518 | " plt.ylabel(\"Energy (cm^(-1))\")\n",
|
515 | 519 | " plt.xlabel(\"R (Ang)\")\n",
|
516 | 520 | " 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", |
520 | 524 | " 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)))" |
524 | 528 | ]
|
525 | 529 | },
|
526 | 530 | {
|
|
552 | 556 | }
|
553 | 557 | ],
|
554 | 558 | "source": [
|
555 |
| - "#spline100 data\n", |
556 | 559 | "# save output for each V lambdas as required by molscat!\n",
|
557 | 560 | "print('LAMBDA = 0,2,4,6,8,10,12,14,16,18,')\n",
|
558 | 561 | "print('NTERM = ', '3,'*10)\n",
|
559 | 562 | "print('NPOWER = ', '0,'*30)\n",
|
560 | 563 | "print('A = ')\n",
|
561 |
| - "for j in range (10):\n", |
| 564 | + "for j in range (lm):\n", |
562 | 565 | " print(a[j],',',b[j],',',c[j],',')\n",
|
563 | 566 | "print('E =', '-1,-2,-3,'*10)"
|
564 | 567 | ]
|
|
0 commit comments