Example #1
0
    def estimateThermoViaGroupAdditivityForSaturatedStructWithoutSymmetryCorrection(self, molecule):
        """
        Return the set of thermodynamic parameters corresponding to a given
        :class:`Molecule` object `molecule` by estimation using the group
        additivity values. If no group additivity values are loaded, a
        :class:`DatabaseError` is raised.
        
        The entropy is not corrected for the symmetry of the molecule.
        This should be done later by the calling function.
        """

        assert not molecule.isRadical(), "This method is only for saturated non-radical species."
        # For thermo estimation we need the atoms to already be sorted because we
        # iterate over them; if the order changes during the iteration then we
        # will probably not visit the right atoms, and so will get the thermo wrong
        molecule.sortVertices()

        # Create the ThermoData object
        thermoData = ThermoData(
            Tdata = ([300,400,500,600,800,1000,1500],"K"),
            Cpdata = ([0.0,0.0,0.0,0.0,0.0,0.0,0.0],"J/(mol*K)"),
            H298 = (0.0,"kJ/mol"),
            S298 = (0.0,"J/(mol*K)"),
        )

        cyclic = molecule.isCyclic()
        # 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:
                    self.__addGroupThermoData(thermoData, self.groups['group'], molecule, {'*':atom})
                except KeyError:
                    logging.error("Couldn't find in main thermo database:")
                    logging.error(molecule)
                    logging.error(molecule.toAdjacencyList())
                    raise
                # Correct for gauche and 1,5- interactions
                if not cyclic:
                    try:
                        self.__addGroupThermoData(thermoData, self.groups['gauche'], molecule, {'*':atom})
                    except KeyError: pass
                try:
                    self.__addGroupThermoData(thermoData, self.groups['int15'], molecule, {'*':atom})
                except KeyError: pass
                try:
                    self.__addGroupThermoData(thermoData, self.groups['other'], molecule, {'*':atom})
                except KeyError: pass

        # Do ring corrections separately because we only want to match
        # each ring one time
        
        if cyclic:                
            if molecule.getAllPolycyclicVertices():
                # If the molecule has fused ring atoms, this implies that we are dealing
                # with a polycyclic ring system, for which separate ring strain corrections may not
                # be adequate.  Therefore, we search the polycyclic thermo group corrections
                # instead of adding single ring strain corrections within the molecule.
                # For now, assume only one  polycyclic RSC can be found per molecule
                try:
                    self.__addGroupThermoData(thermoData, self.groups['polycyclic'], molecule, {})
                except:
                    logging.error("Couldn't find in polycyclic ring database:")
                    logging.error(molecule)
                    logging.error(molecule.toAdjacencyList())
                    raise
            else:
                rings = molecule.getSmallestSetOfSmallestRings()
                for ring in rings:
                    # Make a temporary structure containing only the atoms in the ring
                    # NB. if any of the ring corrections depend on ligands not in the ring, they will not be found!
                    try:
                        self.__addGroupThermoData(thermoData, self.groups['ring'], molecule, {})
                    except KeyError:
                        logging.error("Couldn't find in ring database:")
                        logging.error(ring)
                        logging.error(ring.toAdjacencyList())
                        raise

        return thermoData
Example #2
0
    def estimateThermoViaGroupAdditivity(self, molecule):
        """
        Return the set of thermodynamic parameters corresponding to a given
        :class:`Molecule` object `molecule` by estimation using the group
        additivity values. If no group additivity values are loaded, a
        :class:`DatabaseError` is raised.
        """
        # For thermo estimation we need the atoms to already be sorted because we
        # iterate over them; if the order changes during the iteration then we
        # will probably not visit the right atoms, and so will get the thermo wrong
        molecule.sortVertices()

        # Create the ThermoData object
        thermoData = ThermoData(
            Tdata = ([300,400,500,600,800,1000,1500],"K"),
            Cpdata = ([0.0,0.0,0.0,0.0,0.0,0.0,0.0],"J/(mol*K)"),
            H298 = (0.0,"kJ/mol"),
            S298 = (0.0,"J/(mol*K)"),
        )

        if molecule.getRadicalCount() > 0: # radical species
            return self.estimateRadicalThermoViaHBI(molecule, self.estimateThermoViaGroupAdditivity )

        else: # non-radical species
            cyclic = molecule.isCyclic()
            # 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:
                        self.__addGroupThermoData(thermoData, self.groups['group'], molecule, {'*':atom})
                    except KeyError:
                        logging.error("Couldn't find in main thermo database:")
                        logging.error(molecule)
                        logging.error(molecule.toAdjacencyList())
                        raise
                    # Correct for gauche and 1,5- interactions
                    if not cyclic:
                        try:
                            self.__addGroupThermoData(thermoData, self.groups['gauche'], molecule, {'*':atom})
                        except KeyError: pass
                    try:
                        self.__addGroupThermoData(thermoData, self.groups['int15'], molecule, {'*':atom})
                    except KeyError: pass
                    try:
                        self.__addGroupThermoData(thermoData, self.groups['other'], molecule, {'*':atom})
                    except KeyError: pass

            # Do ring corrections separately because we only want to match
            # each ring one time
            
            if cyclic:                
                if molecule.getAllPolycyclicVertices():
                    # If the molecule has fused ring atoms, this implies that we are dealing
                    # with a polycyclic ring system, for which separate ring strain corrections may not
                    # be adequate.  Therefore, we search the polycyclic thermo group corrections
                    # instead of adding single ring strain corrections within the molecule.
                    # For now, assume only one  polycyclic RSC can be found per molecule
                    try:
                        self.__addGroupThermoData(thermoData, self.groups['polycyclic'], molecule, {})
                    except:
                        logging.error("Couldn't find in polycyclic ring database:")
                        logging.error(molecule)
                        logging.error(molecule.toAdjacencyList())
                        raise
                else:
                    rings = molecule.getSmallestSetOfSmallestRings()
                    for ring in rings:
                        # Make a temporary structure containing only the atoms in the ring
                        # NB. if any of the ring corrections depend on ligands not in the ring, they will not be found!
                        try:
                            self.__addGroupThermoData(thermoData, self.groups['ring'], molecule, {})
                        except KeyError:
                            logging.error("Couldn't find in ring database:")
                            logging.error(ring)
                            logging.error(ring.toAdjacencyList())
                            raise
                            
                
        # Correct entropy for symmetry number
        molecule.calculateSymmetryNumber()
        thermoData.S298.value_si -= constants.R * math.log(molecule.symmetryNumber)

        return thermoData
Example #3
0
def calculateCyclicSymmetryNumber(molecule):
    """
    Get the symmetry number correction for cyclic regions of a molecule.
    For complicated fused rings the smallest set of smallest rings is used.
    """
    from rdkit.Chem.rdmolops import SanitizeMol
    from rdkit.Chem.rdchem import Mol

    symmetryNumber = 1

    rings = molecule.getSmallestSetOfSmallestRings()

    # Get symmetry number for each ring in structure
    for ring0 in rings:

        # Make another copy structure
        structure = molecule.copy(True)
        ring = [structure.atoms[molecule.atoms.index(atom)] for atom in ring0]

        # Remove bonds of ring from structure
        for i, atom1 in enumerate(ring):
            for atom2 in ring[i + 1 :]:
                if structure.hasBond(atom1, atom2):
                    structure.removeBond(atom1.edges[atom2])

        structures = structure.split()
        groups = []
        for struct in structures:
            for atom in ring:
                if struct.hasAtom(atom):
                    struct.removeAtom(atom)
            groups.append(struct.split())
        # Find equivalent functional groups on ring
        equivalentGroups = []
        equivalentGroupCount = []
        for group in groups:
            found = False
            for i, eqGroup in enumerate(equivalentGroups):
                if not found and len(group) == len(eqGroup):
                    for g, eg in zip(group, eqGroup):
                        if not g.isIsomorphic(eg):
                            # The groups do not match
                            break
                    else:
                        # The groups match
                        found = True
                if found:
                    # We've found a matching group, so increment its count
                    equivalentGroupCount[i] += 1
                    break
            else:
                # No matching group found, so add it as a new group
                equivalentGroups.append(group)
                equivalentGroupCount.append(1)

        # Find equivalent bonds on ring
        equivalentBonds = []
        for i, atom1 in enumerate(ring0):
            for atom2 in ring0[i + 1 :]:
                if molecule.hasBond(atom1, atom2):
                    bond = molecule.getBond(atom1, atom2)
                    found = False
                    for eqBond in equivalentBonds:
                        if not found:
                            if bond.equivalent(eqBond[0]):
                                eqBond.append(group)
                                found = True
                    if not found:
                        equivalentBonds.append([bond])

        # Find maximum number of equivalent groups and bonds
        minEquivalentGroups = min(equivalentGroupCount)
        maxEquivalentGroups = max(equivalentGroupCount)
        minEquivalentBonds = None
        maxEquivalentBonds = 0
        for bonds in equivalentBonds:
            N = len(bonds)
            if minEquivalentBonds is None or N < minEquivalentBonds:
                minEquivalentBonds = N
            if N > maxEquivalentBonds:
                maxEquivalentBonds = N

        if maxEquivalentGroups == maxEquivalentBonds == len(ring):
            symmetryNumber *= len(ring) * 2
        else:
            symmetryNumber *= min(minEquivalentGroups, minEquivalentBonds)

    return symmetryNumber
Example #4
0
def calculateCyclicSymmetryNumber(molecule):
    """
    Get the symmetry number correction for cyclic regions of a molecule.
    For complicated fused rings the smallest set of smallest rings is used.
    """
    from rdkit.Chem.rdmolops import SanitizeMol
    from rdkit.Chem.rdchem import Mol 
    mcopy = molecule.toRDKitMol(removeHs=True, returnMapping=False)
    SanitizeMol(mcopy)
    symmetryNumber = 1
            
    # Get symmetry number for each ring in structure
    rings = molecule.getSmallestSetOfSmallestRings()
    for ring0 in rings:

        # Make copy of structure
        structure = molecule.copy(True)
        ring = [structure.atoms[molecule.atoms.index(atom)] for atom in ring0]
        # Figure out which atoms and bonds are aromatic and reassign appropriately:
        for i, atom1 in enumerate(ring0):
            for atom2 in ring0[i+1:]:
                if molecule.hasBond(atom1, atom2):
                    if mcopy.GetBondBetweenAtoms(i,i+1) is not None:
                        if str(mcopy.GetBondBetweenAtoms(i,i+1).GetBondType()) == 'AROMATIC':
                            bond = molecule.getBond(atom1, atom2)
                            bond.applyAction(['CHANGE_BOND', atom1, 'B', atom2])
                            atom1.atomType = atom2.atomType = rmgpy.molecule.atomTypes['Cb']
                    else:
                        pass
        # Remove bonds of ring from structure
        for i, atom1 in enumerate(ring):
            for atom2 in ring[i+1:]:
                if structure.hasBond(atom1, atom2):
                    structure.removeBond(atom1.edges[atom2])

        structures = structure.split()
        groups = []
        for struct in structures:
            for atom in ring:
                if struct.hasAtom(atom): struct.removeAtom(atom)
            groups.append(struct.split())
        # Find equivalent functional groups on ring
        equivalentGroups = []; equivalentGroupCount = []
        for group in groups:
            found = False
            for i, eqGroup in enumerate(equivalentGroups):
                if not found and len(group) == len(eqGroup):
                    for g, eg in zip(group, eqGroup):
                        if not g.isIsomorphic(eg):
                            # The groups do not match
                            break
                    else:
                        # The groups match
                        found = True
                if found:
                    # We've found a matching group, so increment its count
                    equivalentGroupCount[i] += 1        
                    break
            else:
                # No matching group found, so add it as a new group
                equivalentGroups.append(group)
                equivalentGroupCount.append(1)

        # Find equivalent bonds on ring
        equivalentBonds = []
        for i, atom1 in enumerate(ring0):
            for atom2 in ring0[i+1:]:
                if molecule.hasBond(atom1, atom2):
                    bond = molecule.getBond(atom1, atom2)
                    found = False
                    for eqBond in equivalentBonds:
                        if not found:
                            if bond.equivalent(eqBond[0]):
                                eqBond.append(group)
                                found = True
                    if not found:
                        equivalentBonds.append([bond])

        # Find maximum number of equivalent groups and bonds
        minEquivalentGroups = min(equivalentGroupCount)
        maxEquivalentGroups = max(equivalentGroupCount)
        minEquivalentBonds = None
        maxEquivalentBonds = 0
        for bonds in equivalentBonds:
            N = len(bonds)
            if minEquivalentBonds is None or N < minEquivalentBonds:
                minEquivalentBonds = N
            if N > maxEquivalentBonds:
                maxEquivalentBonds = N

        if maxEquivalentGroups == maxEquivalentBonds == len(ring):
            symmetryNumber *= len(ring) * 2
        else:
            symmetryNumber *= min(minEquivalentGroups, minEquivalentBonds)

        #print len(ring), minEquivalentGroups, maxEquivalentGroups, minEquivalentBonds, maxEquivalentBonds, symmetryNumber


    return symmetryNumber
Example #5
0
def calculateCyclicSymmetryNumber(molecule):
    """
    Get the symmetry number correction for cyclic regions of a molecule.
    For complicated fused rings the smallest set of smallest rings is used.
    """
    from rdkit.Chem.rdmolops import SanitizeMol
    from rdkit.Chem.rdchem import Mol
    mcopy = molecule.toRDKitMol(removeHs=True, returnMapping=False)
    SanitizeMol(mcopy)
    symmetryNumber = 1

    # Get symmetry number for each ring in structure
    rings = molecule.getSmallestSetOfSmallestRings()
    for ring0 in rings:

        # Make copy of structure
        structure = molecule.copy(True)
        ring = [structure.atoms[molecule.atoms.index(atom)] for atom in ring0]
        # Figure out which atoms and bonds are aromatic and reassign appropriately:
        for i, atom1 in enumerate(ring0):
            for atom2 in ring0[i + 1:]:
                if molecule.hasBond(atom1, atom2):
                    if mcopy.GetBondBetweenAtoms(i, i + 1) is not None:
                        if str(
                                mcopy.GetBondBetweenAtoms(
                                    i, i + 1).GetBondType()) == 'AROMATIC':
                            bond = molecule.getBond(atom1, atom2)
                            bond.applyAction(
                                ['CHANGE_BOND', atom1, 'B', atom2])
                            atom1.atomType = atom2.atomType = rmgpy.molecule.atomTypes[
                                'Cb']
                    else:
                        pass
        # Remove bonds of ring from structure
        for i, atom1 in enumerate(ring):
            for atom2 in ring[i + 1:]:
                if structure.hasBond(atom1, atom2):
                    structure.removeBond(atom1.edges[atom2])

        structures = structure.split()
        groups = []
        for struct in structures:
            for atom in ring:
                if struct.hasAtom(atom): struct.removeAtom(atom)
            groups.append(struct.split())
        # Find equivalent functional groups on ring
        equivalentGroups = []
        equivalentGroupCount = []
        for group in groups:
            found = False
            for i, eqGroup in enumerate(equivalentGroups):
                if not found and len(group) == len(eqGroup):
                    for g, eg in zip(group, eqGroup):
                        if not g.isIsomorphic(eg):
                            # The groups do not match
                            break
                    else:
                        # The groups match
                        found = True
                if found:
                    # We've found a matching group, so increment its count
                    equivalentGroupCount[i] += 1
                    break
            else:
                # No matching group found, so add it as a new group
                equivalentGroups.append(group)
                equivalentGroupCount.append(1)

        # Find equivalent bonds on ring
        equivalentBonds = []
        for i, atom1 in enumerate(ring0):
            for atom2 in ring0[i + 1:]:
                if molecule.hasBond(atom1, atom2):
                    bond = molecule.getBond(atom1, atom2)
                    found = False
                    for eqBond in equivalentBonds:
                        if not found:
                            if bond.equivalent(eqBond[0]):
                                eqBond.append(group)
                                found = True
                    if not found:
                        equivalentBonds.append([bond])

        # Find maximum number of equivalent groups and bonds
        minEquivalentGroups = min(equivalentGroupCount)
        maxEquivalentGroups = max(equivalentGroupCount)
        minEquivalentBonds = None
        maxEquivalentBonds = 0
        for bonds in equivalentBonds:
            N = len(bonds)
            if minEquivalentBonds is None or N < minEquivalentBonds:
                minEquivalentBonds = N
            if N > maxEquivalentBonds:
                maxEquivalentBonds = N

        if maxEquivalentGroups == maxEquivalentBonds == len(ring):
            symmetryNumber *= len(ring) * 2
        else:
            symmetryNumber *= min(minEquivalentGroups, minEquivalentBonds)

        #print len(ring), minEquivalentGroups, maxEquivalentGroups, minEquivalentBonds, maxEquivalentBonds, symmetryNumber

    return symmetryNumber
Example #6
0
def calculateCyclicSymmetryNumber(molecule):
    """
    Get the symmetry number correction for cyclic regions of a molecule.
    For complicated fused rings the smallest set of smallest rings is used.
    """
    from rdkit.Chem.rdmolops import SanitizeMol
    from rdkit.Chem.rdchem import Mol

    symmetryNumber = 1

    rings = molecule.getSmallestSetOfSmallestRings()

    # Get symmetry number for each ring in structure
    for ring0 in rings:

        # Make another copy structure
        structure = molecule.copy(True)
        ring = [structure.atoms[molecule.atoms.index(atom)] for atom in ring0]

        # Remove bonds of ring from structure
        for i, atom1 in enumerate(ring):
            for atom2 in ring[i + 1:]:
                if structure.hasBond(atom1, atom2):
                    structure.removeBond(atom1.edges[atom2])

        structures = structure.split()
        groups = []
        for struct in structures:
            for atom in ring:
                if struct.hasAtom(atom): struct.removeAtom(atom)
            groups.append(struct.split())
        # Find equivalent functional groups on ring
        equivalentGroups = []
        equivalentGroupCount = []
        for group in groups:
            found = False
            for i, eqGroup in enumerate(equivalentGroups):
                if not found and len(group) == len(eqGroup):
                    for g, eg in zip(group, eqGroup):
                        if not g.isIsomorphic(eg):
                            # The groups do not match
                            break
                    else:
                        # The groups match
                        found = True
                if found:
                    # We've found a matching group, so increment its count
                    equivalentGroupCount[i] += 1
                    break
            else:
                # No matching group found, so add it as a new group
                equivalentGroups.append(group)
                equivalentGroupCount.append(1)

        # Find equivalent bonds on ring
        equivalentBonds = []
        for i, atom1 in enumerate(ring0):
            for atom2 in ring0[i + 1:]:
                if molecule.hasBond(atom1, atom2):
                    bond = molecule.getBond(atom1, atom2)
                    found = False
                    for eqBond in equivalentBonds:
                        if not found:
                            if bond.equivalent(eqBond[0]):
                                eqBond.append(group)
                                found = True
                    if not found:
                        equivalentBonds.append([bond])

        # Find maximum number of equivalent groups and bonds
        minEquivalentGroups = min(equivalentGroupCount)
        maxEquivalentGroups = max(equivalentGroupCount)
        minEquivalentBonds = None
        maxEquivalentBonds = 0
        for bonds in equivalentBonds:
            N = len(bonds)
            if minEquivalentBonds is None or N < minEquivalentBonds:
                minEquivalentBonds = N
            if N > maxEquivalentBonds:
                maxEquivalentBonds = N

        if maxEquivalentGroups == maxEquivalentBonds == len(ring):
            symmetryNumber *= len(ring) * 2
        else:
            symmetryNumber *= min(minEquivalentGroups, minEquivalentBonds)

    return symmetryNumber