Ejemplo n.º 1
0
    def get_helmholtz_energy(self,
                             temperature,
                             electronic_energy=0,
                             verbose=False):
        """Returns the Helmholtz 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
        -------
        helmholtz_energy : numeric
            Helmholtz energy in eV
        """
        thermo_object = HarmonicThermo(vib_energies=self.vib_energies,
                                       potentialenergy=electronic_energy)
        self.helmholtz_energy = thermo_object.get_helmholtz_energy(
            temperature=temperature, verbose=verbose)
        return (self.helmholtz_energy)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
                 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)
Ejemplo n.º 5
0
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