def get_internal_energy(self, temperature, electronic_energy=0, verbose=False): """Returns the internal energy of an adsorbed molecule. Parameters ---------- temperature : numeric temperature in K electronic_energy : numeric energy in eV verbose : boolean whether to print ASE thermochemistry output Returns ------- internal_energy : numeric Internal energy in eV """ thermo_object = HarmonicThermo(vib_energies=self.vib_energies, potentialenergy=electronic_energy) self.internal_energy = thermo_object.get_internal_energy( temperature=temperature, verbose=verbose) return (self.internal_energy)
def thermo(vib_path, temp): """Calculate vibrational contributions to U, ZPE, Cv, and S vib_path (str): path to vibrational frequency data file (cm^-1 units) """ # Read in vibrational frequencies (assumes cm^-1 units) vib = np.loadtxt(vib_path) # convert from cm-1 to eV vib_energies = vib * ase.units.invcm # initialize Harmonic-oscillator-approximated thermochemistry object thermo = HarmonicThermo(vib_energies) # Calculate U, ZPE, Cv, S, and ST. # Note that when E_pot = 0, U = ZPE + Cv u = thermo.get_internal_energy(temp, verbose=False) zpe = thermo.get_ZPE_correction() cv = u - zpe s = thermo.get_entropy(temp, verbose=False) ts = s * temp # calculate the G correction term g_correction = zpe + cv - ts # convert the results into a dictionary results = { 'E_ZPE': zpe, 'Cv_harm (0->T)': cv, 'U': u, 'S_harm': s, 'T*S': ts, f'G_correction @ {temp} K': g_correction } # print the results in a nice format center = 50 line = '=' * center print(line) print(f'Harmonic Thermochem Results at T = {temp} K'.center(center)) print(line) # iteratively print each result in table format for name in results: lbl = 'eV/K' if name == 'S_harm' else 'eV' space = center - len(name) - 5 val = results[name] print(f'{name}{val:>{space}.9f} {lbl}') print(line)
ediff=1e-8, ediffg=-0.01, algo="Fast", gga="RP", xc="PBE", kpts=(4, 4, 1), # isif = 0, # ibrion = 5, # nsw = 0, # nfree = 2 ) adsorbedH.set_calculator(calc) electronicenergy = adsorbedH.get_potential_energy() print("electronic energy is %.5f" % electronicenergy) vib = Vibrations(adsorbedH, indices=[23], delta=0.01, nfree=2) vib.run() print(vib.get_frequencies()) vib.summary() print(vib.get_mode(-1)) vib.write_mode(-1) vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, electronicenergy=electronicenergy) thermo.get_entropy(temperature=298.15) thermo.get_internal_energy(temperature=298.15) thermo.get_free_energy(temperature=298.15)
class Reactant: """ """ def __init__( self, surf_name=None, surf_class=None, #type species_name=None, species_type=None, traj_loc=None, vib_loc=None, symmetrynumber=None, geometry=None, spin=None, calc_params=None, tag='', ): self.surf_name = surf_name self.surf_class = surf_class self.species_name = species_name self.species_type = species_type self.traj_loc = traj_loc self.symmetrynumber = symmetrynumber self.geometry = geometry self.spin = spin self.tag = tag if calc_params != None: self.calc_params = calc_params if traj_loc != None: self.atoms = read(traj_loc) self.calc_params = self.get_calc_params() if self.calc_params['xc'] == 'BEEF': self.beef = self.beef_reader() else: self.atoms = [] self.calc_params = {} if vib_loc != None: self.vibs = self.vibs_reader(vib_loc) if self.species_type == 'slab': self.gibbs = None elif self.species_type == 'gas': self.gibbs = IdealGasThermo( vib_energies=self.vibs, potentialenergy=0, atoms=self.atoms, geometry=self.geometry, symmetrynumber=self.symmetrynumber, spin=self.spin, ) elif self.species_type == 'adsorbate': self.gibbs = HarmonicThermo(vib_energies=self.vibs, potentialenergy=0) else: print "Warning: unrecognized species_type: ", self.species_type self.gibbs = None def get_calc_params(self): directory = '/'.join(self.traj_loc.split('/')[0:-1]) if len(directory) < 1: directory = '.' #Look for all log/input files in all folders in main directory, choose first for calc params calcdirs = glob(directory + '/*/log') if len(calcdirs) == 0: print "WARNING: No log file found for: %s" % (self.species_name) calc_params = {'pw': None, 'xc': None, 'kpts': None, 'psp': None} return calc_params #Get parameters from longest log file #size = 0 #for i, cd in enumerate(calcdirs): # if os.stat(cd).st_size > size: # j = i # size = os.stat(cd).st_size j = 0 log_file = open(calcdirs[j], 'r').readlines() inpdirs = glob(directory + '/*/pw.inp') inp_file = open(inpdirs[j], 'r') calc_params = {} calc_params['psp'] = {} for i, line in enumerate(log_file): line = line.strip() #XC if line.startswith('Exchange-correlation'): if 'BEEF' in line: xc = 'BEEF' elif 'RPBE' in line: xc = 'RPBE' elif 'PBE' in line: xc = 'PBE' #PSP if line.startswith('PseudoPot.'): elem = line.split()[4] md5 = log_file[i + 2].split()[-1] calc_params['psp'][elem] = md5 #PW if line.startswith('kinetic-energy cutoff'): pw = float(re.findall("\d+\.\d+", line)[0]) pw *= 13.61 #ryd to ev pw = str(int(pw)) #round pw and turn to str calc_params['xc'] = xc calc_params['pw'] = pw #K-PTS inplines = inp_file.readlines() kpts = inplines[-1].split() calc_params['kpts'] = ''.join(kpts[0:3]) if len(calc_params) < 3: print "ERROR: Unable to extract calculator parameters automatically, user can supply manually." exit() inp_file.close() return calc_params def beef_reader(self): directory = '/'.join(self.traj_loc.split('/')[0:-1]) if os.path.exists(directory + '/ensemble.pkl') == False or os.stat( directory + '/ensemble.pkl').st_size == 0: print "Warning: No beef ensemble for %s" % (directory) return beef_array = pickle.load(open(directory + '/ensemble.pkl', 'r')) #Check if BEEF normalized around zero or not #Correct???? if abs(np.mean(beef_array)) < 100: beef_array += read(self.traj_loc).get_potential_energy() return beef_array def vibs_reader(self, vib_loc): f = open(vib_loc + '/myjob.out', 'r') vibenergies = [] for line in f: try: temp = line.replace('.', '') temp = temp.replace(' ', '') temp = temp.replace('i', '') float(temp) except ValueError: continue num, meV, inv_cm, = line.split() if 'i' in meV: meV = 7 vibenergies.append(float(meV)) #convert from meV to eV for each mode vibenergies[:] = [round(ve / 1000., 4) for ve in vibenergies] if vibenergies == []: print "Warning: No vibrational energies for %s" % (self.traj_loc) return vibenergies def get_Gcorr(self, T, P=101325, verbose=False): if self.species_type == 'slab' or self.species_type == 'bulk': Gcorr = 0 elif self.species_type == 'gas': if len(self.vibs) == 0: print "ERROR: No vibrations for %s" % (self.traj_loc) exit() Gcorr = self.gibbs.get_gibbs_energy(T, P, verbose=verbose) elif self.species_type == 'adsorbate': if len(self.vibs) == 0: print "ERROR: No vibrations for %s" % (self.traj_loc) exit() Gcorr = self.gibbs.get_helmholtz_energy(T, verbose=verbose) else: print "Error: ambiguous species type for function get_dGcorr. Should be 'gas' or 'adsorbate'." exit() return Gcorr def get_Hcorr(self, T, verbose=False): if self.species_type == 'slab' or self.species_type == 'bulk': Hcorr = 0 elif self.species_type == 'gas': Hcorr = self.gibbs.get_enthalpy(T, verbose=verbose) elif self.species_type == 'adsorbate': Hcorr = self.gibbs.get_internal_energy(T, verbose=verbose) return Hcorr