def run_harOH_DVR(self, plotPhasedWfns=False): """ Runs a harmonic approximation using DVR to be fast over the OH coordinate at every OO value.""" from McUtils.Zachary import finite_difference dvr_1D = DVR("ColbertMiller1D") finite_dict = self.logData.finite_dict(midpoint=True) roos = np.array(list(finite_dict.keys())) potential_array = np.zeros((len(finite_dict), self.NumPts, 2)) energies_array = np.zeros((len(finite_dict), self.desiredEnergies)) wavefunctions_array = np.zeros( (len(finite_dict), self.NumPts, self.desiredEnergies)) for j, n in enumerate(finite_dict): x = Constants.convert(finite_dict[n][:, 0], "angstroms", to_AU=True) min_idx = np.argmin(finite_dict[n][:, 1]) sx = x - x[min_idx] y = finite_dict[n][:, 1] k = finite_difference(sx, y, 2, end_point_precision=0, stencil=5, only_center=True)[0] mini = min(sx) - 1.0 maxi = max(sx) + 1.0 res = dvr_1D.run(potential_function="harmonic_oscillator", k=k, mass=self.massdict["muOOH"], divs=self.NumPts, domain=(mini, maxi), num_wfns=self.desiredEnergies) potential = Constants.convert(res.potential_energy.diagonal(), "wavenumbers", to_AU=False) grid = Constants.convert((res.grid + x[min_idx]), "angstroms", to_AU=False) shiftgrid = (n / 2) + grid potential_array[j, :, 0] = shiftgrid potential_array[j, :, 1] = potential ens = Constants.convert((res.wavefunctions.energies + min(y)), "wavenumbers", to_AU=False) energies_array[j, :] = ens wavefunctions_array[j, :, :] = res.wavefunctions.wavefunctions epsilon_pots = np.column_stack((roos, energies_array[:, :4])) npz_filename = os.path.join( self.DVRdir, f"{self.method}_HarmOHDVR_energies{self.desiredEnergies}.npz") # data saved in wavenumbers/angstroms wavefuns_array = self.wfn_flipper(wavefunctions_array, plotPhasedWfns=plotPhasedWfns, pot_array=potential_array) np.savez(npz_filename, method="harm", potential=potential_array, epsilonPots=epsilon_pots, wfns_array=wavefuns_array) return npz_filename
def calc_coefs(sys): udrive = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) mainD = os.path.join(udrive, sys) dips = np.load(os.path.join(mainD, "structures", f"FD{sys}_rotdips2021_test.npy")) molObj, gaussdat = pull_data(sys) scancoords = np.array(list(gaussdat.cartesians.keys())) sort_ind = np.lexsort((scancoords[:, 1], scancoords[:, 0])) sort_grid = scancoords[sort_ind, :] sort_dips = dips[sort_ind, :] fd_ohs = Constants.convert(np.unique(sort_grid[:, 1]), "angstroms", to_AU=True) fd_oos = Constants.convert(np.unique(sort_grid[:, 0]), "angstroms", to_AU=True) FDgrid = np.array(np.meshgrid(fd_oos, fd_ohs)).T FDvaluesx = np.reshape(sort_dips[:, 0], (5, 5)) FDvaluesy = np.reshape(sort_dips[:, 1], (5, 5)) FDvaluesz = np.reshape(sort_dips[:, 2], (5, 5)) eqDipole = np.array((FDvaluesx[2, 2], FDvaluesy[2, 2], FDvaluesz[2, 2])) xderivs = calc_derivs(fd_ohs, fd_oos, FDgrid, FDvaluesx) yderivs = calc_derivs(fd_ohs, fd_oos, FDgrid, FDvaluesy) zderivs = calc_derivs(fd_ohs, fd_oos, FDgrid, FDvaluesz) derivs = {'x': xderivs, 'y': yderivs, 'z': zderivs} fn = os.path.join(mainD, "Finite Scan Data", f"DipCoefs{sys}_smallscan.npz") np.savez(fn, x=xderivs, y=yderivs, z=zderivs, eqDip=eqDipole) print("eqDipole:", eqDipole) return derivs
def printFreqs(self): from Converter import Constants Frr = self.force_constants[0] FRR = self.force_constants[1] print( "OO:", Constants.convert(np.sqrt(FRR / self.massdict["muOO"]), "wavenumbers", to_AU=False)) print( "OH:", Constants.convert(np.sqrt(Frr / self.massdict["muOH"]), "wavenumbers", to_AU=False)) print( "OOH:", Constants.convert(np.sqrt(Frr / self.massdict["muXH"]), "wavenumbers", to_AU=False)) print( "ZPE:", Constants.convert((np.sqrt(FRR / self.massdict["muOO"])) / 2 + (np.sqrt(Frr / self.massdict["muXH"])) / 2, "wavenumbers", to_AU=False))
def interp_2D_dipoles(self): from functools import reduce from operator import mul from Converter import Constants from MolecularSys import MolecularOperations from scipy.interpolate import griddata # in bohr oos = MolecularOperations(self.molecule).calculateBonds( self.embeddedCoords, *self.scanCoords[0]) ohs = MolecularOperations(self.molecule).calculateBonds( self.embeddedCoords, *self.scanCoords[1]) MrOH = (oos / 2) - ohs MrOH = np.around(MrOH, 4) dip_vecs = self.embeddedDips if self.dimension == "2D": bigGrid = Constants.convert(self.TwoDres["grid"][0], "angstroms", to_AU=True) npts = reduce(mul, bigGrid.shape[:-1], 1) grid = np.reshape(bigGrid, (npts, bigGrid.shape[-1])) new_dips = np.zeros((npts, 3)) for j in np.arange(3): if self.delta: Doos = oos - Constants.convert( self.molecule.OOmin, "angstroms", to_AU=True) Dxhs = MrOH - Constants.convert( self.molecule.XHmin, "angstroms", to_AU=True) new_dips[:, j] = griddata(np.column_stack((Doos, Dxhs)), dip_vecs[:, j], grid, method="cubic", fill_value=np.min(dip_vecs[:, j])) else: new_dips[:, j] = griddata(np.column_stack((oos, MrOH)), dip_vecs[:, j], grid, method="cubic", fill_value=np.min(dip_vecs[:, j])) else: # this should interpolate grid to 1D DVR # of MrOHs x PES # of OOs (500 x 35) potz = self.OHDVRres["potential"] rxh = Constants.convert(np.unique(potz[:, :, 0]), "angstroms", to_AU=True) cut_dict = self.logData.cut_dictionary() roo = Constants.convert(np.array(list(cut_dict.keys())), "angstroms", to_AU=True) new_grid = np.meshgrid(roo, rxh, indexing="ij") grid = np.column_stack( (new_grid[0].flatten(), new_grid[1].flatten())) new_dips = np.zeros((len(grid), 3)) for j in np.arange(3): new_dips[:, j] = griddata(np.column_stack((oos, MrOH)), dip_vecs[:, j], grid, method="cubic", fill_value=np.min(dip_vecs[:, j])) return grid, new_dips # bohr & debye
def make_Vel_plots(mol_res_obj): Vel = mol_res_obj.RxnPathResults["electronicE"][:, 1] Vel -= min(Vel) degrees = mol_res_obj.RxnPathResults["electronicE"][:, 0] plt.plot(degrees, Constants.convert(Vel, "wavenumbers", to_AU=False), "o", label="Vel") Vel_coeffs = fourier_coeffs(np.column_stack((np.radians(degrees), Vel)), sin_order=6, cos_order=6) Vel_fit = calc_curves(np.radians(np.arange(0, 360, 1)), Vel_coeffs, function="fourier") Vel_res = np.column_stack( (np.arange(0, 360, 1), Constants.convert(Vel_fit, "wavenumbers", to_AU=False))) max_arg1 = np.argmax(Vel_res[100:270, 1]) print("Vel", Vel_res[max_arg1 + 100, :]) plt.plot(Vel_res[:, 0], Vel_res[:, 1], "-b") Vel_ZPE = mol_res_obj.VelCoeffs velZPE = calc_curves(np.radians(np.arange(0, 360, 1)), Vel_ZPE, function="fourier") res = np.column_stack( (np.arange(0, 360, 1), Constants.convert(velZPE, "wavenumbers", to_AU=False))) max_arg = np.argmax(res[100:270, 1]) print("Vel+ZPE", res[max_arg + 100, :]) plt.plot(res[:, 0], res[:, 1], label="Vel+ZPE") plt.legend() plt.show()
def run_tor_adiabats(self): from TorsionPOR import POR from Converter import Constants if "EmilData" in self.PORparams or "MixedData2" in self.PORparams: file_name = os.path.join(self.MoleculeInfo.MoleculeDir, "Emil_vOHenergies.txt") self.PORparams["EmilEnergies"] = np.loadtxt(file_name, skiprows=1) PORobj = POR(DVR_res=self.DegreeDVRresults, fit_gmatrix=self.fittedGmatrix, Velcoeffs=self.VelCoeffs, params=self.PORparams) results = PORobj.solveHam( ) # returns a list of dictionaries for each torsional potential if "PrintResults" in self.PORparams: for i in np.arange(len(results)): bh = Constants.convert(results[i]["barrier"], "wavenumbers", to_AU=False) print(f"Barrier Height : {bh} cm^-1") energy = results[i]["energy"] ens = Constants.convert(energy, "wavenumbers", to_AU=False) print( f"vOH = {i} : E0+ : {ens[0]} E0- : {ens[1]} / {ens[1]-ens[0]:.3f} \n" f" E1+ : {ens[2]} / {ens[2]-ens[0]:.3f} E1- : {ens[3]} / {ens[3]-ens[0]:.3f} \n" f" E2+ : {ens[4]} / {ens[4]-ens[0]:.3f} E2- : {ens[5]} / {ens[5]-ens[0]:.3f} \n" f" E3+ : {ens[6]} / {ens[6]-ens[0]:.3f} E3- : {ens[7]} / {ens[7]-ens[0]:.3f} \n" ) wfns = PORobj.PORwfns( ) # returns a list of arrays with each wfn for each torsional potential return results, wfns
def run_2D_DVR(self): """Runs 2D DVR over the original 2D potential should take flat xy array, and flat 2D grid in ATOMIC UNITS""" from PyDVR import DVR, ResultsInterpreter from Converter import Constants import os dvr_2D = DVR("ColbertMillerND") xy = np.column_stack((self.grid[0].flatten(), self.grid[1].flatten())) ens = self.ModelHarmonicPotential().flatten() res = dvr_2D.run(potential_grid=np.column_stack((xy, ens)), divs=(100, 100), mass=[self.massdict["muOO"], self.massdict["muOH"]], num_wfns=15, domain=((min(xy[:, 0]), max(xy[:, 0])), (min(xy[:, 1]), max(xy[:, 1]))), results_class=ResultsInterpreter) dvr_grid = Constants.convert(res.grid, "angstroms", to_AU=False) dvr_pot = Constants.convert(res.potential_energy.diagonal(), "wavenumbers", to_AU=False) all_ens = Constants.convert(res.wavefunctions.energies, "wavenumbers", to_AU=False) ResultsInterpreter.wfn_contours(res) oh1oo0 = int(input("OH=1 OO=0 Wavefunction Index: ")) oh1oo1 = int(input("OH=1 OO=1 Wavefunction Index: ")) oh1oo2 = int(input("OH=1 OO=2 Wavefunction Index: ")) ens = np.zeros(4) wfns = np.zeros((4, res.wavefunctions[0].data.shape[0])) for i, wf in enumerate(res.wavefunctions): wfn = wf.data if i == 0: wfns[0] = wfn ens[0] = all_ens[i] elif i == oh1oo0: wfns[1] = wfn ens[1] = all_ens[i] elif i == oh1oo1: wfns[2] = wfn ens[2] = all_ens[i] elif i == oh1oo2: wfns[3] = wfn ens[3] = all_ens[i] else: pass # data saved in wavenumbers/angstrom dvr_dir = os.path.join(self.molecule.mol_dir, "DVR Results") if self.CC: npz_filename = f"{dvr_dir}/HMP_wCC_2D_DVR_OHOO.npz" else: npz_filename = f"{dvr_dir}/HMP_2D_DVR_OHOO.npz" np.savez(npz_filename, grid=[dvr_grid], potential=[dvr_pot], vrwfn_idx=[0, oh1oo0, oh1oo1, oh1oo2], energy_array=ens, wfns_array=wfns) return npz_filename
def run_harOO_DVR(self, OHDVRres=None, plotPhasedWfns=False): """Fits epsilon potentials to a harmonic oscillator and then runs an OO DVR over it""" from McUtils.Zachary import finite_difference dvr_1D = DVR("ColbertMiller1D") OHresults = np.load(OHDVRres) epsi_pots = OHresults["epsilonPots"] potential_array = np.zeros((2, self.NumPts, 2)) energies_array = np.zeros((2, self.desiredEnergies)) wavefunctions_array = np.zeros((2, self.NumPts, self.desiredEnergies)) x = Constants.convert(epsi_pots[:, 0], "angstroms", to_AU=True) for j in np.arange(2): en = Constants.convert(epsi_pots[:, j + 1], "wavenumbers", to_AU=True) minE_idx = np.argmin(en) en_s = en[minE_idx - 2:minE_idx + 3] sx = x - x[minE_idx] k = finite_difference(sx[minE_idx - 2:minE_idx + 3], en_s, 2, end_point_precision=0, stencil=5, only_center=True)[0] mini = min(sx) - 1.0 maxi = max(sx) + 0.5 res = dvr_1D.run(potential_function="harmonic_oscillator", k=k, mass=self.massdict["muOO"], divs=self.NumPts, domain=(mini, maxi), num_wfns=self.desiredEnergies) potential = Constants.convert(res.potential_energy.diagonal() + en[minE_idx], "wavenumbers", to_AU=False) potential_array[j, :, 0] = Constants.convert( (res.grid + x[minE_idx]), "angstroms", to_AU=False) potential_array[j, :, 1] = potential ens = Constants.convert(res.wavefunctions.energies + en[minE_idx], "wavenumbers", to_AU=False) energies_array[j, :] = ens wavefunctions_array[j, :, :] = res.wavefunctions.wavefunctions npz_filename = os.path.join( self.DVRdir, f"{self.method}_harmOODVR_w{OHresults['method']}OHDVR_energies{self.desiredEnergies}.npz" ) # data saved in wavenumbers/angstroms wavefuns_array = self.wfn_flipper(wavefunctions_array, plotPhasedWfns=plotPhasedWfns, pot_array=potential_array) np.savez(npz_filename, potential=potential_array, energy_array=energies_array, wfns_array=wavefuns_array) return npz_filename
def make_PotWfnplots(ResDict, wfn_idx=[0, 1], ZPE=True, filename=None): """ Plot the potential curves and energy levels of the given transitions. If ZPE plots include the ZPE, else they are plotted with the ZPE subtracted off so that min(gsPot) = 0 """ from FourierExpansions import calc_curves fig = plt.figure(figsize=(7, 8), dpi=600) x = np.linspace(0, 360, len(ResDict["eigvecs"][:, 0])) rad_x = np.radians(x) # create gs plot if len(ResDict["V"]) < 7: ResDict["V"] = np.hstack( (ResDict["V"], np.zeros(7 - len(ResDict["V"])))) gsPot = Constants.convert(calc_curves(rad_x, ResDict["V"], function="fourier"), "wavenumbers", to_AU=False) colors = ["b", "r", "g", "indigo", "teal", "mediumvioletred"] for i in wfn_idx: en0g = Constants.convert(ResDict['energy'][i], "wavenumbers", to_AU=False) if ZPE is False: en0g -= min(gsPot) wfn0g = ResDict["eigvecs"][:, i] if wfn0g[150] < wfn0g[0]: wfn0g *= -1 plt_wfn0g = en0g + wfn0g * 500 # for aestheics only (on all wfn) plt.plot(x, np.repeat(en0g, len(x)), "-k", linewidth=2) plt.plot(x, plt_wfn0g, color=colors[i], linewidth=3) # en1g = Constants.convert(ResDict['energy'][tunnel_pair[1]], "wavenumbers", to_AU=False) # if ZPE is False: # en1g -= min(gsPot) # wfn1g = ResDict["eigvecs"][:, tunnel_pair[1]] # if wfn1g[21] < wfn0g[0]: # wfn1g *= -1 # plt_wfn1g = en1g + ResDict["eigvecs"][:, tunnel_pair[1]] * 100 # plt.plot(x, np.repeat(en1g, len(x)), "-k", linewidth=2) # plt.plot(x, plt_wfn1g, color=colors[tunnel_pair[1]], linewidth=3) if ZPE is False: gsPot -= min(gsPot) plt.plot(x, gsPot, "-k", linewidth=3) plt.xticks(np.arange(0, 390, 60)) plt.xlabel(r"$\tau$ (Degrees)") plt.ylim(min(gsPot) - 20, min(gsPot) + 1020) plt.ylabel(r"Energy (cm$^{-1}$)", labelpad=15.0) if filename is None: plt.show() else: fig.savefig(f"{filename}.jpg", dpi=fig.dpi, bbox_inches="tight") print(f"Figure saved to {filename}") plt.close()
def componentTMs(self, mark_pts, ylim=None, xlim=None): comp = ["X", "Y", "Z"] x = Constants.convert(self.tmObj.mus[0]["dipSurf"][:, 0, 0], "angstroms", to_AU=False) mus = self.tmObj.mus[1]["dipSurf"] bigGrid = Constants.convert(self.tmObj.tdms[0], "angstroms", to_AU=False) exMus = self.tmObj.tdms[1] labelNames = { "dipSurf": "Full", "quadOH": "Quadratic", "linOH": "Linear" } # colors = ["darkmagenta", "mediumslateblue", "mediumblue"] colors = ["C4", "C1", "C2"] ls = ["-.", "-", "--"] fig = plt.figure(figsize=(5, 5.5), dpi=600) for j, v in enumerate(comp): if j == 0: # make only the x-component for i, t in enumerate(labelNames.keys()): plt.plot(bigGrid, exMus[t][:, j], color=colors[i], linestyle=ls[i], label=labelNames[t], linewidth=2.0) for l, pt in enumerate(x): if pt == mark_pts[0] or pt == mark_pts[ 1] or pt == mark_pts[2]: plt.plot(pt, mus[l, j], "o", color="red", fillstyle="none") else: plt.plot(pt, mus[l, j], 'ok') if ylim is not None: plt.ylim(*ylim) if xlim is not None: plt.xlim(*xlim) plt.title(f"{v} Component TDM", size=18) plt.legend(fontsize=14, frameon=False) plt.xlabel("$\mathrm{R_{OO}}$ ($\mathrm{\AA}$)", size=16) plt.ylabel("Transition Dipole Moment (Debye)", size=16) plt.tight_layout() plt.savefig( f"{self.fig_dir}/{self.molecule.MoleculeName}_{self.molecule.method}_{v}componentTDM_wNMarkers.png", dpi=fig.dpi, bbox_inches="tight") plt.close() else: pass
def find_FancyF(FD_file, woo=None, woh=None): from Converter import Constants from McUtils.Zachary import finite_difference mO = Constants.mass("O", to_AU=True) mH = Constants.mass("H", to_AU=True) freqoo = Constants.convert(woo, "wavenumbers", to_AU=True) muOO = mO / 2 freqoh = Constants.convert(woh, "wavenumbers", to_AU=True) muOH = ((2 * mO) * mH) / ((2 * mO) + mH) finite_vals = np.loadtxt(FD_file, skiprows=7) finite_vals = finite_vals[:, 2:] finite_vals[:, 0] *= 2 ens = finite_vals[:, 2] print( "Energy Difference from Minimum: ", Constants.convert(finite_vals[:, 2] - min(finite_vals[:, 2]), "wavenumbers", to_AU=False)) ohDiffs = np.array([ ens[1] - ens[0], ens[6] - ens[5], ens[11] - ens[10], ens[16] - ens[15], ens[21] - ens[20] ]) print("OH energy differences: ", Constants.convert(ohDiffs, "wavenumbers", to_AU=False)) finite_vals[:, :2] = Constants.convert( finite_vals[:, :2], "angstroms", to_AU=True) # convert to bohr for math idx = np.lexsort( (finite_vals[:, 0], finite_vals[:, 1])) # resort so same oh different oo finite_vals = finite_vals[idx] finite_vals = finite_vals.reshape((5, 5, 3)) FR = np.zeros(5) # compute first derivative wrt oo FR for j in range(finite_vals.shape[0]): x = finite_vals[j, :, 0] # roos y = finite_vals[j, :, 2] # energies print("OO energy difference: ", Constants.convert((y[1] - y[0]), "wavenumbers", to_AU=False)) FR[j] = finite_difference(x, y, 1, end_point_precision=0, stencil=5, only_center=True)[0] print(f"FR: {FR}") # compute mixed derivative FrrR FrrR = finite_difference(finite_vals[:, 1, 1], FR, 2, end_point_precision=0, stencil=5, only_center=True)[0] print(f"FrrR: {FrrR}") Qoo = np.sqrt(1 / muOO / freqoo) Qoh = np.sqrt(1 / muOH / freqoh) fancyF = FrrR * Qoh**2 * Qoo return Constants.convert(fancyF, "wavenumbers", to_AU=False)
def get_reducedmass(self): from Converter import Constants mO = Constants.mass("O", to_AU=True) mH = Constants.mass("H", to_AU=True) massdict = dict() muXH = ((2 * mO) * mH) / ((2 * mO) + mH) massdict["muXH"] = muXH muOH = 1 / (1 / mO + 1 / mH) massdict["muOH"] = muOH muOO = mO / 2 massdict["muOO"] = muOO return massdict
def NormModeFreqs(fchk_names): """Calculate the normal mode frequencies""" from NormalModes import run data = DataClass(fchk_names) hess = data.hessian mass = Constants.convert(data.atomicmasses, "amu", to_AU=True) numCoords = 3 * len(mass) resAU = run(hess, numCoords, mass) freqsAU = np.sqrt(resAU["freq2"]) freqs = Constants.convert(freqsAU, "wavenumbers", to_AU=False) # print(freqs) return freqs
def run_2D_DVR(self): """Runs 2D DVR over the original 2D potential""" dvr_2D = DVR("ColbertMillerND") npz_filename = os.path.join(self.DVRdir, f"{self.method}_2D_DVR.npz") twoD_grid = self.logData.rawenergies xy = Constants.convert(twoD_grid[:, :2], "angstroms", to_AU=True) en = twoD_grid[:, 2] en[en > 0.228] = 0.228 # set stricter limit res = dvr_2D.run(potential_grid=np.column_stack((xy, twoD_grid[:, 2])), divs=(100, 100), mass=[self.massdict["muOO"], self.massdict["muOOH"]], num_wfns=15, domain=((min(xy[:, 0]), max(xy[:, 0])), (min(xy[:, 1]), max(xy[:, 1]))), results_class=ResultsInterpreter) dvr_grid = Constants.convert(res.grid, "angstroms", to_AU=False) dvr_pot = Constants.convert(res.potential_energy.diagonal(), "wavenumbers", to_AU=False) all_ens = Constants.convert(res.wavefunctions.energies, "wavenumbers", to_AU=False) ResultsInterpreter.wfn_contours(res) oh1oo0 = int(input("OH=1 OO=0 Wavefunction Index: ")) oh1oo1 = int(input("OH=1 OO=1 Wavefunction Index: ")) oh1oo2 = int(input("OH=1 OO=2 Wavefunction Index: ")) ens = np.zeros(4) wfns = np.zeros((4, res.wavefunctions[0].data.shape[0])) for i, wf in enumerate(res.wavefunctions): wfn = wf.data if i == 0: wfns[0] = wfn ens[0] = all_ens[i] elif i == oh1oo0: wfns[1] = wfn ens[1] = all_ens[i] elif i == oh1oo1: wfns[2] = wfn ens[2] = all_ens[i] elif i == oh1oo2: wfns[3] = wfn ens[3] = all_ens[i] else: pass # data saved in wavenumbers/angstroms np.savez(npz_filename, grid=[dvr_grid], potential=[dvr_pot], vrwfn_idx=[0, oh1oo0, oh1oo1, oh1oo2], energy_array=ens, wfns_array=wfns) return npz_filename
def plot_g_matrix(mol_res_obj): gmats = mol_res_obj.Gmatrix[0] for level in np.arange(gmats.shape[0]): plt.plot(gmats[level, :, 0], Constants.convert(gmats[level, :, 1], "wavenumbers", to_AU=False), label=f"vOH = {level}") new_a = np.delete(mol_res_obj.Gmatrix[1], 18, 0) plt.plot(new_a[:, 0], Constants.convert(new_a[:, 1], "wavenumbers", to_AU=False), "--m", label="EQ G Matrix") plt.xticks(np.arange(0, 390, 30)) plt.xlabel("Torsion Angle (degrees)") plt.ylabel(r"G-Matrix Element ($cm^{-1}$)") plt.legend()
def get_reducedmass(self): mO = Constants.mass("O", to_AU=True) mH = Constants.mass("H", to_AU=True) mD = Constants.mass("D", to_AU=True) massdict = dict() muOOH = ((2 * mO) * mH) / ((2 * mO) + mH) massdict["muOOH"] = muOOH muOOD = ((2 * mO) * mD) / ((2 * mO) + mD) massdict["muOOD"] = muOOD muOH = 1 / (1 / mO + 1 / mH) massdict["muOH"] = muOH muOO = mO / 2 massdict["muOO"] = muOO return massdict
def get_bonds_angles(tor_angles, internal_coords): # pulls in the bond lengths and angles needed for the g matrix value_dict = dict() # ultimately will be a nested dict value_dict[tor_angle][value] for i in tor_angles: internal_vals = dict() internal_coord = internal_coords[i] internal_vals["r12"] = Constants.convert(internal_coord["B6"][12], "angstroms", to_AU=True) # pulls eq_roh internal_vals["r23"] = Constants.convert(internal_coord["B5"], "angstroms", to_AU=True) internal_vals["r34"] = Constants.convert(internal_coord["B4"], "angstroms", to_AU=True) internal_vals["phi123"] = np.radians(internal_coord["A5"]) internal_vals["phi234"] = np.radians(internal_coord["A4"]) internal_vals["tau1234"] = np.radians(internal_coord["D4"]) value_dict[i] = internal_vals return value_dict # values in bohr/radians
def run_OO_DVR(self, OHDVRres=None, plotPhasedWfns=False): """Runs OO DVR over the epsilon potentials""" from PotentialHandlers import Potentials1D dvr_1D = DVR("ColbertMiller1D") OHresults = np.load(OHDVRres) epsi_pots = OHresults["epsilonPots"] potential_array = np.zeros((2, self.NumPts, 2)) energies_array = np.zeros((2, self.desiredEnergies)) wavefunctions_array = np.zeros((2, self.NumPts, self.desiredEnergies)) x = Constants.convert(epsi_pots[:, 0], "angstroms", to_AU=True) mini = min(x) - 0.3 maxi = max(x) + 0.15 for j in np.arange(2): en = Constants.convert(epsi_pots[:, j + 1], "wavenumbers", to_AU=True) res = dvr_1D.run(potential_function=Potentials1D().potlint(x, en), mass=self.massdict["muOO"], divs=self.NumPts, domain=(mini, maxi), num_wfns=self.desiredEnergies) potential = Constants.convert(res.potential_energy.diagonal(), "wavenumbers", to_AU=False) grid = Constants.convert(res.grid, "angstroms", to_AU=False) potential_array[j, :, 0] = grid potential_array[j, :, 1] = potential min_idx = np.argmin(potential) print(j, grid[min_idx], potential[min_idx]) ens = Constants.convert(res.wavefunctions.energies, "wavenumbers", to_AU=False) print(j, ens) energies_array[j, :] = ens wavefunctions_array[j, :, :] = res.wavefunctions.wavefunctions npz_filename = os.path.join( self.DVRdir, f"{self.method}_OODVR_w{OHresults['method']}OHDVR_energies{self.desiredEnergies}.npz" ) # data saved in wavenumbers/angstroms wavefuns_array = self.wfn_flipper(wavefunctions_array, plotPhasedWfns=plotPhasedWfns, pot_array=potential_array) np.savez(npz_filename, potential=potential_array, energy_array=energies_array, wfns_array=wavefuns_array) return npz_filename
def calc_stat_intensity(self, values=None): from FourierExpansions import calc_curves from collections import OrderedDict if values is None: values = np.arange(230, 280, 10) else: pass OH_0coefs = self.tor_results[0][0]["V"] all_intensities = OrderedDict() for Tstring in self.transition: # should be a list of strings TDM = self.calc_TDM(Tstring) degrees = np.linspace(0, 360, len(TDM)) OH_excoefs = self.tor_results[0][int(Tstring[-1])]["V"] intensity = np.zeros((len(values), 3)) for i, value in enumerate(values): idx = np.argwhere(degrees == value)[0][0] OH_0 = calc_curves(value, OH_0coefs) OH_ex = calc_curves(value, OH_excoefs) freq = OH_ex - OH_0 freq_wave = Constants.convert(freq, "wavenumbers", to_AU=False) print("\n") print(f"Stationary Frequency {Tstring}: {freq_wave}") for c, val in enumerate(["A", "B", "C"]): # loop through components intensity[i, c] = (abs( TDM[idx, c]))**2 * freq_wave * 2.506 / ( 0.393456**2) # convert to km/mol # print(f"Stationary Intensity @ {value} {val} : {intensity[i, c]}") # print(f"Total Intensity {value} : ", np.sum(intensity[i, :])) all_intensities[Tstring] = np.sum(intensity[i, :]) oStrength = np.sum(intensity[i, :]) / 5.33E6 print(f"Oscillator Strength @ {value} : {oStrength}") return all_intensities
def make_pot_comp_plot(fullporRes, filename=None): from FourierExpansions import calc_curves fig = plt.figure(figsize=(4, 4), dpi=600) x = np.linspace(0, 360, 100) rad_x = np.linspace(0, 2 * np.pi, 100) colors = [ "b", "r", "goldenrod", "indigo", "mediumseagreen", "darkturquoise" ] for i, porRes in enumerate(fullporRes): if len(porRes["V"]) < 7: porRes["V"] = np.hstack( (porRes["V"], np.zeros(7 - len(porRes["V"])))) Pot = Constants.convert(calc_curves(rad_x, porRes["V"]), "wavenumbers", to_AU=False) Pot -= min(Pot) plt.plot(x, Pot, color=colors[i], label=r"v$_\mathrm{OH}$ = % s" % i) plt.ylabel( r"$V_{\mathrm{v_{OH}}}^\mathrm{eff.}$($\tau$) - " r"$V_{\mathrm{v_{OH}}}^\mathrm{eff.}$($\tau_\mathrm{min}$)(cm$^{-1}$)") plt.xlabel(r"$\tau$ (Degrees)") plt.xticks(np.arange(0, 450, 90)) plt.xlim(0, 360) plt.legend() if filename is None: plt.show() else: fig.savefig(f"{filename}.jpg", dpi=fig.dpi, bbox_inches="tight") print(f"Figure saved to {filename}")
def getHarmonicPotOH(self, plotV=None): Frr = self.force_constants[0] FRR = self.force_constants[1] oh_HO = 1 / 2 * Frr * (self.grid[1]**2) OO_HO = 1 / 2 * FRR * (self.grid[0]**2) if self.CC: V_HO = OO_HO + oh_HO + (self.CubicCoupling * self.grid[1]**2 * self.grid[0]) else: V_HO = OO_HO + oh_HO if plotV is not None: from Converter import Constants import matplotlib.pyplot as plt V_HOwave = Constants.convert(V_HO, "wavenumbers", to_AU=False) V_HOwave[V_HOwave > 25000] = 25000 plt.contour(self.grid[0], self.grid[1], V_HOwave, colors='k', levels=10) plt.contourf(self.grid[0], self.grid[1], V_HOwave, cmap='viridis', levels=10) plt.colorbar() plt.xlabel("Delta OO") plt.ylabel("Delta OH") plt.show() # plt.savefig(plotV) # plt.close() return V_HO
def mass_array(self): from Converter import Constants if self._mass_array is None: m = np.array( [Constants.mass(x, to_AU=True) for x in self.atom_array]) self._mass_array = m # returns masses in AMU return self._mass_array
def make_scan_plots(self, grid=False, contour=True): plt.rcParams.update({'font.size': 20}) fig = plt.figure(dpi=600) if grid: pts = np.array(list(self.logData.cartesians.keys())) plt.plot(pts[:, 0], pts[:, 1], 'ok', markersize=1.5) # plt.close() if contour: pts = self.logData.rawenergies pot = pts[:, 2] potwv = Constants.convert(pot, "wavenumbers", to_AU=False) potwv[potwv > 24000] = 24000 plt.tricontourf(pts[:, 0], pts[:, 1], potwv, cmap='viridis', levels=8) cb = plt.colorbar() cb.set_label("Energy ($\mathrm{cm^{-1}}$)") plt.tricontour(pts[:, 0], pts[:, 1], potwv, colors='k', levels=8) plt.xlabel("$\mathrm{R_{OO}}$ ($\mathrm{\AA}$)") plt.ylabel("$\mathrm{r_{XH}}$ ($\mathrm{\AA}$)") # plt.axis("off") plt.savefig( f"{self.fig_dir}/{self.molecule.MoleculeName}_XH_OOpot.png", dpi=fig.dpi, bbox_inches="tight")
def calc_all_RotConstants(molInfo_obj, torWfn_coefs, numstates, vOH, filetags=""): import csv from Converter import Constants degrees = np.arange(0, 370, 10) rots = np.zeros((len(degrees), 3)) with open(f"RotationalConstants_vOH{vOH}{filetags}.csv", mode="w") as results: results_writer = csv.writer(results, delimiter=',') results_writer.writerow(["initial", "final", "A", "B", "C"]) for d, val in enumerate([ "000", "010", "020", "030", "040", "050", "060", "070", "080", "090", "100", "110", "120", "130", "140", "150", "160", "170", "180", "190", "200", "210", "220", "230", "240", "250", "260", "270", "280", "290", "300", "310", "320", "330", "340", "350", "360" ]): rots[d] = RotConstants(molInfo_obj, f"tbhp_{val}.log") R_coeffs = RotCoeffs(rots) R_mat = RotationMatrix(R_coeffs) for State in np.arange(numstates): matEl = np.zeros(3) for c, val in enumerate(["A", "B", "C"]): # loop through components supere = np.dot(R_mat[c, :, :], torWfn_coefs[:, State].T) matEl_au = np.dot(torWfn_coefs[:, State], supere) matEl[c] = Constants.convert(matEl_au, "wavenumbers", to_AU=False) results_writer.writerow([State, State, *matEl])
def make_adiabatplots(self): from scipy import interpolate, optimize mini_pot = self.logData.minimum_pot() eps = self.OHDVRresults["epsilonPots"] oo_energies = self.OODVRresults["energy_array"] fig = plt.figure(figsize=(5, 6), dpi=600) plt.rcParams.update({'font.size': 16}) grid = self.OODVRresults["potential"][0][:, 0] # plot electronic energy E = Constants.convert(mini_pot[:, 2], "wavenumbers", to_AU=False).T roos = mini_pot[:, 0] tck = interpolate.splrep(roos, E, s=0) E_fit = interpolate.splev(grid, tck, der=0) plt.plot(grid, E_fit, '-k', linewidth=6.0) # plot epsilon curves and energy levels colors = ["royalblue", "crimson"] for i in range(2): # plot curves pot = eps[:, i + 1] en_level = oo_energies[i, :] print(f"{self.OHDVRresults['method']} Frequency OO(OH={i}): ", en_level[1] - en_level[0]) print(f"{self.OHDVRresults['method']} Ground State(OH={i}): ", en_level[0]) tck = interpolate.splrep(eps[:, 0], pot, s=0) pot_fit = interpolate.splev(grid, tck, der=0) # plot minimum line # if i == 0: # min_idx = np.argmin(pot_fit) # print("minimum OO: ", grid[min_idx]) # plt.plot(np.repeat(grid[min_idx], 100), np.linspace(0, 8000, 100), "--C7", linewidth=3.0) plt.plot(grid, pot_fit, colors[i], linewidth=6.0) for j in range(2): # plot levels # -- if levels aren't plotting check xl and xr and make sure they are making actual vectors. xl = optimize.root( lambda x: interpolate.splev(x, tck, der=0) - en_level[j], grid[0], method="lm").x xr = optimize.root( lambda x: interpolate.splev(x, tck, der=0) - en_level[j], grid[-1], method="lm").x enl_x = np.linspace(xr[0], xl[0], 10) E = [en_level[j]] * len(enl_x) plt.plot(enl_x, E, colors[i], linewidth=4.0) print(f"{self.OHDVRresults['method']} Frequency OH: ", oo_energies[1, 0] - oo_energies[0, 0]) # plt.title(f"{self.molecule.method} {self.OHDVRresults['method']} OH") plt.yticks(fontsize=20) plt.ylim(-100, 8000) plt.xticks(fontsize=20) plt.xlim(2, 3.5) plt.tight_layout() plt.axis('off') plt.savefig( f"{self.fig_dir}/{self.molecule.MoleculeName}_adiabatplot_{self.OHDVR}OH{self.OODVR}OO_noaxis.pdf", dpi=fig.dpi, bbox_inches="tight", transparent=True) plt.close()
def scale_barrier(energy_dat, barrier_height, scaling_factor): """finds the minima and cuts those points between, scales barrier and returns full data set back""" print(f"Beginning Electronic Barrier Scaling") energy_dat[:, 0] = np.radians(energy_dat[:, 0]) mins = np.argsort(energy_dat[:, 1]) mins = np.sort(mins[:2]) center = energy_dat[mins[0]:mins[-1]+1, :] max_idx = np.argmax(center[:, 1]) true_bh = center[max_idx, 1] - center[0, 1] print(f"True Barrier Height: {Constants.convert(true_bh, 'wavenumbers', to_AU=False)} cm ^-1") if barrier_height is None: # scale based on scaling factor new_center = np.column_stack((center[:, 0], scaling_factor * center[:, 1])) max_idx = np.argmax(new_center[:, 1]) scaled_bh = new_center[max_idx, 1] - new_center[0, 1] print(f"Scaled Barrier Height: {Constants.convert(scaled_bh, 'wavenumbers', to_AU=False)} cm ^-1") print(f"using a scaling factor of {scaling_factor}") left = energy_dat[0:mins[0], :] right = energy_dat[mins[-1] + 1:, :] scaled_energies = np.vstack((left, new_center, right)) elif scaling_factor is None: # scale to a defined barrier height barrier_har = Constants.convert(barrier_height, "wavenumbers", to_AU=True) scaling_factor = barrier_har / true_bh print(f"Scaling Factor: {scaling_factor}") print(f"Scaled Barrier Height: {barrier_height} cm^-1") new_center = np.column_stack((center[:, 0], scaling_factor*center[:, 1])) left = energy_dat[0:mins[0], :] right = energy_dat[mins[-1]+1:, :] scaled_energies = np.vstack((left, new_center, right)) else: raise Exception("Can not scale with barrier_height or scaling_factor undefined") return scaling_factor, scaled_energies # float64, [degree, energy]
def COMcoords(data, massweight=True): """ This function takes coords for Gaussian output and translates them to them to the center of mass and then mass-weights them :param data: FchkInterpreter class of data from a Gaussian fchk file :type data: class :return: array of coordinates shifted to the center of mass and mass weighted :rtype: np.array """ mass = Constants.convert(data.atomicmasses, "amu", to_AU=True) tot_mass = np.sum(mass) # coords = Constants.convert(data.cartesians, "angstroms", to_AU=True) coords = data.cartesians COM = np.zeros(3) for i in range(3): mr = 0 for j in np.arange(len(mass)): mr += mass[j] * coords[j, i] COM[i] = (1 / tot_mass) * mr x_coords = coords[:, 0] - COM[0] y_coords = coords[:, 1] - COM[1] z_coords = coords[:, 2] - COM[2] COM_coords = np.concatenate( (x_coords[:, np.newaxis], y_coords[:, np.newaxis], z_coords[:, np.newaxis]), axis=1) if massweight: mwCOM_coords = np.zeros(COM_coords.shape) for j in np.arange(len(mass)): mwCOM_coords[j, :] = np.sqrt(mass[j]) * COM_coords[j, :] fcoords = mwCOM_coords else: fcoords = COM_coords # print(fcoords) return fcoords
def calc_Vcoeffs(self): from Converter import Constants from FourierExpansions import calc_cos_coefs, calc_4cos_coefs print("Using DFT for all potentials...") dvr_energies = self.DVRresults["energies"] # [degrees vOH=0 ... vOH=6] dvr_energies[:, 1:] = Constants.convert(dvr_energies[:, 1:], "wavenumbers", to_AU=True) coeff_dict = dict() # build coeff dict rad = np.radians(dvr_energies[:, 0]) coeff_dict["Vel"] = self.Velcoeffs if "Vexpansion" in self.PORparams: if self.PORparams["Vexpansion"] == "fourth": print("Expaning potential coefficients to four tau") for i in np.arange( 1, dvr_energies.shape[1]): # loop through saved energies energies = np.column_stack((rad, dvr_energies[:, i])) coeff_dict[f"V{i - 1}"] = calc_4cos_coefs(energies) elif self.PORparams["Vexpansion"] == "sixth": print("Expaning potential coefficients to six tau") for i in np.arange( 1, dvr_energies.shape[1]): # loop through saved energies energies = np.column_stack((rad, dvr_energies[:, i])) coeff_dict[f"V{i - 1}"] = calc_cos_coefs(energies) else: raise Exception( f"Can not expand to {self.PORparams['Vexpansion']}") return coeff_dict
def ohWfn_PAs(self, **kwargs): from matplotlib.lines import Line2D potz = self.OHDVRresults["potential"] wfns = self.OHDVRresults["wfns_array"] eps = self.OHDVRresults["epsilonPots"] roos = eps[:, 0] colors = ["grey", "red", "orange", "deeppink"] for i, j in enumerate(roos): print("Roo : ", j) print("Energies : ", eps[i, :]) fig = plt.figure(dpi=600, facecolor="white") ax1 = plt.axes() ax1.get_xaxis().tick_bottom() ax1.axes.get_yaxis().set_visible(False) ax1.spines["top"].set_visible(False) ax1.spines["left"].set_visible(False) ax1.spines["right"].set_visible(False) for k in np.arange(2): plt.plot(potz[i, :, 0], (wfns[i, :, k]**2), linewidth=3.0, color=colors[k + 1], label="$\psi_{%d_{XH}}$" % k) if k == 0: # calc width (std dev) = sqrt(<x^2>-<x>^2) bohr = Constants.convert(potz[i, :, 0], "angstroms", to_AU=True) ket1 = bohr**2 * wfns[i, :, k] x2 = np.dot(wfns[i, :, 0], ket1) ket2 = bohr * wfns[i, :, k] x_square = np.dot(wfns[i, :, k], ket2)**2 width = np.sqrt(x2 - x_square) print(f"{k} width : ", Constants.convert(width, "angstroms", to_AU=False)) plt.ylim(-0.001, 0.025) plt.xlim(-0.4, 0.7) # ax1.add_artist(Line2D((-0.4, 0.7), (-0.001, -0.001), color='k', linewidth=2)) plt.xlabel("$\mathrm{r_{XH}}$ ($\mathrm{\AA}$)", size=16) # plt.ylabel("Probability Amplitude", size=16) plt.legend(fontsize=14, frameon=False) plt.title(f"Roo = {j}", size=16) plt.tight_layout() plt.savefig( f"{self.wfn_dir}/{self.molecule.method}_{self.OHDVR}_OHPAs_Roo_{j}_test.png", dpi=fig.dpi, bbox_inches="tight") plt.close()
def run_anharOH_DVR(self, plotPhasedWfns=False): """ Runs anharmonic DVR over the OH coordinate at every OO value.""" from PotentialHandlers import Potentials1D dvr_1D = DVR("ColbertMiller1D") cut_dict = self.logData.cut_dictionary() roos = np.array(list(cut_dict.keys())) potential_array = np.zeros((len(cut_dict), self.NumPts, 2)) energies_array = np.zeros((len(cut_dict), self.desiredEnergies)) wavefunctions_array = np.zeros( (len(cut_dict), self.NumPts, self.desiredEnergies)) for j, n in enumerate(cut_dict): x = Constants.convert(cut_dict[n][:, 0], "angstroms", to_AU=True) mini = min(x) - 0.3 maxi = max(x) + 0.3 en = cut_dict[n][:, 1] res = dvr_1D.run(potential_function=Potentials1D().potlint(x, en), mass=self.massdict["muOOH"], divs=self.NumPts, domain=(mini, maxi), num_wfns=self.desiredEnergies) potential = Constants.convert(res.potential_energy.diagonal(), "wavenumbers", to_AU=False) grid = Constants.convert(res.grid, "angstroms", to_AU=False) # shiftgrid = (n/2) + grid potential_array[j, :, 0] = grid # shiftgrid potential_array[j, :, 1] = potential ens = Constants.convert(res.wavefunctions.energies, "wavenumbers", to_AU=False) energies_array[j, :] = ens wavefunctions_array[j, :, :] = res.wavefunctions.wavefunctions epsilon_pots = np.column_stack((roos, energies_array[:, :4])) npz_filename = os.path.join( self.DVRdir, f"{self.method}_AnharmOHDVR_energies{self.desiredEnergies}.npz") # data saved in wavenumbers/angstroms wavefuns_array = self.wfn_flipper(wavefunctions_array, plotPhasedWfns=plotPhasedWfns, pot_array=potential_array) np.savez(npz_filename, method="anharm", potential=potential_array, epsilonPots=epsilon_pots, wfns_array=wavefuns_array) return npz_filename