def generateThermoData(self, molecule): """ Determine the group additivity thermodynamic data for the given `molecule`. """ thermoData = None if sum([atom.radicalElectrons for atom in molecule.atoms]) > 0: # Make a copy of the structure so we don't change the original saturatedStruct = molecule.copy(deep=True) # Saturate structure by replacing all radicals with bonds to # hydrogen atoms added = {} for atom in saturatedStruct.atoms: for i in range(atom.radicalElectrons): H = Atom('H') bond = Bond('S') saturatedStruct.addAtom(H) saturatedStruct.addBond(atom, H, bond) if atom not in added: added[atom] = [] added[atom].append([H, bond]) atom.decrementRadical() # Update the atom types of the saturated structure (not sure why # this is necessary, because saturating with H shouldn't be # changing atom types, but it doesn't hurt anything and is not # very expensive, so will do it anyway) saturatedStruct.updateConnectivityValues() saturatedStruct.sortVertices() saturatedStruct.updateAtomTypes() # Get thermo estimate for saturated form of structure thermoData = self.generateThermoData(saturatedStruct) assert thermoData is not None, "Thermo data of saturated %s of molecule %s is None!" % (saturatedStruct, molecule) # For each radical site, get radical correction # Only one radical site should be considered at a time; all others # should be saturated with hydrogen atoms for atom in added: # Remove the added hydrogen atoms and bond and restore the radical for H, bond in added[atom]: saturatedStruct.removeBond(atom, H) saturatedStruct.removeAtom(H) atom.incrementRadical() saturatedStruct.updateConnectivityValues() thermoData += self.__getThermoData(self.radicalDatabase, saturatedStruct, {'*':atom}) # Re-saturate for H, bond in added[atom]: saturatedStruct.addAtom(H) saturatedStruct.addBond(atom, H, bond) atom.decrementRadical() # Subtract the enthalpy of the added hydrogens for H, bond in added[atom]: thermoData.H298 -= 52.103 * 4184 # Correct the entropy for the symmetry number else: # Generate estimate of thermodynamics for atom in molecule.atoms: # Iterate over heavy (non-hydrogen) atoms if atom.isNonHydrogen(): # Get initial thermo estimate from main group database try: if thermoData is None: thermoData = self.__getThermoData(self.groupDatabase, molecule, {'*':atom}) else: thermoData += self.__getThermoData(self.groupDatabase, molecule, {'*':atom}) except KeyError: print molecule print molecule.toAdjacencyList() raise # Correct for gauche and 1,5- interactions try: thermoData += self.__getThermoData(self.gaucheDatabase, molecule, {'*':atom}) except KeyError: pass try: thermoData += self.__getThermoData(self.int15Database, molecule, {'*':atom}) except KeyError: pass try: thermoData += self.__getThermoData(self.otherDatabase, molecule, {'*':atom}) except KeyError: pass # Do ring corrections separately because we only want to match # each ring one time; this doesn't work yet rings = molecule.getSmallestSetOfSmallestRings() for ring in rings: # Make a temporary structure containing only the atoms in the ring ringStructure = Molecule() for atom in ring: ringStructure.addAtom(atom) for atom1 in ring: for atom2 in ring: if molecule.hasBond(atom1, atom2): ringStructure.addBond(atom1, atom2, molecule.getBond(atom1, atom2)) # Get thermo correction for this ring thermoData += self.__getThermoData(self.ringDatabase, ringStructure, {}) return thermoData