|
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