def update_molecule(mol, to_single_bonds=False): """ Returns a copy of the current molecule with updated atomTypes if to_single_bonds is True, the returned mol contains only single bonds. This is useful for isomorphism comparison """ new_mol = Molecule() try: atoms = mol.atoms except AttributeError: return None atom_mapping = dict() for atom1 in atoms: new_atom = new_mol.addAtom(Atom(atom1.element)) atom_mapping[atom1] = new_atom for atom1 in atoms: for atom2 in atom1.bonds.keys(): bond_order = 1.0 if to_single_bonds else atom1.bonds[ atom2].getOrderNum() bond = Bond(atom_mapping[atom1], atom_mapping[atom2], bond_order) new_mol.addBond(bond) try: new_mol.updateAtomTypes() except AtomTypeError: pass new_mol.multiplicity = mol.multiplicity return new_mol
def testSubgraphIsomorphism(self): # Simple test comparing C-C to C-C-C (no hydrogens) mol = Molecule() c1 = Atom(getElement(6)) c2 = c1.copy() mol.addAtom(c1) mol.addAtom(c2) mol.addBond(Bond(c1, c2)) mol2 = Molecule() c1 = c1.copy() c2 = c1.copy() c3 = c1.copy() mol2.addAtom(c1) mol2.addAtom(c2) mol2.addAtom(c3) mol2.addBond(Bond(c1, c2)) mol2.addBond(Bond(c2, c3)) self.assertTrue(self.vf3.isSubgraphIsomorphic(mol2, mol, None)) self.assertFalse(self.vf3.isSubgraphIsomorphic(mol, mol2, None)) # Ring membership is a semantic property of molecules, # so straight chains are not considered sub graphs of rings hexane = Molecule().fromSMILES("C1CCCCC1") self.assertFalse(self.vf3.isSubgraphIsomorphic(hexane, mol, None)) self.assertFalse(self.vf3.isSubgraphIsomorphic(hexane, mol2, None)) # Benzene and hexane, while technically sharing the same shape, # differ in semantic information. benzene = Molecule().fromSMILES("C1=CC=CC=C1") self.assertFalse(self.vf3.isSubgraphIsomorphic(hexane, benzene, None)) # Test sub graph isomorphism on rings hexaneMinusH = hexane.copy(True) hexaneMinusH.removeVertex(hexaneMinusH.vertices[6]) self.assertTrue(self.vf3.isSubgraphIsomorphic(hexane, hexaneMinusH, None)) self.assertFalse(self.vf3.isSubgraphIsomorphic(hexaneMinusH, hexane, None)) benzeneMinusH = benzene.copy(True) benzeneMinusH.removeVertex(benzeneMinusH.vertices[6]) self.assertTrue(self.vf3.isSubgraphIsomorphic(benzene, benzeneMinusH, None)) self.assertFalse(self.vf3.isSubgraphIsomorphic(benzeneMinusH, hexane, None))
def testToAdjacencyListForNonIntegerBonds(self): """ Test the adjacency list can be created for molecules with bond orders that don't fit into single, double, triple, or benzene """ from rmgpy.molecule.molecule import Atom, Bond, Molecule atom1 = Atom(element='H', lonePairs=0) atom2 = Atom(element='H', lonePairs=0) bond = Bond(atom1, atom2, 0.5) mol = Molecule(multiplicity=1) mol.addAtom(atom1) mol.addAtom(atom2) mol.addBond(bond) adjlist = mol.toAdjacencyList() self.assertIn('H', adjlist) self.assertIn('{1,0.5}', adjlist)
def testToAdjacencyListForNonIntegerBonds(self): """ Test the adjacency list can be created for molecules with bond orders that don't fit into single, double, triple, or benzene """ from rmgpy.molecule.molecule import Atom, Bond, Molecule atom1 = Atom(element='H',lonePairs=0) atom2 = Atom(element='H',lonePairs=0) bond = Bond(atom1, atom2, 0.5) mol = Molecule(multiplicity=1) mol.addAtom(atom1) mol.addAtom(atom2) mol.addBond(bond) adjlist = mol.toAdjacencyList() self.assertIn('H', adjlist) self.assertIn('{1,0.5}',adjlist)
def getResonanceHybrid(self): """ Returns a molecule object with bond orders that are the average of all the resonance structures. """ # get labeled resonance isomers self.generate_resonance_structures(keep_isomorphic=True) # only consider reactive molecules as representative structures molecules = [mol for mol in self.molecule if mol.reactive] # return if no resonance if len(molecules) == 1: return molecules[0] # create a sorted list of atom objects for each resonance structure cython.declare(atomsFromStructures = list, oldAtoms = list, newAtoms = list, numResonanceStructures=cython.short, structureNum = cython.short, oldBondOrder = cython.float, index1 = cython.short, index2 = cython.short, newMol=Molecule, oldMol = Molecule, atom1=Atom, atom2=Atom, bond=Bond, atoms=list,) atomsFromStructures = [] for newMol in molecules: newMol.atoms.sort(key=lambda atom: atom.id) atomsFromStructures.append(newMol.atoms) numResonanceStructures = len(molecules) # make original structure with no bonds newMol = Molecule() originalAtoms = atomsFromStructures[0] for atom1 in originalAtoms: atom = newMol.addAtom(Atom(atom1.element)) atom.id = atom1.id newAtoms = newMol.atoms # initialize bonds to zero order for index1, atom1 in enumerate(originalAtoms): for atom2 in atom1.bonds: index2 = originalAtoms.index(atom2) bond = Bond(newAtoms[index1],newAtoms[index2], 0) newMol.addBond(bond) # set bonds to the proper value for structureNum, oldMol in enumerate(molecules): oldAtoms = atomsFromStructures[structureNum] for index1, atom1 in enumerate(oldAtoms): # make bond orders average of resonance structures for atom2 in atom1.bonds: index2 = oldAtoms.index(atom2) newBond = newMol.getBond(newAtoms[index1], newAtoms[index2]) oldBondOrder = oldMol.getBond(oldAtoms[index1], oldAtoms[index2]).getOrderNum() newBond.applyAction(('CHANGE_BOND',None,oldBondOrder / numResonanceStructures / 2)) # set radicals in resonance hybrid to maximum of all structures if atom1.radicalElectrons > 0: newAtoms[index1].radicalElectrons = max(atom1.radicalElectrons, newAtoms[index1].radicalElectrons) newMol.updateAtomTypes(logSpecies = False, raiseException=False) return newMol
def assign_representative_molecule(self): # create a molecule from fragment.vertices.copy mapping = self.copyAndMap() # replace CuttingLabel with CC atoms = [] additional_atoms = [] additional_bonds = [] for vertex in self.vertices: mapped_vertex = mapping[vertex] if isinstance(mapped_vertex, CuttingLabel): # replace cutting label with atom C atom_C1 = Atom(element=getElement('C'), radicalElectrons=0, charge=0, lonePairs=0) for bondedAtom, bond in mapped_vertex.edges.iteritems(): new_bond = Bond(bondedAtom, atom_C1, order=bond.order) bondedAtom.edges[atom_C1] = new_bond del bondedAtom.edges[mapped_vertex] atom_C1.edges[bondedAtom] = new_bond # add hydrogens and carbon to make it CC atom_H1 = Atom(element=getElement('H'), radicalElectrons=0, charge=0, lonePairs=0) atom_H2 = Atom(element=getElement('H'), radicalElectrons=0, charge=0, lonePairs=0) atom_C2 = Atom(element=getElement('C'), radicalElectrons=0, charge=0, lonePairs=0) atom_H3 = Atom(element=getElement('H'), radicalElectrons=0, charge=0, lonePairs=0) atom_H4 = Atom(element=getElement('H'), radicalElectrons=0, charge=0, lonePairs=0) atom_H5 = Atom(element=getElement('H'), radicalElectrons=0, charge=0, lonePairs=0) atoms.append(atom_C1) additional_atoms.extend( [atom_H1, atom_H2, atom_H3, atom_H4, atom_H5, atom_C2]) additional_bonds.extend([ Bond(atom_C1, atom_H1, 1), Bond(atom_C1, atom_H2, 1), Bond(atom_C2, atom_H3, 1), Bond(atom_C2, atom_H4, 1), Bond(atom_C2, atom_H5, 1), Bond(atom_C1, atom_C2, 1) ]) else: atoms.append(mapped_vertex) mol_repr = Molecule() mol_repr.atoms = atoms for atom in additional_atoms: mol_repr.addAtom(atom) for bond in additional_bonds: mol_repr.addBond(bond) # update connectivity mol_repr.update() # create a species object from molecule self.mol_repr = mol_repr return mapping