def setUp(self): """ A function run before each unit test in this class. """ self.ethylene = Conformer( E0=(0.0, "kJ/mol"), modes=[ IdealGasTranslation(mass=(28.03, "amu")), NonlinearRotor(inertia=([3.41526, 16.6498, 20.065], "amu*angstrom^2"), symmetry=4), HarmonicOscillator(frequencies=([828.397, 970.652, 977.223, 1052.93, 1233.55, 1367.56, 1465.09, 1672.25, 3098.46, 3111.7, 3165.79, 3193.54], "cm^-1")), ], spin_multiplicity=1, optical_isomers=1, ) self.oxygen = Conformer( E0=(0.0, "kJ/mol"), modes=[ IdealGasTranslation(mass=(31.99, "amu")), LinearRotor(inertia=(11.6056, "amu*angstrom^2"), symmetry=2), HarmonicOscillator(frequencies=([1621.54], "cm^-1")), ], spin_multiplicity=3, optical_isomers=1, ) # The following data is for ethane at the CBS-QB3 level self.coordinates = np.array([ [0.0000, 0.0000, 0.0000], [-0.0000, -0.0000, 1.0936], [1.0430, -0.0000, -0.3288], [-0.4484, 0.9417, -0.3288], [-0.7609, -1.2051, -0.5580], [-0.7609, -1.2051, -1.6516], [-0.3125, -2.1468, -0.2292], [-1.8039, -1.2051, -0.2293], ]) self.number = np.array([6, 1, 1, 1, 6, 1, 1, 1]) self.mass = np.array([12, 1.007825, 1.007825, 1.007825, 12, 1.007825, 1.007825, 1.007825]) self.E0 = -93.5097 self.conformer = Conformer( E0=(self.E0, "kJ/mol"), modes=[ IdealGasTranslation(mass=(30.0469, "amu")), NonlinearRotor(inertia=([6.27071, 25.3832, 25.3833], "amu*angstrom^2"), symmetry=6), HarmonicOscillator(frequencies=([818.917, 819.479, 987.099, 1206.76, 1207.05, 1396, 1411.35, 1489.73, 1489.95, 1492.49, 1492.66, 2995.36, 2996.06, 3040.77, 3041, 3065.86, 3066.02], "cm^-1")), HinderedRotor(inertia=(1.56768, "amu*angstrom^2"), symmetry=3, barrier=(2.69401, "kcal/mol"), quantum=False, semiclassical=False), ], spin_multiplicity=1, optical_isomers=1, coordinates=(self.coordinates, "angstrom"), number=self.number, mass=(self.mass, "amu"), )
def setUp(self): """ A method that is run before each unit test in this class. """ self.species = Species( index=1, label='C2H4', thermo=ThermoData( Tdata=([300.0,400.0,500.0,600.0,800.0,1000.0,1500.0],'K'), Cpdata=([3.0,4.0,5.0,6.0,8.0,10.0,15.0],'cal/(mol*K)'), H298=(-20.0,'kcal/mol'), S298=(50.0,'cal/(mol*K)'), Tmin=(300.0,'K'), Tmax=(2000.0,'K'), ), conformer=Conformer( E0=(0.0,'kJ/mol'), modes=[ IdealGasTranslation(mass=(28.03,'amu')), NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46],'kg*m^2'), symmetry=1), HarmonicOscillator(frequencies=([834.50, 973.31, 975.37, 1067.1, 1238.5, 1379.5, 1472.3, 1691.3, 3121.6, 3136.7, 3192.5, 3221.0],'cm^-1')), ], spinMultiplicity=1, opticalIsomers=1, ), molecule=[Molecule().fromSMILES('C=C')], transportData=TransportData(sigma=(1, 'angstrom'), epsilon=(100, 'K')), molecularWeight=(28.03,'amu'), reactive=True, )
def setUp(self): """ A method that is run before each unit test in this class. """ self.species = Species( index=1, label='C2H4', thermo=ThermoData( Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), H298=(-20.0, 'kcal/mol'), S298=(50.0, 'cal/(mol*K)'), Tmin=(300.0, 'K'), Tmax=(2000.0, 'K'), ), conformer=Conformer( E0=(0.0, 'kJ/mol'), modes=[ IdealGasTranslation(mass=(28.03, 'amu')), NonlinearRotor( inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), HarmonicOscillator(frequencies=([ 834.50, 973.31, 975.37, 1067.1, 1238.5, 1379.5, 1472.3, 1691.3, 3121.6, 3136.7, 3192.5, 3221.0 ], 'cm^-1')), ], spin_multiplicity=1, optical_isomers=1, ), molecule=[Molecule().from_smiles('C=C')], transport_data=TransportData(sigma=(1, 'angstrom'), epsilon=(100, 'K')), molecular_weight=(28.03, 'amu'), reactive=True, ) self.species2 = Species().from_adjacency_list(""" 1 C u0 p0 c0 {2,D} {6,S} {7,S} 2 C u0 p0 c0 {1,D} {3,S} {8,S} 3 C u0 p0 c0 {2,S} {4,D} {9,S} 4 C u0 p0 c0 {3,D} {5,S} {10,S} 5 C u0 p0 c0 {4,S} {6,D} {11,S} 6 C u0 p0 c0 {1,S} {5,D} {12,S} 7 H u0 p0 c0 {1,S} 8 H u0 p0 c0 {2,S} 9 H u0 p0 c0 {3,S} 10 H u0 p0 c0 {4,S} 11 H u0 p0 c0 {5,S} 12 H u0 p0 c0 {6,S} """)
def load_conformer(self, symmetry=None, spin_multiplicity=0, optical_isomers=None, label=''): """ Load the molecular degree of freedom data from a log file created as the result of a Gaussian "Freq" quantum chemistry calculation. As Gaussian's guess of the external symmetry number is not always correct, you can use the `symmetry` parameter to substitute your own value; if not provided, the value in the Gaussian log file will be adopted. In a log file with multiple Thermochemistry sections, only the last one will be kept. """ modes = [] unscaled_frequencies = [] e0 = 0.0 if optical_isomers is None or symmetry is None: _optical_isomers, _symmetry, _ = self.get_symmetry_properties() if optical_isomers is None: optical_isomers = _optical_isomers if symmetry is None: symmetry = _symmetry with open(self.path, 'r') as f: line = f.readline() while line != '': # Read the spin multiplicity if not explicitly given if spin_multiplicity == 0 and 'Multiplicity =' in line: spin_multiplicity = int(line.split()[-1]) logging.debug('Conformer {0} is assigned a spin multiplicity of {1}' .format(label, spin_multiplicity)) # The data we want is in the Thermochemistry section of the output if '- Thermochemistry -' in line: modes = [] in_partition_functions = False line = f.readline() while line != '': # This marks the end of the thermochemistry section if '-------------------------------------------------------------------' in line: break # Read molecular mass for external translational modes elif 'Molecular mass:' in line: mass = float(line.split()[2]) translation = IdealGasTranslation(mass=(mass, "amu")) modes.append(translation) # Read moments of inertia for external rotational modes elif 'Rotational constants (GHZ):' in line: inertia = [float(d) for d in line.split()[-3:]] for i in range(3): inertia[i] = constants.h / (8 * constants.pi * constants.pi * inertia[i] * 1e9) \ * constants.Na * 1e23 rotation = NonlinearRotor(inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) modes.append(rotation) elif 'Rotational constant (GHZ):' in line: inertia = [float(line.split()[3])] inertia[0] = constants.h / (8 * constants.pi * constants.pi * inertia[0] * 1e9) \ * constants.Na * 1e23 rotation = LinearRotor(inertia=(inertia[0], "amu*angstrom^2"), symmetry=symmetry) modes.append(rotation) # Read vibrational modes elif 'Vibrational temperatures:' in line: frequencies = [] frequencies.extend([float(d) for d in line.split()[2:]]) line = f.readline() frequencies.extend([float(d) for d in line.split()[1:]]) line = f.readline() while line.strip() != '': frequencies.extend([float(d) for d in line.split()]) line = f.readline() # Convert from K to cm^-1 if len(frequencies) > 0: frequencies = [freq * 0.695039 for freq in frequencies] # kB = 0.695039 cm^-1/K unscaled_frequencies = frequencies vibration = HarmonicOscillator(frequencies=(frequencies, "cm^-1")) modes.append(vibration) # Read ground-state energy elif 'Sum of electronic and zero-point Energies=' in line: e0 = float(line.split()[6]) * 4.35974394e-18 * constants.Na # Read spin multiplicity if above method was unsuccessful elif 'Electronic' in line and in_partition_functions and spin_multiplicity == 0: spin_multiplicity = int(float(line.split()[1].replace('D', 'E'))) elif 'Log10(Q)' in line: in_partition_functions = True # Read the next line in the file line = f.readline() if 'Error termination' in line: raise LogError(f'The Gaussian job in {self.path} did not converge.') # Read the next line in the file line = f.readline() return Conformer(E0=(e0 * 0.001, "kJ/mol"), modes=modes, spin_multiplicity=spin_multiplicity, optical_isomers=optical_isomers), unscaled_frequencies
def load_conformer(self, symmetry=None, spin_multiplicity=0, optical_isomers=None, label=''): """ Load the molecular degree of freedom data from a log file created as the result of a MolPro "Freq" quantum chemistry calculation with the thermo printed. """ modes = [] unscaled_frequencies = [] e0 = 0.0 if optical_isomers is None or symmetry is None: _optical_isomers, _symmetry, _ = self.get_symmetry_properties() if optical_isomers is None: optical_isomers = _optical_isomers if symmetry is None: symmetry = _symmetry with open(self.path, 'r') as f: line = f.readline() while line != '': # Read the spin multiplicity if not explicitly given if spin_multiplicity == 0 and 'spin' in line: splits = line.replace('=', ' ').replace(',', ' ').split(' ') for i, s in enumerate(splits): if 'spin' in s: spin_multiplicity = int(splits[i + 1]) + 1 logging.debug( 'Conformer {0} is assigned a spin multiplicity of {1}' .format(label, spin_multiplicity)) break if spin_multiplicity == 0 and 'SPIN SYMMETRY' in line: spin_symmetry = line.split()[-1] if spin_symmetry == 'Singlet': spin_multiplicity = 1 elif spin_symmetry == 'Doublet': spin_multiplicity = 2 elif spin_symmetry == 'Triplet': spin_multiplicity = 3 elif spin_symmetry == 'Quartet': spin_multiplicity = 4 elif spin_symmetry == 'Quintet': spin_multiplicity = 5 elif spin_symmetry == 'Sextet': spin_multiplicity = 6 if spin_multiplicity: logging.debug( 'Conformer {0} is assigned a spin multiplicity of {1}' .format(label, spin_multiplicity)) break # The data we want is in the Thermochemistry section of the output if 'THERMODYNAMICAL' in line: modes = [] line = f.readline() while line != '': # This marks the end of the thermochemistry section if '*************************************************' in line: break # Read molecular mass for external translational modes elif 'Molecular Mass:' in line: mass = float(line.split()[2]) translation = IdealGasTranslation(mass=(mass, "amu")) modes.append(translation) # Read moments of inertia for external rotational modes elif 'Rotational Constants' in line and line.split( )[-1] == '[GHz]': inertia = [float(d) for d in line.split()[-4:-1]] for i in range(3): inertia[i] = constants.h / (8 * constants.pi * constants.pi * inertia[i] * 1e9) \ * constants.Na * 1e23 rotation = NonlinearRotor( inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) modes.append(rotation) elif 'Rotational Constant' in line and line.split( )[3] == '[GHz]': inertia = float(line.split()[2]) inertia = constants.h / (8 * constants.pi * constants.pi * inertia * 1e9) \ * constants.Na * 1e23 rotation = LinearRotor(inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) modes.append(rotation) # Read vibrational modes elif 'Vibrational Temperatures' in line: frequencies = [] frequencies.extend( [float(d) for d in line.split()[3:]]) line = f.readline() while line.strip() != '': frequencies.extend( [float(d) for d in line.split()]) line = f.readline() # Convert from K to cm^-1 if len(frequencies) > 0: frequencies = [ freq * 0.695039 for freq in frequencies ] # kB = 0.695039 cm^-1/K unscaled_frequencies = frequencies vibration = HarmonicOscillator( frequencies=(frequencies, "cm^-1")) modes.append(vibration) # Read the next line in the file line = f.readline() # Read the next line in the file line = f.readline() return Conformer(E0=(e0 * 0.001, "kJ/mol"), modes=modes, spin_multiplicity=spin_multiplicity, optical_isomers=optical_isomers), unscaled_frequencies
def loadConformer(self, symmetry=None, spinMultiplicity=0, opticalIsomers=None, label=''): """ Load the molecular degree of freedom data from an output file created as the result of a QChem "Freq" calculation. As QChem's guess of the external symmetry number is not always correct, you can use the `symmetry` parameter to substitute your own value; if not provided, the value in the QChem output file will be adopted. """ modes = []; freq = []; mmass = []; rot = []; inertia = [] unscaled_frequencies = [] E0 = 0.0 if opticalIsomers is None or symmetry is None: _opticalIsomers, _symmetry = self.get_optical_isomers_and_symmetry_number() if opticalIsomers is None: opticalIsomers = _opticalIsomers if symmetry is None: symmetry = _symmetry f = open(self.path, 'r') line = f.readline() while line != '': # Read spin multiplicity if not explicitly given if '$molecule' in line and spinMultiplicity == 0: line = f.readline() if len(line.split()) == 2: spinMultiplicity = int(float(line.split()[1])) logging.debug('Conformer {0} is assigned a spin multiplicity of {1}'.format(label,spinMultiplicity)) # The rest of the data we want is in the Thermochemistry section of the output elif 'VIBRATIONAL ANALYSIS' in line: modes = [] line = f.readline() while line != '': # This marks the end of the thermochemistry section if 'Thank you very much for using Q-Chem.' in line: break # Read vibrational modes elif 'VIBRATIONAL FREQUENCIES (CM**-1)' in line: frequencies = [] while 'STANDARD THERMODYNAMIC QUANTITIES AT' not in line: if ' Frequency:' in line: if len(line.split()) == 4: frequencies.extend([float(d) for d in line.split()[-3:]]) elif len(line.split()) == 3: frequencies.extend([float(d) for d in line.split()[-2:]]) elif len(line.split()) == 2: frequencies.extend([float(d) for d in line.split()[-1:]]) line = f.readline() line = f.readline() # If there is an imaginary frequency, remove it if frequencies[0] < 0.0: frequencies = frequencies[1:] unscaled_frequencies = frequencies vibration = HarmonicOscillator(frequencies=(frequencies,"cm^-1")) # modes.append(vibration) freq.append(vibration) # Read molecular mass for external translational modes elif 'Molecular Mass:' in line: mass = float(line.split()[2]) translation = IdealGasTranslation(mass=(mass,"amu")) # modes.append(translation) mmass.append(translation) # Read moments of inertia for external rotational modes, given in atomic units elif 'Eigenvalues --' in line: inertia = [float(d) for d in line.split()[-3:]] # Read the next line in the file line = f.readline() # Read the next line in the file line = f.readline() if len(inertia): if inertia[0] == 0.0: # If the first eigenvalue is 0, the rotor is linear inertia.remove(0.0) logging.debug('inertia is {}'.format(str(inertia))) for i in range(2): inertia[i] *= (constants.a0 / 1e-10) ** 2 inertia = numpy.sqrt(inertia[0] * inertia[1]) rotation = LinearRotor(inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) rot.append(rotation) else: for i in range(3): inertia[i] *= (constants.a0 / 1e-10) ** 2 rotation = NonlinearRotor(inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) # modes.append(rotation) rot.append(rotation) inertia = [] # Close file when finished f.close() modes = mmass + rot + freq return Conformer(E0=(E0*0.001,"kJ/mol"), modes=modes, spinMultiplicity=spinMultiplicity, opticalIsomers=opticalIsomers), unscaled_frequencies
def loadConformer(self, symmetry=None, spinMultiplicity=None, opticalIsomers=1): """ Load the molecular degree of freedom data from a log file created as the result of a Gaussian "Freq" quantum chemistry calculation. As Gaussian's guess of the external symmetry number is not always correct, you can use the `symmetry` parameter to substitute your own value; if not provided, the value in the Gaussian log file will be adopted. In a log file with multiple Thermochemistry sections, only the last one will be kept. """ modes = [] E0 = 0.0 f = open(self.path, 'r') line = f.readline() while line != '': # The data we want is in the Thermochemistry section of the output if '- Thermochemistry -' in line: modes = [] inPartitionFunctions = False line = f.readline() while line != '': # This marks the end of the thermochemistry section if '-------------------------------------------------------------------' in line: break # Read molecular mass for external translational modes elif 'Molecular mass:' in line: mass = float(line.split()[2]) translation = IdealGasTranslation(mass=(mass,"amu")) modes.append(translation) # Read Gaussian's estimate of the external symmetry number elif 'Rotational symmetry number' in line and symmetry is None: symmetry = int(float(line.split()[3])) # Read moments of inertia for external rotational modes elif 'Rotational constants (GHZ):' in line: inertia = [float(d) for d in line.split()[-3:]] for i in range(3): inertia[i] = constants.h / (8 * constants.pi * constants.pi * inertia[i] * 1e9) *constants.Na*1e23 rotation = NonlinearRotor(inertia=(inertia,"amu*angstrom^2"), symmetry=symmetry) modes.append(rotation) elif 'Rotational constant (GHZ):' in line: inertia = [float(line.split()[3])] inertia[0] = constants.h / (8 * constants.pi * constants.pi * inertia[0] * 1e9) *constants.Na*1e23 rotation = LinearRotor(inertia=(inertia[0],"amu*angstrom^2"), symmetry=symmetry) modes.append(rotation) # Read vibrational modes elif 'Vibrational temperatures:' in line: frequencies = [] frequencies.extend([float(d) for d in line.split()[2:]]) line = f.readline() frequencies.extend([float(d) for d in line.split()[1:]]) line = f.readline() while line.strip() != '': frequencies.extend([float(d) for d in line.split()]) line = f.readline() # Convert from K to cm^-1 if len(frequencies) > 0: frequencies = [freq * 0.695039 for freq in frequencies] # kB = 0.695039 cm^-1/K vibration = HarmonicOscillator(frequencies=(frequencies,"cm^-1")) modes.append(vibration) # Read ground-state energy elif 'Sum of electronic and zero-point Energies=' in line: E0 = float(line.split()[6]) * 4.35974394e-18 * constants.Na # Read spin multiplicity if not explicitly given elif 'Electronic' in line and inPartitionFunctions and spinMultiplicity is None: spinMultiplicity = int(float(line.split()[1].replace('D', 'E'))) elif 'Log10(Q)' in line: inPartitionFunctions = True # Read the next line in the file line = f.readline() # Read the next line in the file line = f.readline() # Close file when finished f.close() return Conformer(E0=(E0*0.001,"kJ/mol"), modes=modes, spinMultiplicity=spinMultiplicity, opticalIsomers=opticalIsomers)
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
def loadConformer(self, symmetry=None, spinMultiplicity=None, opticalIsomers=1): """ Load the molecular degree of freedom data from a log file created as the result of a Qchem "Freq" calculation. As Qchem's guess of the external symmetry number is not always correct, you can use the `symmetry` parameter to substitute your own value; if not provided, the value in the Qchem output file will be adopted. """ modes = [] freq = [] mmass = [] rot = [] E0 = 0.0 f = open(self.path, 'r') line = f.readline() while line != '': # The data we want is in the Thermochemistry section of the output if 'VIBRATIONAL ANALYSIS' in line: modes = [] inPartitionFunctions = False line = f.readline() while line != '': # This marks the end of the thermochemistry section if 'Thank you very much for using Q-Chem.' in line: break # Read vibrational modes elif 'VIBRATIONAL FREQUENCIES (CM**-1)' in line: frequencies = [] while 'STANDARD THERMODYNAMIC QUANTITIES AT' not in line: if ' Frequency:' in line: frequencies.extend( [float(d) for d in line.split()[-3:]]) line = f.readline() line = f.readline() # If there is an imaginary frequency, remove it if frequencies[0] < 0.0: frequencies = frequencies[1:] vibration = HarmonicOscillator( frequencies=(frequencies, "cm^-1")) #modes.append(vibration) freq.append(vibration) # Read molecular mass for external translational modes elif 'Molecular Mass:' in line: mass = float(line.split()[2]) translation = IdealGasTranslation(mass=(mass, "amu")) #modes.append(translation) mmass.append(translation) # Read moments of inertia for external rotational modes, given in atomic units elif 'Eigenvalues --' in line: inertia = [float(d) for d in line.split()[-3:]] # If the first eigenvalue is 0, the rotor is linear if inertia[0] == 0.0: inertia.remove(0.0) for i in range(2): inertia[i] *= (constants.a0 / 1e-10)**2 rotation = LinearRotor( inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) #modes.append(rotation) rot.append(rotation) else: for i in range(3): inertia[i] *= (constants.a0 / 1e-10)**2 rotation = NonlinearRotor( inertia=(inertia, "amu*angstrom^2"), symmetry=symmetry) #modes.append(rotation) rot.append(rotation) # Read Qchem's estimate of the external rotational symmetry number, which may very well be incorrect elif 'Rotational Symmetry Number is' in line and symmetry is None: symmetry = int(float(line.split()[4])) elif 'Final energy is' in line: E0 = float( line.split()[3]) * constants.E_h * constants.Na print 'energy is' + str(E0) # Read ZPE and add to ground-state energy # NEED TO MULTIPLY ZPE BY scaling factor! elif 'Zero point vibrational energy:' in line: ZPE = float(line.split()[4]) * 4184 E0 = E0 + ZPE # Read spin multiplicity if not explicitly given # elif 'Electronic' in line and inPartitionFunctions and spinMultiplicity is None: # spinMultiplicity = int(float(line.split()[1].replace('D', 'E'))) # elif 'Log10(Q)' in line: # inPartitionFunctions = True # Read the next line in the file line = f.readline() # Read the next line in the file line = f.readline() # Close file when finished f.close() modes = mmass + rot + freq #modes.append(mmass), modes.append(rot), modes.append(freq) return Conformer(E0=(E0 * 0.001, "kJ/mol"), modes=modes, spinMultiplicity=spinMultiplicity, opticalIsomers=opticalIsomers)
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