def _get_isomorphisms(self, test_atoms): ref = self.ref mol = test_atoms # Make all bond orders 1 and reassign atom types # this is a magic recipe for bond in ob.OBMolBondIter(ref): bond.SetBondOrder(1) for bond in ob.OBMolBondIter(mol): bond.SetBondOrder(1) ob.OBAtomTyper().AssignTypes(ref) ob.OBAtomTyper().AssignTypes(mol) # DEBUG if False: rt = [a for a in ob.OBMolAtomIter(ref)] mt = [a for a in ob.OBMolAtomIter(mol)] for (r, m) in zip(rt, mt): print(r.GetType(), m.GetType(), r.GetType() == m.GetType()) r = self._atoms2obabel(ref_atoms, ref_type) m = self._atoms2obabel(mol_atoms) obutils.writeMolecule(r, 'ref.mol2', ftype='mol2') obutils.writeMolecule(m, 'mol.mol2', ftype='mol2') # Mapping magic query = ob.CompileMoleculeQuery(ref) mapper = ob.OBIsomorphismMapper.GetInstance(query) isomorphs = ob.vvpairUIntUInt() mapper.MapAll(mol, isomorphs) return isomorphs
def pymatgen_2_babel_atom_idx_map(pmg_mol, ob_mol): """ Create an atom index mapping between pymatgen mol and openbabel mol. This does not require pymatgen mol and ob mol has the same number of atoms. But ob_mol can have smaller number of atoms. Args: pmg_mol (pymatgen.Molecule): pymatgen molecule ob_mol (ob.Mol): openbabel molecule Returns: dict: with atom index in pymatgen mol as key and atom index in babel mol as value. Value is `None` if there is not corresponding atom in babel. """ pmg_coords = pmg_mol.cart_coords ob_coords = [[a.GetX(), a.GetY(), a.GetZ()] for a in ob.OBMolAtomIter(ob_mol)] ob_index = [a.GetIdx() for a in ob.OBMolAtomIter(ob_mol)] mapping = {i: None for i in range(len(pmg_coords))} for idx, oc in zip(ob_index, ob_coords): for i, gc in enumerate(pmg_coords): if np.allclose(oc, gc): mapping[i] = idx break else: raise RuntimeError( "Cannot create atom index mapping pymatgen and ob mols") return mapping
def copy_ob_mol(ob_mol): copy_mol = ob.OBMol(ob_mol) assert copy_mol.HasAromaticPerceived() == ob_mol.HasAromaticPerceived() assert copy_mol.HasHybridizationPerceived() == ob_mol.HasHybridizationPerceived() for a, b in zip(ob.OBMolAtomIter(ob_mol), ob.OBMolAtomIter(copy_mol)): assert a.GetImplicitHCount() == b.GetImplicitHCount() return copy_mol
def GetCharge(mol): """ Gets the charge of an OBMol object (Only works for protein residues) mol: An OBMol object (Protein residue) returns: The charge of an OBMol object (Protein residue) """ mol.ConnectTheDots() mol.PerceiveBondOrders() charge = 0 for atom in ob.OBMolAtomIter(mol): if atom.GetAtomicNum() == 7: charge += (atom.GetExplicitValence() - 3) elif atom.GetAtomicNum() == 8: charge += (atom.GetExplicitValence() - 2) elif atom.GetAtomicNum() == 16: charge += (atom.GetExplicitValence() - 2) return charge
def openbabel_to_rdkit_mol(mol: openbabel.OBMol) -> Chem.Mol: """Convert an OpenBabel molecule to a RDKit molecule. :param mol: OpenBabel molecule """ # Create an editable molecule rdkitmol = Chem.rdchem.EditableMol(Chem.rdchem.Mol()) for obatom in openbabel.OBMolAtomIter(mol): # Create new atom and assign values atom = Chem.Atom(obatom.GetAtomicNum()) atom.SetIsotope(obatom.GetIsotope()) atom.SetFormalCharge(obatom.GetFormalCharge()) atom.SetDoubleProp('_PartialCharge', obatom.GetPartialCharge()) atom.SetNumRadicalElectrons(obatom.GetSpinMultiplicity() - 1 if obatom.GetSpinMultiplicity() != 0 else 0) # Add it to the current molecule rdkitmol.AddAtom(atom) orders = {1: Chem.rdchem.BondType.SINGLE, 2: Chem.rdchem.BondType.DOUBLE, 3: Chem.rdchem.BondType.TRIPLE, 4: Chem.rdchem.BondType.QUADRUPLE, 5: Chem.rdchem.BondType.QUINTUPLE, 1.5: Chem.rdchem.BondType.AROMATIC} for obbond in openbabel.OBMolBondIter(mol): rdkitmol.AddBond(obbond.GetBeginAtomIdx() - 1, obbond.GetEndAtomIdx() - 1, orders[obbond.GetBondOrder()]) rdkitmol = rdkitmol.GetMol() Chem.SanitizeMol(rdkitmol) return rdkitmol
def applyRotations(coordArr, newMol): #newCoords will be the origin and direction vectors of the new base pair after the transformations are applied newCoords = coordArr[-1] origin = newCoords[0] #firstCoords is the origin and direction vectors of the nucleobase before the transformations are applied firstCoords = coordArr[0] newMol.SetChainsPerceived() for atom in openbabel.OBMolAtomIter(newMol): atomVector = np.transpose(np.array([atom.GetX(), atom.GetY(), atom.GetZ()], dtype=float)) #- firstCoords[0] length = np.linalg.norm(atomVector) oldTransMatrix = np.array([firstCoords[1], firstCoords[2], firstCoords[3]]) newTransMatrix = np.transpose(np.array([newCoords[1], newCoords[2], newCoords[3]], dtype=float)) transMatrix = np.matmul(newTransMatrix, oldTransMatrix) superVector = np.matmul(transMatrix, atomVector) atom.SetVector(superVector[0], superVector[1], superVector[2]) #Returns the nucleobase, after the rotations have been applied return newMol
def _build_automorphism(self): """ automorphisms are 0-based prune the automorphism map to remove identities and compact multiple mappings """ self._equivalents = {} automorphs = ob.vvpairUIntUInt() mol_copy = ob.OBMol(self.mol) for i in ob.OBMolAtomIter(mol_copy): isotope = i.GetIsotope() if not isotope == 0: n = i.GetAtomicNum() i.SetAtomicNum(n + isotope) ob.FindAutomorphisms(mol_copy, automorphs) self.automorphs = {} for am in automorphs: for i, j in am: if i == j: continue k = i + 1 l = j + 1 if self._is_hydrogen(k): continue if self._is_hydrogen(l): continue if not k in self.automorphs: self.automorphs[k] = [] self.automorphs[k].append(l) for k, v in list(self.automorphs.items()): self.automorphs[k] = set(v) if self.debug: from pprint import pprint print("AUTOMORPHS", end='') pprint(self.automorphs)
def confab_conformers(self, forcefield="mmff94", freeze_atoms=None, rmsd_cutoff=0.5, energy_cutoff=50.0, conf_cutoff=100000, verbose=False): """ Conformer generation based on Confab to generate all diverse low-energy conformers for molecules. This is different from rotor_conformer or gen3d_conformer as it aims to not simply to find a low energy conformation but to generate several different conformations. Args: forcefield (str): Default is mmff94. Options are 'gaff', 'ghemical', 'mmff94', 'mmff94s', and 'uff'. freeze_atoms ([int]): index of atoms to be freezed when performing conformer search, default is None. rmsd_cutoff (float): rmsd_cufoff, default is 0.5 Angstrom. energy_cutoff (float): energy_cutoff, default is 50.0 kcal/mol. conf_cutoff (float): max number of conformers to test, default is 1 million. verbose (bool): whether to display information on torsions found, default is False. Returns: (list): list of pymatgen Molecule objects for generated conformers. """ if self._obmol.GetDimension() != 3: self.make3d() else: self.add_hydrogen() ff = ob.OBForceField_FindType(forcefield) if ff == 0: print("Could not find forcefield {} in openbabel, the forcefield " "will be reset as default 'mmff94'".format(forcefield)) ff = ob.OBForceField_FindType("mmff94") if freeze_atoms: print('{} atoms will be freezed'.format(len(freeze_atoms))) constraints = ob.OBFFConstraints() for atom in ob.OBMolAtomIter(self._obmol): atom_id = atom.GetIndex() + 1 if id in freeze_atoms: constraints.AddAtomConstraint(atom_id) ff.SetConstraints(constraints) # Confab conformer generation ff.DiverseConfGen(rmsd_cutoff, conf_cutoff, energy_cutoff, verbose) ff.GetConformers(self._obmol) # Number of conformers generated by Confab conformer generation conformer_num = self._obmol.NumConformers() conformers = [] for i in range(conformer_num): self._obmol.SetConformer(i) conformer = copy.deepcopy(BabelMolAdaptor(self._obmol).pymatgen_mol) conformers.append(conformer) self._obmol.SetConformer(0) return conformers
def SideChainGrowerIterative(protein, mol, cmol): count = 1 while count > 0: count = 0 for mol_atom in ob.OBMolAtomIter(protein): if OBAminoAcid.IsInMolecule(mol_atom, cmol): count += OBAminoAcid.SideChainGrowerHelper( mol_atom, mol, cmol)
def MolGrowerIterative(mol, big_mol): atomArr = [] count = 1 while count > 0: count = 0 for mol_atom in ob.OBMolAtomIter(big_mol): if OBLigand.IsInMolecule(mol_atom, mol): count += OBLigand.MolGrowerHelper(mol_atom, mol)
def testIterators(self): """Basic check that at least two iterators are working""" mol = pybel.readstring("smi", "c1ccccc1C(=O)Cl") atoms = list(ob.OBMolAtomIter(mol.OBMol)) self.assertEqual(len(atoms), 9) elements = [atom.GetAtomicNum() for atom in atoms] self.assertEqual(elements, [6, 6, 6, 6, 6, 6, 6, 8, 17]) bonds = list(ob.OBMolBondIter(mol.OBMol)) self.assertEqual(len(bonds), 9)
def ob_mol_center(ob_mol): assert ob_mol.NumAtoms() > 0 x, y, z, n = 0 for a in ob.OBMolAtomIter(ob_mol): x += a.GetX() y += a.GetY() z += a.GetZ() n += 1 return (x/n, y/n, z/n)
def array_of_imine_carbons(OBMolecule): molecule = OBMolecule imine_carbons = list() for atom in openbabel.OBMolAtomIter(molecule): atomic_num = atom.GetAtomicNum() atom_in_ring_size = atom.MemberOfRingSize() if atomic_num == 6: if atom_in_ring_size == 0: imine_carbons.append(atom) return imine_carbons
def pymatgen_mol(self): """ Returns pymatgen Molecule object. """ sp = [] coords = [] for atom in ob.OBMolAtomIter(self._obmol): sp.append(atom.GetAtomicNum()) coords.append([atom.GetX(), atom.GetY(), atom.GetZ()]) return Molecule(sp, coords)
def IsConnectedTo(atom, mol, big_mol): if OBLigand.IsInMolecule(atom, mol): return False for mol_atom in ob.OBMolAtomIter(big_mol): if OBLigand.IsInMolecule(mol_atom, mol): for n_atom in ob.OBAtomAtomIter(mol_atom): if OBLigand.Equals(n_atom, atom): return True return False
def ob_mol_to_rd_mol(ob_mol): ''' Convert an OBMol to an RWMol, copying over the elements, coordinates, formal charges, bonds and aromaticity. ''' n_atoms = ob_mol.NumAtoms() rd_mol = Chem.RWMol() rd_conf = Chem.Conformer(n_atoms) for ob_atom in ob.OBMolAtomIter(ob_mol): rd_atom = Chem.Atom(ob_atom.GetAtomicNum()) rd_atom.SetFormalCharge(ob_atom.GetFormalCharge()) rd_atom.SetIsAromatic(ob_atom.IsAromatic()) rd_atom.SetNumExplicitHs(ob_atom.GetImplicitHCount()) rd_atom.SetNoImplicit(True) # don't use rdkit valence model rd_atom.SetHybridization(ob_hyb_to_rd_hyb(ob_atom)) idx = rd_mol.AddAtom(rd_atom) rd_coords = Geometry.Point3D( ob_atom.GetX(), ob_atom.GetY(), ob_atom.GetZ() ) rd_conf.SetAtomPosition(idx, rd_coords) rd_mol.AddConformer(rd_conf) for ob_bond in ob.OBMolBondIter(ob_mol): # OB uses 1-indexing, rdkit uses 0 i = ob_bond.GetBeginAtomIdx() - 1 j = ob_bond.GetEndAtomIdx() - 1 bond_order = ob_bond.GetBondOrder() if bond_order == 1: bond_type = Chem.BondType.SINGLE elif bond_order == 2: bond_type = Chem.BondType.DOUBLE elif bond_order == 3: bond_type = Chem.BondType.TRIPLE else: raise Exception('unknown bond order {}'.format(bond_order)) rd_mol.AddBond(i, j, bond_type) rd_bond = rd_mol.GetBondBetweenAtoms(i, j) rd_bond.SetIsAromatic(ob_bond.IsAromatic()) Chem.GetSSSR(rd_mol) # initialize ring info rd_mol.UpdatePropertyCache(strict=False) # compute valence return rd_mol
def optimize(self, mol): """ Optimize `mol`. Parameters ---------- mol : :class:`.Molecule` The molecule to be optimized. Returns ------- mol : :class:`.Molecule` The optimized molecule. """ temp_file = 'temp.mol' mol.write(temp_file) try: obConversion = openbabel.OBConversion() obConversion.SetInFormat("mol") OBMol = openbabel.OBMol() obConversion.ReadFile(OBMol, temp_file) OBMol.PerceiveBondOrders() finally: os.system('rm temp.mol') forcefield = openbabel.OBForceField.FindForceField( self._forcefield ) outcome = forcefield.Setup(OBMol) if not outcome: raise ForceFieldSetupError( f"{self._forcefield} could not be setup for {mol}" ) for step in range(self._repeat_steps): forcefield.SteepestDescent(self._sd_steps) forcefield.GetCoordinates(OBMol) forcefield.ConjugateGradients(self._cg_steps) forcefield.GetCoordinates(OBMol) position_matrix = [] for atom in openbabel.OBMolAtomIter(OBMol): # get coordinates position_matrix.append( np.array([atom.GetX(), atom.GetY(), atom.GetZ()]) ) mol = mol.with_position_matrix(np.asarray(position_matrix)) return mol
def test_mol_iteration(self): mol = parse_smiles("c12c(O[CH](C1=O)C(C)C)cc1c(c2)ccc(=O)o1") element_counts = {} for atom in ob.OBMolAtomIter(mol): n = atom.GetAtomicNum() element_counts[n] = element_counts.get(n, 0) + 1 self.assertEqual(element_counts[8], 4) bond_counts = {} for bond in ob.OBMolBondIter(mol): n = bond.GetBondOrder() if not bond.IsAromatic(): bond_counts[n] = bond_counts.get(n, 0) + 1 self.assertEqual(bond_counts[2], 2)
def test_to_OBMol(configuration): """Test creating an OBMol object from a structure.""" mol = configuration.to_OBMol() bondorder_list = [] for bond in openbabel.OBMolBondIter(mol): bondorder_list.append(bond.GetBondOrder()) atno_list = [] for atno in openbabel.OBMolAtomIter(mol): atno_list.append(mol.GetAtmoicNum()) assert configuration.atoms.atomic_numbers == atno_list assert configuration.bonds.bondorders == bondorder_list
def find_termini(self): for atom1 in ob.OBMolAtomIter(self.mol): if atom1.GetAtomicNum() == 7: for atom2 in ob.OBAtomAtomIter(atom1): if atom2.GetAtomicNum() == 6: for atom3 in ob.OBAtomAtomIter(atom2): if atom3.GetAtomicNum() == 6: for atom4 in ob.OBAtomAtomIter(atom3): if atom4.GetAtomicNum( ) == 8 and atom3.GetBond( atom4).GetBondOrder() == 2: self.NTerminus = atom1 self.CTerminus = atom3 return
def get_atom_info(self): for atom in ob.OBMolAtomIter(self.mol): N = atom.GetAtomicNum() element = ATOMIC_NUMBER[N] coordlines = "%4s %-6s %8.4f %8.4f %8.4f\n" % ( element, self._get_ff_type(atom), atom.GetX(), atom.GetY(), atom.GetZ()) self.update(atomic_info=coordlines) if atom.GetFormalCharge() != 0: conn_atom = str(atom.GetFormalCharge()) + "C" order = "S" # currently set to a single bond. tableline = "%4i%4s%4s\n" % (atom.GetIdx() - 1, conn_atom, order) self.update(bond_table=tableline)
def filter_conformers(mol, limit=3, percentage=50.): ### This is an (approximate) reimplementation of the ELF ### algorithm as implemented in the OpenEye Toolkits. ene_init = list() ene_elf = list() percentage /= 100. if percentage > 1.: percentage = 1. elif percentage < 0.: percentage = 0.1 n_confs = mol.NumConformers() mff = ob.OBForceField.FindForceField("mmff94") for conf_i in range(n_confs): mol.SetConformer(conf_i) mff.Setup(mol) mff.GetCoordinates(mol) ene_init.append(mff.Energy()) confs_init = sorted(range(n_confs), key=ene_init.__getitem__) cut_1st = int(n_confs * percentage) if cut_1st == 0: cut_1st = 1 if cut_1st < limit: limit = cut_1st for a in ob.OBMolAtomIter(mol): chg = a.GetPartialCharge() if chg < 0.: a.SetPartialCharge(chg * -1.) sff = ob.OBForceField.FindForceField("mmff94") for conf_i in range(cut_1st): mol.SetConformer(confs_init[conf_i]) sff.Setup(mol) sff.GetCoordinates(mol) ene_elf.append(sff.Energy()) confs_elf = sorted(range(cut_1st), key=ene_elf.__getitem__) confs_final = list() for conf_i in range(limit): confs_final.append(confs_init[confs_elf[conf_i]]) del_i = 0 for conf_i in range(n_confs): if conf_i not in confs_final: mol.DeleteConformer(del_i) else: del_i += 1
def get_connect_info(self): """Grab all the atoms which are flagged by this program to be connectivity points. Namely, Xe, Y, and Rn. Ac series elements are replacement Xe atoms for special bonding purposes. """ special, remove = [], [] connect_index = 0 for ind, atom in enumerate(ob.OBMolAtomIter(self.mol)): N = atom.GetAtomicNum() if N == 54 or (N >= 89 and N <= 102): connect_index += 1 con_line = "%4i " % (connect_index) X = "%12.4f %8.4f %8.4f" % (atom.GetX(), atom.GetY(), atom.GetZ()) if (N >= 89 and N <= 102): special.append((connect_index, N % 89 + 1)) net_vector, bond_vector = "", "" for neighbour in ob.OBAtomAtomIter(atom): x = neighbour.GetX() - atom.GetX() y = neighbour.GetY() - atom.GetY() z = neighbour.GetZ() - atom.GetZ() if neighbour.GetAtomicNum() == 39: net_atom = neighbour net_vector = "%12.4f %8.4f %8.4f" % (x, y, z) remove.append(net_atom) elif neighbour.GetAtomicNum() == 86: bond_atom = neighbour bond_vector = "%12.4f %8.4f %8.4f" % (x, y, z) remove.append(bond_atom) else: #TEMP if Rn does not exist bond_vector = "%12.4f %8.4f %8.4f" % (-x, -y, -z) neighbour.SetFormalCharge(connect_index) id = neighbour.GetIdx() con_line += "".join([X, bond_vector, net_vector, "\n"]) self.update(connectivity=con_line) remove.append(atom) self._remove_atoms(*remove) # include special considerations for (i, spec) in special: if spec == 2: bond_partner = 1 elif spec == 1: bond_partner = 2 else: bond_partner = 0 const_line = '%5i%5i%5i\n' % (i, spec, bond_partner) self.update(connect_flag=const_line)
def set_coordinates(self, value): """ Sets coordinates of molecule Arguments: value -- coordinates to store. Must be numpy array. """ if not isinstance(value, numpy.ndarray): raise TypeError("Argument 'value' must be of type numpy array") (n, ) = numpy.shape(value) if n != self.get_num_atoms(): raise ValueError("Argument 'value' has the wrong number of atoms") for iat, _obatom in enumerate(openbabel.OBMolAtomIter(self._obmol)): x, y, z = value[iat] _obatom.SetVector(x, y, z)
def IsProtein(mol, big_mol): for atom1 in ob.OBMolAtomIter(big_mol): if OBLigand.IsInMolecule(atom1, mol): if atom1.GetAtomicNum() == 7: for atom2 in ob.OBAtomAtomIter(atom1): if atom2.GetAtomicNum() == 6: for atom3 in ob.OBAtomAtomIter(atom2): if atom3.GetAtomicNum() == 6: for atom4 in ob.OBAtomAtomIter(atom3): if atom4.GetAtomicNum( ) == 8 and atom3.GetBond( atom4).GetBondOrder() == 2: return True return False
def readmol(fname, keep_hydrogens=False, deleted_atoms=[]): extension = fname.split('.')[-1] obmol_generator = obutils.OBMolSupplier(fname, extension) obmols = [mol for mol in obmol_generator] for obmol in obmols: to_del = [] for atom in ob.OBMolAtomIter(obmol): #if atom.IsNonPolarHydrogen(): if atom.GetAtomicNum() == 1: to_del.append(atom) if not keep_hydrogens: for atom in to_del[::-1]: obmol.DeleteAtom(atom) deleted_atoms.append(to_del) # to match indices return obmols
def make_h_explicit(self, ob_mol, atoms): ''' Make implicit hydrogens into explicit hydrogens and set their hybridization state. ''' # hydrogens are not added if this flag is set ob_mol.SetHydrogensAdded(False) ob_mol.AddHydrogens() for a in ob.OBMolAtomIter(ob_mol): if a.GetAtomicNum() == 1: a.SetHyb(1) # AddHydrogens() resets some flags self.disable_perception(ob_mol)
def SplitUp(mol): molArr = [] addNew = True for atom in ob.OBMolAtomIter(mol): for i in range(len(molArr)): if OBLigand.IsInMolecule(atom, molArr[i]): addNew = False if addNew: molArr.append(ob.OBMol()) molArr[-1].AddAtom(atom) OBLigand.MolGrowerIterative(molArr[-1], mol) addNew = True return molArr
def get_pmg_mol_from_smiles(smiles: str) -> Molecule: """ Get a pymatgen molecule from smiles representation Args: smiles: (str) smiles representation of molecule Returns: pymatgen Molecule """ b_mol = pb.readstring("smi", smiles) # noqa b_mol.make3D() b_mol = b_mol.OBMol sp = [] coords = [] for atom in ob.OBMolAtomIter(b_mol): sp.append(atom.GetAtomicNum()) coords.append([atom.GetX(), atom.GetY(), atom.GetZ()]) return Molecule(sp, coords)
def visit_mol(mol, msg): mol = copy_ob_mol(mol) visited_mols.append(mol) if self.debug: bmap = {1: '-', 2: '=', 3: '≡'} print(len(visited_mols), msg) assert (mol.HasHybridizationPerceived() and mol.HasAromaticPerceived()), 'perception is on' return for a in ob.OBMolAtomIter(mol): print(' ', (a.GetAtomicNum(), a.IsAromatic(), a.GetHyb(), a.GetImplicitHCount()), end=' ') for b in ob.OBAtomBondIter(a): print('({}{}{})'.format(b.GetBeginAtomIdx(), bmap[b.GetBondOrder()], b.GetEndAtomIdx()), end=' ') print()