Ejemplo n.º 1
0
    def get_enthalpy_of_formation(self,
                                  freq_scale_factor=1.0,
                                  apply_bond_corrections=True):
        """
        Calculate the enthalpy of formation at 298.15 K.
        Apply bond energy corrections if desired and if
        model chemistry is compatible.
        """
        temperature = 298.15
        mol = pybel.readstring('smi',
                               self.smiles)  # Use OBMol to extract bond types

        # Use RMG molecule to determine if it's linear
        # Assume it's not linear if we can't parse SMILES with RMG
        rmg_mol = None
        try:
            rmg_mol = Molecule().fromSMILES(self.smiles)
        except AtomTypeError:
            try:
                rmg_mol = Molecule().fromSMILES(self.smiles2)
            except AtomTypeError:
                try:
                    rmg_mol = Molecule().fromInChI(self.inchi)
                except AtomTypeError:
                    warnings.warn(
                        'Could not determine linearity from RMG molecule in {}'
                        .format(self.file_name))
        if rmg_mol is not None:
            is_linear = rmg_mol.isLinear()
        else:
            is_linear = False

        # Translation
        translation = IdealGasTranslation()

        # Rotation
        if is_linear:
            rotation = LinearRotor()
        else:
            rotation = NonlinearRotor(
                rotationalConstant=(self.rotational_consts, 'GHz'))

        # Vibration
        freqs = [f * freq_scale_factor
                 for f in self.freqs]  # Apply scale factor
        vibration = HarmonicOscillator(frequencies=(freqs, 'cm^-1'))

        # Group modes
        modes = [translation, rotation, vibration]
        conformer = Conformer(modes=modes)

        # Energy
        e0 = self.e0 * constants.E_h * constants.Na
        zpe = self.zpe * constants.E_h * constants.Na * freq_scale_factor

        # Bring energy to gas phase reference state at 298.15K
        atom_energies = energy_data.atom_energies[self.model_chemistry]
        for element in self.elements:
            e0 -= atom_energies[element] * constants.E_h * constants.Na
            e0 += self.enthalpy_corrections[element] * 4184.0

        if apply_bond_corrections:
            bond_energies = energy_data.bond_energy_corrections[
                self.model_chemistry]
            for bond in pybel.ob.OBMolBondIter(mol.OBMol):
                bond_symbol_split = [
                    self.atomic_num_dict[bond.GetBeginAtom().GetAtomicNum()],
                    self.bond_symbols[bond.GetBondOrder()],
                    self.atomic_num_dict[bond.GetEndAtom().GetAtomicNum()]
                ]

                try:
                    bond_energy = bond_energies[''.join(bond_symbol_split)]
                except KeyError:
                    bond_energy = bond_energies[''.join(
                        bond_symbol_split[::-1])]  # Try reverse order

                e0 += bond_energy * 4184.0

        conformer.E0 = (e0 + zpe, 'J/mol')
        self.hf298 = conformer.getEnthalpy(temperature) + conformer.E0.value_si

        return self.hf298
Ejemplo n.º 2
0
def get_thermo(optfreq_log, optfreq_level, energy_level, energy_log=None,
               mol=None, bacs=None, soc=False,
               infer_symmetry=False, infer_chirality=False, unique_id='0', scr_dir='SCRATCH'):

    q = QChem(logfile=optfreq_log)
    symbols, coords = q.get_geometry()
    inertia = q.get_moments_of_inertia()
    freqs = q.get_frequencies()
    zpe = q.get_zpe()

    if energy_log is None:
        e0 = q.get_energy()
        multiplicity = q.get_multiplicity()
    else:
        m = Molpro(logfile=energy_log)
        e0 = m.get_energy()
        multiplicity = m.get_multiplicity()

    # Infer connections only if not given explicitly
    if mol is None:
        mol = geo_to_rmg_mol((symbols, coords))  # Does not contain bond orders

    # Try to infer point group to calculate symmetry number and chirality
    symmetry = optical_isomers = 1
    point_group = None
    if infer_symmetry or infer_chirality:
        qmdata = QMData(
            groundStateDegeneracy=multiplicity,  # Only needed to check if valid QMData
            numberOfAtoms=len(symbols),
            atomicNumbers=[atomic_symbol_dict[sym] for sym in symbols],
            atomCoords=(coords, 'angstrom'),
            energy=(e0 * 627.5095, 'kcal/mol')  # Only needed to avoid error
        )
        settings = type("", (), dict(symmetryPath='symmetry', scratchDirectory=scr_dir))()  # Creates anonymous class
        pgc = PointGroupCalculator(settings, unique_id, qmdata)
        point_group = pgc.calculate()
    if point_group is not None:
        if infer_symmetry:
            symmetry = point_group.symmetryNumber
        if infer_chirality and point_group.chiral:
            optical_isomers = 2

    # Translational mode
    mass = mol.getMolecularWeight()
    translation = IdealGasTranslation(mass=(mass, 'kg/mol'))

    # Rotational mode
    if isinstance(inertia, list):  # Nonlinear
        rotation = NonlinearRotor(inertia=(inertia, 'amu*angstrom^2'), symmetry=symmetry)
    else:
        rotation = LinearRotor(inertia=(inertia, 'amu*angstrom^2'), symmetry=symmetry)

    # Vibrational mode
    freq_scale_factor = freq_scale_factors.get(optfreq_level, 1.0)
    freqs = [f * freq_scale_factor for f in freqs]
    vibration = HarmonicOscillator(frequencies=(freqs, 'cm^-1'))

    # Bring energy to gas phase reference state
    e0 *= constants.E_h * constants.Na
    zpe *= constants.E_h * constants.Na * freq_scale_factor
    for sym in symbols:
        if soc:
            e0 -= (atom_energies[energy_level][sym] - atom_socs[sym]) * constants.E_h * constants.Na
        else:
            e0 -= atom_energies[energy_level][sym] * constants.E_h * constants.Na
        e0 += (h0expt[sym] - h298corr[sym]) * 4184.0

    if bacs is not None:
        e0 -= get_bac_correction(mol, **bacs) * 4184.0

    # Group modes into Conformer object
    modes = [translation, rotation, vibration]
    conformer = Conformer(modes=modes, spinMultiplicity=multiplicity, opticalIsomers=optical_isomers)

    # Calculate heat of formation, entropy of formation, and heat capacities
    conformer.E0 = (e0 + zpe, 'J/mol')
    hf298 = conformer.getEnthalpy(298.0) + conformer.E0.value_si
    s298 = conformer.getEntropy(298.0)

    Tlist = [300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0]
    cp = np.zeros(len(Tlist))
    for i, T in enumerate(Tlist):
        cp[i] = conformer.getHeatCapacity(T)

    # Return in kcal/mol and cal/mol/K
    return hf298/4184.0, s298/4.184, cp/4.184