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 __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 GibbsAds(energy, frequencies, T): #Expecting T in Kelvin #note that frequencies should have a lower bound, e.g. 56 cm-1, in order to bound entropic contributions. cm_to_eV = 1.23981e-4 vib_energies = list(frequencies) for i in range(len(vib_energies)): vib_energies[i] = vib_energies[i] * cm_to_eV thermo_ads = HarmonicThermo(vib_energies=vib_energies, electronicenergy=energy) val = thermo_ads.get_gibbs_energy(temperature=T, verbose=False) return val
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)
def GibbsAds(energy, frequencies, T): #Expecting T in Kelvin #note that frequencies should have a lower bound, e.g. 56 cm-1, in order to bound entropic contributions. cm_to_eV = 1.23981e-4 vib_energies = list(frequencies) for i in range(len(vib_energies)): vib_energies[i] = vib_energies[i] * cm_to_eV thermo_ads = HarmonicThermo(vib_energies=vib_energies, potentialenergy=energy) #In older versions of ASE the Helmholtz energy was called the Gibbs free energy. #However, nothing has changed in what is actually calculated. #The two different free energies are approximately equal since their difference (the pV term) is small. #This pV term was always neglected. val = thermo_ads.get_helmholtz_energy(temperature=T, verbose=False) return val
def test_harmonic_thermo(): atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.calc = EMT() add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint(FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations(atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15)
def _calculate_Gcorr(self, adsorbate, verbose=False): """Re-calculates the G_correction for the specified adsorbate. Note since only harmonic degrees of freedom are considered and the clean slab is considered to be static (i.e., its vibrations are unaffected by the presence of the adsorbate), then its G correction is 0. Args: adsorbate (string) verbose (bool, optional) """ if adsorbate == '': self.data[adsorbate]['EtoG'] = 0. if adsorbate == '': self.data[adsorbate]['EtoG'] = 0. else: vibs = self.data[adsorbate]['vibs'] thermo = HarmonicThermo(vib_energies=vibs, electronicenergy=None) self.data[adsorbate]['EtoG'] = thermo.get_gibbs_energy( temperature=self.temperature, verbose=verbose)
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)
# name of output file for free energies output_name = 'out.energy' ### At 300K and 101325 Pa ### change for your operating conditions T = 300 # K P = 101325 # Pa ######################################################################################################### ##### END ##### ######################################################################################################### energy = atoms.get_potential_energy() # caclulate the energy, to be used to determine G vibrateatoms = [atom.index for atom in atoms if atom.symbol in ['H','N']] # calculate the vibrational modes for all N and H atoms # Calculate vibrations vib = Vibrations(atoms,indices=vibrateatoms,delta=0.03) # define a vibration calculation vib.run() # run the vibration calculation vib.summary(method='standard') # summarize the calculated results for mode in range(len(vibrateatoms)*3): # Make trajectory files to visualize the modes. vib.write_mode(mode) vibenergies=vib.get_energies() vibenergies=[vib for vib in vibenergies if not isinstance(vib,complex)] # only take the real modes gibbs = HarmonicThermo(vib_energies = vibenergies, electronicenergy = energy) freeenergy = gibbs.get_gibbs_energy(T,P) f=open(output_name,'w') f.write('Potential energy: '+str(energy)+'\n'+'Free energy: '+str(freeenergy)+'\n') f.close
isif=2, prec="Normal", ediff=1E-4, ediffg=-0.02, xc="pbe", ispin=2, lcharg=True, encut=470, lorbit=11, nelmin=6, nelm=60, ivdw=11, isym=0, lreal="Auto", gamma=True, potim=0.015, nfree=2) freq_cluster.calc = freq_calc vib = Vibrations(freq_cluster) vib.run() vib_energies = vib.get_energies() thermo = HarmonicThermo( vib_energies=vib_energies, potentialenergy=potentialenergy, atoms=freq_cluster ) #harmonic approximation for the adsorbate species vibration G = thermo.get_gibbs_energy(temperature=298.15, pressure=101325.) S = thermo.get_entropy(temperature=298.15) H = thermo.get_helmholtz_energy(temperature=298.15)
def get_ads_Gcorr(self,vibs,verbose=False): gibbs = HarmonicThermo(vib_energies = vibs, potentialenergy = 0) return gibbs
## more information here: https://wiki.fysik.dtu.dk/ase/ase/thermochemistry/thermochemistry.html name = 'example_ads' #electronic energy in eV energy = -21861.531796 #vibrational energies in meV vibenergies = [60.5, 77.1, 314.2] #convert from meV to eV for each mode vibenergies[:] = [ve / 1000. for ve in vibenergies] #list of temperatures temperatures = [300] #operating pressure pressures = [101325] f = open(name + '_free.energy', 'w') for temperature in temperatures: for pressure in pressures: gibbs = HarmonicThermo(vib_energies=vibenergies, electronicenergy=energy) freeenergy = gibbs.get_gibbs_energy(temperature, pressure) f.write('Temperature: ' + str(temperature) + '\t' + 'Pressure: ' + str(pressure) + '\t' + 'Free energy: ' + str(freeenergy) + '\n') f.close
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
#vib = Vibrations(new_atoms, name=vibstr, indices=[87]) vib.run() vib.summary(method='frederiksen') # Make trajectory files to visualize normal modes: for mode in range(len(vib.modes)): vib.write_mode(mode) ##vib.PrintHessianMatrix(mass=1,precision=3,suppress_small=1) #vib.PrintFrequencies() #print 'Zero-point energy = %1.2f eV' % vib.GetZeroPointEnergy() #from ase.thermochemistry import IdealGasThermo from ase.thermochemistry import HarmonicThermo vib_energies = vib.get_energies() print vib_energies thermo = HarmonicThermo( vib_energies=vib_energies, electronicenergy=electronicenergy, ) G = thermo.get_internal_energy(temperature=298.15) #import os #import subprocess #subprocess.call('rm -f WAVECAR', shell=True) #write(name+'opt.traj',atoms)
###################################################### ## more information here: https://wiki.fysik.dtu.dk/ase/ase/thermochemistry/thermochemistry.html name = 'example_ads' #electronic energy in eV energy = -21861.531796 #vibrational energies in meV vibenergies = [60.5, 77.1, 314.2] #convert from meV to eV for each mode vibenergies[:] = [ve/1000. for ve in vibenergies] #list of temperatures temperatures = [300] #operating pressure pressures = [101325] f=open(name+'_free.energy','w') for temperature in temperatures: for pressure in pressures: gibbs = HarmonicThermo(vib_energies = vibenergies, electronicenergy = energy) freeenergy = gibbs.get_gibbs_energy(temperature,pressure) f.write('Temperature: '+str(temperature)+'\t'+'Pressure: '+str(pressure)+'\t'+'Free energy: '+str(freeenergy)+'\n') f.close
def test_thermochemistry(): """Tests of the major methods (HarmonicThermo, IdealGasThermo, CrystalThermo) from the thermochemistry module.""" # Ideal gas thermo. atoms = Atoms('N2', positions=[(0, 0, 0), (0, 0, 1.1)], calculator=EMT()) QuasiNewton(atoms).run(fmax=0.01) energy = atoms.get_potential_energy() vib = Vibrations(atoms, name='idealgasthermo-vib') vib.run() vib_energies = vib.get_energies() thermo = IdealGasThermo(vib_energies=vib_energies, geometry='linear', atoms=atoms, symmetrynumber=2, spin=0, potentialenergy=energy) thermo.get_gibbs_energy(temperature=298.15, pressure=2 * 101325.) # Harmonic thermo. atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.set_calculator(EMT()) add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint( FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations( atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15) # Crystal thermo. atoms = bulk('Al', 'fcc', a=4.05) calc = EMT() atoms.set_calculator(calc) energy = atoms.get_potential_energy() # Phonon calculator N = 7 ph = Phonons(atoms, calc, supercell=(N, N, N), delta=0.05) ph.run() ph.read(acoustic=True) phonon_energies, phonon_DOS = ph.dos(kpts=(4, 4, 4), npts=30, delta=5e-4) thermo = CrystalThermo(phonon_energies=phonon_energies, phonon_DOS=phonon_DOS, potentialenergy=energy, formula_units=4) thermo.get_helmholtz_energy(temperature=298.15) # Hindered translator / rotor. # (Taken directly from the example given in the documentation.) vibs = np.array([ 3049.060670, 3040.796863, 3001.661338, 2997.961647, 2866.153162, 2750.855460, 1436.792655, 1431.413595, 1415.952186, 1395.726300, 1358.412432, 1335.922737, 1167.009954, 1142.126116, 1013.918680, 803.400098, 783.026031, 310.448278, 136.112935, 112.939853, 103.926392, 77.262869, 60.278004, 25.825447 ]) vib_energies = vibs / 8065.54429 # Convert to eV from cm^-1. trans_barrier_energy = 0.049313 # eV rot_barrier_energy = 0.017675 # eV sitedensity = 1.5e15 # cm^-2 rotationalminima = 6 symmetrynumber = 1 mass = 30.07 # amu inertia = 73.149 # amu Ang^-2 thermo = HinderedThermo(vib_energies=vib_energies, trans_barrier_energy=trans_barrier_energy, rot_barrier_energy=rot_barrier_energy, sitedensity=sitedensity, rotationalminima=rotationalminima, symmetrynumber=symmetrynumber, mass=mass, inertia=inertia) helmholtz = thermo.get_helmholtz_energy(temperature=298.15) target = 1.593 # Taken from documentation example. assert (helmholtz - target) < 0.001
P = 101325 # Pa ######################################################################################################### ##### END ##### ######################################################################################################### energy = atoms.get_potential_energy( ) # caclulate the energy, to be used to determine G vibrateatoms = [atom.index for atom in atoms if atom.symbol in ['H', 'N'] ] # calculate the vibrational modes for all N and H atoms # Calculate vibrations vib = Vibrations(atoms, indices=vibrateatoms, delta=0.03) # define a vibration calculation vib.run() # run the vibration calculation vib.summary(method='standard') # summarize the calculated results for mode in range(len(vibrateatoms) * 3): # Make trajectory files to visualize the modes. vib.write_mode(mode) vibenergies = vib.get_energies() vibenergies = [vib for vib in vibenergies if not isinstance(vib, complex)] # only take the real modes gibbs = HarmonicThermo(vib_energies=vibenergies, electronicenergy=energy) freeenergy = gibbs.get_gibbs_energy(T, P) f = open(output_name, 'w') f.write('Potential energy: ' + str(energy) + '\n' + 'Free energy: ' + str(freeenergy) + '\n') f.close
dw=8000, # density cutoff nbands=-10, # number of bands kpts=(8, 8, 8), # k points xc='rpbe', # exchange correlation method sigma=0.2, # Fermi temperature dipole={'status': False}, spinpol=False, convergence={ 'energy': 0.0005, 'mixing': 0.1, 'nmix': 10, 'maxsteps': 500, 'diag': 'david' }, outdirprefix='vibdir') atoms.set_calculator(calc) vib = Vibrations(atoms, delta=0.04, indices=vib_atoms) vib.run() vib.summary(log='vibrations.txt') vib.write_jmol() realFreq = [ x * 0.00012 for x in vib.get_frequencies() if not isinstance(x, complex) ] #eV S = HarmonicThermo(realFreq).get_entropy(300) with open('vibrations.txt', 'a') as f: f.write('\nTS at 300K: ' + str(S))
# Harmonic thermo. atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.set_calculator(EMT()) add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint( FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations(atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15) # Crystal thermo. atoms = bulk('Al', 'fcc', a=4.05) calc = EMT() atoms.set_calculator(calc) energy = atoms.get_potential_energy() # Phonon calculator N = 7 ph = Phonons(atoms, calc, supercell=(N, N, N), delta=0.05) ph.run() ph.read(acoustic=True) phonon_energies, phonon_DOS = ph.dos(kpts=(4, 4, 4), npts=30, delta=5e-4)
'nmix':10, 'maxsteps': maxsteps, 'diag': 'david'}, #mode = 'scf', output = {'avoidio':False, 'removewf':True, 'wf_collect':False}, outdirprefix='vibdir' ) atoms.set_calculator(calc) vib = Vibrations(atoms, delta=0.04, indices=vib_atoms) vib.run() vib.summary(log='vibrations.txt') vib.write_jmol() #dyn = QuasiNewton(atoms, logfile='qn.log', trajectory='qn.traj') #dyn.run(fmax=0.05) energy = atoms.get_potential_energy() vibs = vib.get_energies() with open('vibrations.txt','r') as f: ZPE = f.readlines()[-1] gibbs = HarmonicThermo(vib_energies=vibs) entropy = gibbs.get_entropy(temperature) with open('converged.log', 'w') as f: f.write("Energy: %f eV\nEntropy: %f eV/K \n%s"%(energy,entropy,ZPE))