def is_n_terminus(self, mol, atom): """ Determine if an atom is an N-terminus Args: mol (:obj:`openbabel.OBMol`): molecule atom (:obj:`openbabel.OBAtom`): atom Returns: :obj:`bool`: :obj:`True` if the atom is an N-terminus """ if atom is None: return False # check atom is nitrogen if atom.GetAtomicNum() != 7: return False # check atom has charge 0 or +1 if atom.GetFormalCharge() < 0: return False # check atom is single-bonded to at least 1 carbon has_single_c = False for bond in openbabel.OBAtomBondIter(atom): other_atom = bond.GetBeginAtom() if other_atom == atom: other_atom = bond.GetEndAtom() if bond.GetBondOrder() == 1 and other_atom.GetAtomicNum() == 6: has_single_c = True if not has_single_c: return False # check atom can form another bond other_atoms = [ other_atom.GetAtomicNum() for other_atom in openbabel.OBAtomAtomIter(atom) ] tot_bond_order = sum( [bond.GetBondOrder() for bond in openbabel.OBAtomBondIter(atom)]) other_atoms += [1] * (3 + atom.GetFormalCharge() - tot_bond_order) n_charge = atom.GetFormalCharge() n_h = other_atoms.count(1) if not ((n_charge == 1 and n_h >= 2) or (n_charge == 0 and (n_h >= 1 or len(other_atoms) <= 2))): return False # return True return True
def adjust_info(self, obatom): ''' If a atom is bonded to polarhydrogen or heteroatom :param obatom: openbabel OBAtom istance :return: bond_2_hd, bond_2_hetero : boolean ''' bonds = list(openbabel.OBAtomBondIter(obatom)) atom_id = obatom.GetId() bond_2_hd = False bond_2_hetero = False for bond in bonds: begin = bond.GetBeginAtom().GetId() end = bond.GetEndAtom().GetId() if atom_id == begin: link_atom = bond.GetEndAtom() elif atom_id == end: link_atom = bond.GetBeginAtom() else: raise Exception("Wrong bond") if link_atom.IsHydrogen(): # all the remained hydrogen is polarhydrogen bond_2_hd = True if link_atom.IsHeteroatom(): bond_2_hetero = True return bond_2_hd, bond_2_hetero
def get_torsion(mol, ob_atom_idx_0, ob_atom_idx_1): """Get torsion between the two coupled atoms. The function is relevant for atoms in a 3J coupling Arguments: mol {OBMol} -- OpenBabel molecule object ob_atom_idx_0 {int} -- index of atom 0 in OpenBabel ob_atom_idx_1 {int} -- index of atom 1 in OpenBabel Returns: float -- torsion value for the coupled pair """ atom_0 = mol.GetAtom(ob_atom_idx_0) atom_1 = mol.GetAtom(ob_atom_idx_1) for bond in ob.OBAtomBondIter(atom_0): end_atom_idx = bond.GetEndAtomIdx() if end_atom_idx == atom_0.GetIdx(): pivot_1 = bond.GetBeginAtom() else: pivot_1 = bond.GetEndAtom() pivot_2 = get_torsion_pivot(mol, atom_1.GetIdx(), pivot_1.GetIdx()) if pivot_2: return mol.GetTorsion(atom_0, pivot_1, pivot_2, atom_1) return 0
def get_angle(mol, ob_atom_idx_0, ob_atom_idx_1): """Get the angle between two atoms. The function is relevant for atoms in a 2J coupling Arguments: mol {OBMol} -- OpenBabel molecule object ob_atom_idx_0 {int} -- index of atom 0 in OpenBabel ob_atom_idx_1 {int} -- index of atom 1 in OpenBabel Returns: float -- value of angle between coupled atoms """ atom_0 = mol.GetAtom(ob_atom_idx_0) atom_1 = mol.GetAtom(ob_atom_idx_1) for bond in ob.OBAtomBondIter(atom_0): end_atom_idx = bond.GetEndAtomIdx() if end_atom_idx == atom_0.GetIdx(): pivot_atom = bond.GetBeginAtom() else: pivot_atom = bond.GetEndAtom() missing_bond = atom_1.GetBond(pivot_atom) if missing_bond: return mol.GetAngle(atom_0, pivot_atom, atom_1) return 0
def get_torsion_pivot(mol, ob_atom_idx_0, ob_atom_idx_1): """Find pivot atom of a torsion Arguments: mol {OBMol} -- OpenBabel molecule object ob_atom_idx_0 {int} -- index of atom 0 in OpenBabel ob_atom_idx_1 {int} -- index of atom 1 in OpenBabel Returns: OBAtom -- torsion pivot atom """ atom_0 = mol.GetAtom(ob_atom_idx_0) atom_1 = mol.GetAtom(ob_atom_idx_1) for bond in ob.OBAtomBondIter(atom_0): end_atom_idx = bond.GetEndAtomIdx() if end_atom_idx == atom_0.GetIdx(): pivot_atom = bond.GetBeginAtom() else: pivot_atom = bond.GetEndAtom() missing_bond = atom_1.GetBond(pivot_atom) if missing_bond: return pivot_atom return None
def Torsion3J(molname, AtomId1, AtomId2, mol, debug=False): mol, firstAtom, lastAtom = Atoms(molname, AtomId1, AtomId2, mol) if debug: print(molname, mol.GetFormula()) if debug: print(firstAtom.GetType(), firstAtom.GetId(), ':', lastAtom.GetType(), lastAtom.GetId()) for b in openbabel.OBAtomBondIter(firstAtom): # all bonds for first atom secondAtom = SecondAtom(b, firstAtom) for b2 in openbabel.OBAtomBondIter( secondAtom): # all bonds for second atom thirdAtom = SecondAtom(b2, secondAtom) lastBond = thirdAtom.GetBond(lastAtom) if lastBond: # found! if debug: print(secondAtom.GetType(), secondAtom.GetId(), '<->', thirdAtom.GetType(), thirdAtom.GetId()) return mol.GetTorsion(firstAtom, secondAtom, thirdAtom, lastAtom)
def findInternalRotors(self, Mol): rotor = [] for atom in openbabel.OBMolAtomIter(Mol): if not atom.IsHydrogen() and not atom.IsInRing(): for bond in openbabel.OBAtomBondIter(atom): ptr = bond.GetNbrAtom(atom) if ptr.GetId() < atom.GetId(): continue elif bond.GetBO() == 1 and atom.BOSum() > 1 and ptr.BOSum() > 1 and not ptr.IsHydrogen(): idx = [int(atom.GetId())+1, int(ptr.GetId())+1] rotor.append(idx) return rotor
def getatom_b(obatom, marker): b = str(0) if (marker == "0"): return '*' if (obatom.HasSingleBond()): b = 's' if (obatom.HasDoubleBond()): b = 'd' for obbond in ob.OBAtomBondIter(obatom): if (obbond.IsTriple()): b = 't' break i = 0 for obbond in ob.OBAtomBondIter(obatom): if (obbond.IsDouble()): i += 1 if (i > 1): b = 'w' if (obatom.HasAromaticBond()): b = 'd' return b
def Angle2J(molname, AtomId1, AtomId2, mol, debug=False): mol, firstAtom, lastAtom = Atoms(molname, AtomId1, AtomId2, mol) if debug: print(mol.GetFormula()) if debug: print(firstAtom.GetType(), firstAtom.GetId(), ':', lastAtom.GetType(), lastAtom.GetId()) for b in openbabel.OBAtomBondIter(firstAtom): # all bonds for first atom secondAtom = SecondAtom(b, firstAtom) lastBond = secondAtom.GetBond(lastAtom) if lastBond: # found! if debug: print('middle', secondAtom.GetId(), secondAtom.GetType()) return firstAtom.GetAngle(secondAtom, lastAtom)
def _get_neighbors(self, obatom): """ trick or treat? """ neighbors = [] for bond in ob.OBAtomBondIter(obatom): end = bond.GetEndAtom() bgn = bond.GetBeginAtom() if bgn == obatom: neighbors.append(end.GetIndex()) # Index is 0-indexed elif end == obatom: neighbors.append(bgn.GetIndex()) else: raise RuntimeError return neighbors
def get_bom(self): """ get connectivity table """ bom = np.zeros((self.na, self.na), np.int) for i in range(1, self.na + 1): ai = self.m.GetAtom(i) for bond in ob.OBAtomBondIter(ai): ia1 = bond.GetBeginAtomIdx() - 1 ia2 = bond.GetEndAtomIdx() - 1 bo = bond.GetBO() bom[ia1, ia2] = bo bom[ia2, ia1] = bo return bom
def get_bom(m): na = m.NumAtoms() bom = np.zeros((na, na), dtype=int) for i in range(na): ai = m.GetAtomById(i) for bond in ob.OBAtomBondIter(ai): ab, ae = bond.GetBeginAtom(), bond.GetEndAtom() ib1 = ab.GetIdx() - 1 ie1 = ae.GetIdx() - 1 #ib2 = ab.GetId(); ie2 = ae.GetId() #print i, ' -- ', ib1,ie1, ib2,ie2 bo = bond.GetBO() bom[ib1, ie1] = bo bom[ie1, ib1] = bo return bom
def test_atom_iteration(self): mol = parse_smiles("[U](F)(F)(F)[Cl]") atom = mol.GetAtom(1) counts = {9: 0, 17: 0} for bond in ob.OBAtomBondIter(atom): xatom = bond.GetNbrAtom(atom) n = xatom.GetAtomicNum() counts[n] += 1 self.assertEqual(counts, {9: 3, 17: 1}) counts = {9: 0, 17: 0} for atom in ob.OBAtomAtomIter(atom): n = atom.GetAtomicNum() counts[n] += 1 self.assertEqual(counts, {9: 3, 17: 1})
def perceiveBondOrders(self, Molecule, Atom): # . generate OBMol frag = openbabel.OBMol() atoms = [None] + Molecule for i in Molecule: frag.AddAtom(Atom[i].atom) [frag.AddBond(atoms.index(Atom[i].id), atoms.index(bond), 1) for bond in Atom[i].bonds if Atom[i].id in atoms and bond in atoms] frag.AssignSpinMultiplicity(True) # . find potential multi-bonds multi = [[], []] for atom in openbabel.OBMolAtomIter(frag): if atom.GetSpinMultiplicity() > 0: for bond in openbabel.OBAtomBondIter(atom): ptr = bond.GetNbrAtom(atom) if ptr.GetSpinMultiplicity() > 0: aidx = atom.GetIdx();pidx = ptr.GetIdx() bo= Atom[atoms[aidx]].bonds[atoms[pidx]] if (pidx, aidx) in multi[1]: continue multi[0].append(bo-1.0) multi[1].append((aidx, pidx)) # . sort in order of decreasing bond orders multi = sorted(zip(multi[0], multi[1]), reverse=True) # . loop over bonds in <multi> and set multi-bonds while multi: bond = list(multi[0]); del multi[0] atom = frag.GetAtom(bond[1][0]) btom = frag.GetAtom(bond[1][1]) if atom.GetSpinMultiplicity() > 0 and btom.GetSpinMultiplicity() > 0: tmp = frag.GetBond(atom, btom) tmp.SetBO(tmp.GetBO() +1) atom = self.decreaseMultiplicity(Atom=atom) atom.DecrementImplicitValence() btom = self.decreaseMultiplicity(Atom=btom) btom.DecrementImplicitValence() bond[0] -= 1.0 if bond[0] > 0: multi.append(tuple(bond)) multi = sorted(multi, reverse=True) # . return SMILES self.conv.SetInAndOutFormats('mdl', 'can') return self.conv.WriteString(frag).strip().replace('@@','@')
def test_rings(self): mol = parse_smiles("C12CNCC3C1.C2CCC3") atom = mol.GetAtom(1) self.assertTrue(atom.IsInRing()) self.assertTrue(atom.IsInRingSize(6)) self.assertTrue(atom.IsInRingSize(7)) self.assertFalse(atom.IsInRingSize(10)) self.assertEqual(atom.MemberOfRingCount(), 2) self.assertEqual(atom.MemberOfRingSize(), 6) self.assertEqual(atom.CountRingBonds(), 3) sssr = mol.GetSSSR() self.assertEqual(len(sssr), 2) ring_info = [(ring.Size(), ring) for ring in sssr] ring_info.sort() sizes = [x[0] for x in ring_info] self.assertEqual(sizes, [6, 7]) ring = ring_info[0][1] self.assertFalse(ring.IsAromatic()) self.assertEqual(ring.GetType(), "") # XXX *which* of the non-carbons is the root? That isn't documented idx = ring.GetRootAtom() # Shouldn't that be "Idx"? # Since there's only one non-C, it must be the N atom = mol.GetAtom(idx) self.assertEqual(atom.GetAtomicNum(), 7) self.assertTrue(ring.IsMember(atom)) for bond in ob.OBAtomBondIter(atom): self.assertTrue(ring.IsMember(bond)) self.assertTrue(ring.IsInRing(idx)) lssr = mol.GetLSSR() self.assertEqual(len(lssr), 2) sizes = [ring.Size() for ring in lssr] sizes.sort() self.assertEqual(sizes, [6, 7])
def molToMat(mol): n = mol.NumAtoms() # mat = np.zeros((n+3, n+1), dtype=int) mat = [[0 for _i in range(n + 1)] for _j in range(n + 3)] for i in range(1, n + 1): mat[i][0] = mol.GetAtom(i).GetAtomicNum() mat[0][i] = mat[i][0] for atom in ob.OBMolAtomIter(mol): i = atom.GetIdx() nBonds = 0 for bond in ob.OBAtomBondIter(atom): nBonds += bond.GetBondOrder() nonBondingElecs = numValenceElectron( atom.GetAtomicNum()) - nBonds - atom.GetFormalCharge() mat[i][i] = nonBondingElecs mat[n + 1][i] = nBonds mat[n + 2][i] = atom.GetFormalCharge() for bond in ob.OBMolBondIter(mol): i = bond.GetBeginAtomIdx() j = bond.GetEndAtomIdx() mat[i][j] = bond.GetBondOrder() mat[j][i] = mat[i][j] return mat
def get_single_bond_neighbour(ob_atom): """[summary] Args: ob_atom ([type]): [description] Returns: [type]: [description] """ ''' ''' for bond in ob.OBAtomBondIter(ob_atom): if not bond.IsSingle(): continue current_neighbour = bond.GetNbrAtom(ob_atom) if current_neighbour.IsHydrogen(): continue return current_neighbour return None
def extract_ligand(obmol, res_name='INH'): """ This function is used to extract the ligands which are always named 'INH' from CSAR structures. """ ligand = ob.OBMol() for residue in ob.OBResidueIter(obmol): if residue.GetName() == res_name: mapping = {} # insert the ligand atoms into the new molecule for i, atom in enumerate(ob.OBResidueAtomIter(residue), 1): ligand.InsertAtom(atom) mapping[atom.GetIdx()] = i # re-create the bonds for atom in ob.OBResidueAtomIter(residue): for bond in ob.OBAtomBondIter(atom): bgn_idx = mapping[bond.GetBeginAtomIdx()] end_idx = mapping[bond.GetEndAtomIdx()] ligand.AddBond(bgn_idx, end_idx, bond.GetBondOrder(), bond.GetFlags()) # remove the ligand from the original structure for atom_idx in sorted(mapping.keys(), reverse=True): atom = obmol.GetAtom(atom_idx) obmol.DeleteAtom(atom) obmol.DeleteResidue(residue) break return pybel.Molecule(ligand)
def _bfs(mol, startatom, D): ''' given openbabel molecule and a starting atom of type OBAtom, finds all bonds out to degree D via a breath-first search ''' visited_atoms = set() atoms_to_add = [] bonds_to_add = [] queue = deque([(startatom, 0)]) while queue: atom, depth = queue.popleft() index = atom.GetIndex() visited_atoms.add(index) atomtype = atom.GetType() if depth < D: for bond in ob.OBAtomBondIter(atom): if bond not in bonds_to_add: bonds_to_add.append(bond) if depth < D: for atom in ob.OBAtomAtomIter(atom): if atom.GetIndex() not in visited_atoms: queue.append((atom, depth + 1)) return (bonds_to_add)
def make_structure(request): def makeResidue(mol, idx, aaatoms): res = mol.NewResidue() res.SetNum(idx) for atom in ob.OBMolAtomIter(mol): if atom.GetIdx() not in aaatoms: res.AddAtom(atom) aminoacids = request.GET.getlist("as") color = request.GET.get("rescol", "#3EC1CD") mol = ob.OBMol() conv = ob.OBConversion() pattern = ob.OBSmartsPattern() pattern.Init("[NX3][$([CX4H1]([*])),$([CX4H2])][CX3](=[OX1])[OX2]") builder = ob.OBBuilder() conv.SetInAndOutFormats("sdf", "svg") conv.AddOption("d", ob.OBConversion.OUTOPTIONS) conv.AddOption("b", ob.OBConversion.OUTOPTIONS, "none") conv.ReadString(mol, str(Substrate.objects.get(pk=int(aminoacids[0])).structure)) pattern.Match(mol) mollist = pattern.GetUMapList()[0] oatom = mol.GetAtom(mollist[4]) catom = mol.GetAtom(mollist[2]) firstnatom = mol.GetAtom(mollist[0]) makeResidue(mol, 0, mollist) i = 1 for aa in aminoacids[1:]: mol2 = ob.OBMol() conv.ReadString(mol2, str(Substrate.objects.get(pk=int(aa)).structure)) pattern.Match(mol2) mollist = pattern.GetUMapList()[0] makeResidue(mol2, i, mollist) molnatoms = mol.NumAtoms() mol += mol2 natom = mol.GetAtom(molnatoms + mol2.GetAtom(mollist[0]).GetIdx()) builder.Connect(mol, catom.GetIdx(), natom.GetIdx()) foatom = mol.GetAtom(molnatoms + mol2.GetAtom(mollist[4]).GetIdx()) catom = mol.GetAtom(molnatoms + mol2.GetAtom(mollist[2]).GetIdx()) mol.DeleteHydrogens(oatom) mol.DeleteAtom(oatom) natom.SetImplicitValence(3) mol.DeleteHydrogens(natom) mol.AddHydrogens(natom) oatom = foatom i += 1 nidx = firstnatom.GetIdx() oidx = oatom.GetIdx() builder.Build(mol) natom = mol.GetAtom(nidx) oatom = mol.GetAtom(oidx) for res in ob.OBResidueIter(mol): for atom in ob.OBResidueAtomIter(res): for bond in ob.OBAtomBondIter(atom): data = ob.OBPairData() data.SetAttribute("color") data.SetValue(color) bond.CloneData(data) mol.DeleteHydrogens() gen2d = ob.OBOp.FindType("gen2d") gen2d.Do(mol) opp = oatom.GetY() - natom.GetY() adj = oatom.GetX() - natom.GetX() angle = abs(math.atan(opp / adj)) if opp > 0 and adj > 0: pass elif opp > 0 and adj < 0: angle = math.pi - angle elif opp < 0 and adj < 0: angle = math.pi + angle elif opp < 0 and adj > 0: angle = 2 * math.pi - angle angle = -angle mol.Rotate(ob.double_array([math.cos(angle), -math.sin(angle), 0, math.sin(angle), math.cos(angle), 0, 0, 0, 1])) svg = conv.WriteString(mol) # need to get rid of square aspect ratio delstart = svg.find("width") delend = svg.find("svg", delstart) delend = svg.find("viewBox", delend) svgend = svg.rfind("</g>") svg = svg[0:delstart] + svg[delend:svgend] return HttpResponse(svg, mimetype="image/svg+xml")
def draw(self, show=True, filename=None, update=False, usecoords=False, method="mcdl"): """Create a 2D depiction of the molecule. Optional parameters: show -- display on screen (default is True) filename -- write to file (default is None) update -- update the coordinates of the atoms to those determined by the structure diagram generator (default is False) usecoords -- don't calculate 2D coordinates, just use the current coordinates (default is False) method -- two methods are available for calculating the 2D coordinates: OpenBabel's "mcdl" (the default), or "oasa" (from the OASA toolkit) OASA is used for depiction. Tkinter and Python Imaging Library are required for image display. """ etab = ob.OBElementTable() if not oasa: errormessage = ("OASA not found, but is required for depiction. " "OASA is part of BKChem. " "See installation instructions for more " "information.") raise ImportError(errormessage) if method not in ["mcdl", "oasa"]: raise ValueError("Method '%s' not recognised. Should be either" " 'mcdl' or 'oasa'.") workingmol = self if method == "mcdl": if not update: # Call gen2D on a clone workingmol = Molecule(self) if not usecoords: _operations['gen2D'].Do(workingmol.OBMol) usecoords = True # Use the workingmol's coordinates mol = oasa.molecule() for atom in workingmol.atoms: v = mol.create_vertex() v.symbol = etab.GetSymbol(atom.atomicnum) v.charge = atom.formalcharge if usecoords: v.x, v.y, v.z = atom.coords[0] * 30., atom.coords[1] * 30., 0.0 mol.add_vertex(v) for bond in ob.OBMolBondIter(workingmol.OBMol): e = mol.create_edge() e.order = bond.GetBO() if bond.IsHash(): e.type = "h" elif bond.IsWedge(): e.type = "w" mol.add_edge(bond.GetBeginAtomIdx() - 1, bond.GetEndAtomIdx() - 1, e) # I'm sure there's a more elegant way to do the following, but here goes... # let's set the stereochemistry around double bonds self.write("can") # Perceive UP/DOWNness for bond in ob.OBMolBondIter(workingmol.OBMol): ends = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx() if bond.GetBO() == 2: stereobonds = [[b for b in ob.OBAtomBondIter(workingmol.OBMol.GetAtom(x)) if b.GetIdx() != bond.GetIdx() and (b.IsUp() or b.IsDown())] for x in ends] if stereobonds[0] and stereobonds[1]: # Needs to be defined at either end if stereobonds[0][0].IsUp() == stereobonds[1][0].IsUp(): # Either both up or both down stereo = oasa.stereochemistry.cis_trans_stereochemistry.SAME_SIDE else: stereo = oasa.stereochemistry.cis_trans_stereochemistry.OPPOSITE_SIDE atomids = [(b[0].GetBeginAtomIdx(), b[0].GetEndAtomIdx()) for b in stereobonds] extremes = [] for id, end in zip(ends, atomids): if end[0] == id: extremes.append(end[1]) else: extremes.append(end[0]) center = mol.get_edge_between(mol.atoms[ends[0] - 1], mol.atoms[ends[1] - 1]) st = oasa.stereochemistry.cis_trans_stereochemistry( center = center, value = stereo, references = (mol.atoms[extremes[0] - 1], mol.atoms[ends[0] - 1], mol.atoms[ends[1] - 1], mol.atoms[extremes[1] - 1])) mol.add_stereochemistry(st) mol.remove_unimportant_hydrogens() if method == "oasa" and not usecoords: oasa.coords_generator.calculate_coords(mol, bond_length=30) if update: newcoords = [(v.x / 30., v.y / 30., 0.0) for v in mol.vertices] for atom, newcoord in zip(ob.OBMolAtomIter(self.OBMol), newcoords): atom.SetVector(*newcoord) if filename or show: maxx = max([v.x for v in mol.vertices]) minx = min([v.x for v in mol.vertices]) maxy = max([v.y for v in mol.vertices]) miny = min([v.y for v in mol.vertices]) maxcoord = max(maxx - minx, maxy - miny) fontsize = 16 bondwidth = 6 linewidth = 2 if maxcoord > 270: # 300 - margin * 2 for v in mol.vertices: v.x *= 270. / maxcoord v.y *= 270. / maxcoord fontsize *= math.sqrt(270. / maxcoord) bondwidth *= math.sqrt(270. / maxcoord) linewidth *= math.sqrt(270. / maxcoord) if filename: filedes = None else: filedes, filename = tempfile.mkstemp() canvas = oasa.cairo_out.cairo_out() canvas.show_hydrogens_on_hetero = True canvas.font_size = fontsize canvas.bond_width = bondwidth canvas.line_width = linewidth canvas.mol_to_cairo(mol, filename) if show: if not tk: errormessage = ("Tkinter or Python Imaging " "Library not found, but is required for image " "display. See installation instructions for " "more information.") raise ImportError(errormessage) root = tk.Tk() root.title((hasattr(self, "title") and self.title) or self.__str__().rstrip()) frame = tk.Frame(root, colormap="new", visual='truecolor').pack() image = PIL.open(filename) imagedata = piltk.PhotoImage(image) label = tk.Label(frame, image=imagedata).pack() quitbutton = tk.Button(root, text="Close", command=root.destroy).pack(fill=tk.X) root.mainloop() if filedes: os.close(filedes) os.remove(filename)
def atomTotalBondOrder(atom): nBonds = 0 for bond in ob.OBAtomBondIter(atom): nBonds += bond.GetBondOrder() return nBonds
def set_termini(self, mol, monomer, i_n, i_c): """ Set the C and N terminal bond atoms of a monomer Args: mol (:obj:`openbabel.OBMol`): molecule monomer (:obj:`Monomer`): monomer i_n (:obj:`int`): index of N terminus i_c (:obj:`int`): index of C terminus """ if i_c: c_atom = mol.GetAtom(i_c) i_o = None for bond in openbabel.OBAtomBondIter(c_atom): if bond.GetBondOrder() != 1: continue o_atom = bond.GetBeginAtom() if o_atom.GetIdx() == c_atom.GetIdx(): o_atom = bond.GetEndAtom() if o_atom.GetAtomicNum() == 8: other_atoms = sorted([ other_atom.GetAtomicNum() for other_atom in openbabel.OBAtomAtomIter(o_atom) ]) if len(other_atoms) == 1 or other_atoms[-2] == 1: i_o = o_atom.GetIdx() break if i_o: monomer.r_bond_atoms.append( Atom(Monomer, element='C', position=i_c)) if o_atom.GetFormalCharge() == 0: monomer.r_displaced_atoms.append( Atom(Monomer, element='O', position=i_o)) monomer.r_displaced_atoms.append( Atom(Monomer, element='H', position=i_o)) else: monomer.r_displaced_atoms.append( Atom(Monomer, element='O', position=i_o, charge=-1)) if i_n: atom = mol.GetAtom(i_n) other_atoms = [ other_atom.GetAtomicNum() for other_atom in openbabel.OBAtomAtomIter(atom) ] tot_bond_order = sum([ bond.GetBondOrder() for bond in openbabel.OBAtomBondIter(atom) ]) other_atoms += [1] * (3 + atom.GetFormalCharge() - tot_bond_order) n_charge = atom.GetFormalCharge() n_h = other_atoms.count(1) monomer.l_bond_atoms.append( Atom(Monomer, element='N', position=i_n, charge=-n_charge)) if n_charge >= 0 and n_h >= 1: monomer.l_displaced_atoms.append( Atom(Monomer, element='H', position=i_n)) if n_charge >= 1 and n_h >= 2: monomer.l_displaced_atoms.append( Atom(Monomer, element='H', position=i_n, charge=1))
def is_c_terminus(self, mol, atom, residue=True, convert_to_aa=False): """ Determine if an atom is an C-terminus Args: mol (:obj:`openbabel.OBMol`): molecule atom (:obj:`openbabel.OBAtom`): atom residue (:obj:`bool`, optional): if :obj:`True`, search for a residue (H instead of O- at C terminus) convert_to_aa (:obj:`bool`, optional): if :obj:`True`, convert COH to COOH Returns: :obj:`bool`: :obj:`True` if the atom is an C-terminus """ if atom is None: return False # check atom is carbon if atom.GetAtomicNum() != 6: return False # check atom has charge 0 if atom.GetFormalCharge() != 0: return False # check atom is bonded to 1 oxygen, 1 carbon, and 1 hydrogen atom (residue=True) or 1 oxygen (residue=False) atom other_atoms = [ other_atom.GetAtomicNum() for other_atom in openbabel.OBAtomAtomIter(atom) ] tot_bond_order = sum( [bond.GetBondOrder() for bond in openbabel.OBAtomBondIter(atom)]) other_atoms += [1] * (4 + atom.GetFormalCharge() - tot_bond_order) other_atoms.sort() if set(other_atoms).difference(set([1, 6, 8])): return False if 6 not in other_atoms or 8 not in other_atoms: return False if len(other_atoms) != 3: return False if residue and other_atoms[0] != 1: return False if not residue and other_atoms[1] != 8: return False # check bonds to carbon and hydrogen are single bonds and bond to oxygen is a double bond o_single_bonds = [] o_double_bonds = [] for bond in openbabel.OBAtomBondIter(atom): other_atom = bond.GetBeginAtom() if other_atom == atom: other_atom = bond.GetEndAtom() if other_atom.GetAtomicNum() == 8: if bond.GetBondOrder() == 1: o_single_bonds.append((bond, other_atom)) elif bond.GetBondOrder() == 2: o_double_bonds.append((bond, other_atom)) else: return False elif bond.GetBondOrder() != 1: return False if residue and (len(o_single_bonds) != 0 or len(o_double_bonds) != 1): return False if not residue and (len(o_single_bonds) != 1 or len(o_double_bonds) != 1): return False # if not residue, check that single oxygen is bound to H if not residue: oxygen_atom = o_single_bonds[0][1] for bond in openbabel.OBAtomBondIter(oxygen_atom): other_atom = bond.GetBeginAtom() if other_atom == oxygen_atom: other_atom = bond.GetEndAtom() if other_atom.GetIdx() != atom.GetIdx( ) and other_atom.GetAtomicNum() != 1: return False # correct residue to amino acid if convert_to_aa: h_atom = None for other_atom in openbabel.OBAtomAtomIter(atom): if other_atom.GetAtomicNum() == 1: h_atom = other_atom break if h_atom is not None: h_atom.SetAtomicNum(8) else: o_atom = mol.NewAtom() o_atom.SetAtomicNum(8) o_atom.SetFormalCharge(0) bond = openbabel.OBBond() bond.SetBegin(atom) bond.SetEnd(o_atom) bond.SetBondOrder(1) assert mol.AddBond(bond) # return True return True