def getObjects(self, pick): for o, val in pick.hits.items(): #loop over geometries primInd = map(lambda x: x[0], val) g = o.mol.geomContainer if g.geomPickToBonds.has_key(o.name): func = g.geomPickToBonds[o.name] if func: return func(o, primInd) else: l = [] bonds = g.atoms[o.name].bonds[0] if not len(bonds): return BondSet() for i in range(len(primInd)): l.append(bonds[int(primInd[i])]) return BondSet(l)
def makeUniq(self, bnds): if not self.uniq: return bnds #if called with an empty list # an empty list is returned d = {} for b in bnds: d[b] = 0 uniqBonds = d.keys() if len(uniqBonds) == len(bnds): return BondSet(bnds) else: #if there's a duplicate #return list with duplicate removed return BondSet(uniqBonds)
def select(self, bonds=None): """ use self.criteria to select some bnds from input bonds """ #make sure that select is called with some bonds if not bonds: return BondSet([]) if not len(bonds): return BondSet([]) assert isinstance(bonds, BondSet) returnBonds = BondSet() for f in self.criteria: newBonds = filter(f, bonds) returnBonds.extend(BondSet(filter(f, bonds))) return self.makeUniq(returnBonds)
def select(self, bnds): #bonds of carbon bonded to 3 nitrogens c_bnds = self.CSel.select(bnds) c3_bnds = self.Sel3.select(c_bnds) #n_bnds = self.NSel.select(c3_bnds) #nb without lambda expression, this selected PRO bonds N-CA, N-CD # and CYS C-N, THR C-N #c3_ats = AtomSet(filter(lambda x:x.element=='C', self.getAtoms(c3_bnds))) cats = self.getAtoms(c3_bnds) c3_ats = cats.get(lambda x: len(x.bonds) == 3) cycle_bonds = CycleBondSelector().select(bnds) ansBnds = BondSet() for at in c3_ats: incycle_ct = 0 #keep track of bonds in cycles for vina tutorial ligand.pdb ok = True for b in at.bonds: at2 = b.neighborAtom(at) if at2.element != 'N': ok = 0 if b in cycle_bonds: incycle_ct += 1 if ok and incycle_ct < 2: #if 2 of 3 this carbon's bonds are in cycle, NOT guanidinium for b in at.bonds: if b in bnds: ansBnds.append(b) return self.makeUniq(ansBnds)
def mergeLPS(self, atoms, renumber=1): if len(atoms.bonds[0]) == 0: print "WARNING atoms have no bonds....BUILDING THEM!!!" tops = atoms.top.uniq() for t in tops: t.buildBondsByDistance() #lps = atoms.get(lambda x: x.element=='Xx' and \ #(x.name[0]=='L' or x.name[1]=='L')) lps = atoms.get(lambda x: x.element=='Lp' or x.element=='lp' or \ (x.element=='Xx' and (x.name[0]=='L' or x.name[1]=='L'))) if lps is None or len(lps) == 0: return [] #if there aren't any, just return empty list tops = lps.top.uniq() for mol in tops: #t_lps are the nonpolarHydrogens in this molecule, 'mol' mol_lps = lps.get(lambda x: x.top == mol) #clean up t.allAtoms shortcut mol.allAtoms = mol.allAtoms - mol_lps #deal with the charges, if there are any chList = mol_lps[0]._charges.keys() for lp in mol_lps: chs = lp._charges.keys() for c in chList: if c not in chs: chList.remove(c) if not len(chList): print 'no charges on carbons to increment' else: for chargeSet in chList: for lp in mol_lps: if len(lp.bonds) == 0: print lp.full_name(), ' has no bonds!' else: c_atom = lp.bonds[0].atom1 if c_atom == lp: c_atom = lp.bonds[0].atom2 c_atom._charges[ chargeSet] = c_atom.charge + lp.charge #have to do this loop separately because there may not be charges len_lps = len(lps) for lp in lps: #b = at.bonds[0] #could have hydrogens with >1bond!!!(YIKES) for b in lp.bonds: c = b.atom1 if c == lp: c = b.atom2 c.bonds.remove(b) lp.bonds = BondSet() lp.parent.remove(lp) del lp if renumber: lenAts = len(mol.chains.residues.atoms) assert lenAts == len(mol.allAtoms) mol.allAtoms.number = range(1, lenAts + 1) return len_lps
def select(self, bnds): resSet = self.getAtoms(bnds).parent.uniq() aromResSet = resSet.get(lambda x: x.type in list(self.aromDict.keys())) if not aromResSet: return BondSet() bondDict = {} pep_arom = BondSet() for i in range(len(aromResSet)): res = aromResSet[i] keys = self.aromDict[res.type] res_bnds = bondDict[i + 1] = res.atoms.get(lambda x, keys=keys: x.name in keys).bonds[0] for b in res_bnds: b.cyclenum = i + 1 b.aromatic = 1 # ??? if b in bnds: pep_arom.append(b) return pep_arom
def select(self, bnds): ats = self.getAtoms(bnds) bbBnds = BondSet() bb_ats = ats.get(lambda x: x.parent.type in self.std_res_types and x.name in ['N', 'C', 'CA']) if bb_ats: bbBnds = bb_ats.bonds[0] # remove any (small) cyclebonds first!! cycle_bnds = self.cycleBondSelector.select(bbBnds) bbBnds = bbBnds - cycle_bnds # print "found ", len(bbBnds), " PeptideBackBoneBonds" return self.makeUniq(bbBnds)
def select(self, bnds): n_bnds = self.NSel.select(bnds) #bonds between Nitrogens and Carbons nc_bnds = self.CSel.select(n_bnds) ansBnds = BondSet() for b in nc_bnds: a0 = b.atom1 a2 = b.atom2 if a0.element == 'N': a2 = b.atom1 a0 = b.atom2 if len(a0.bonds) == 3: #get bonds from this carbon to oxygen o_bnds = self.OSel.select(BondSet(a0.bonds)) if not len(o_bnds): continue #get bonds from this carbon to leaf oxygen resBnds = self.lSel.select(o_bnds) if len(resBnds): ansBnds.append(b) return self.makeUniq(ansBnds)
def select(self, bnds, detectAll=False): ats = self.getAtoms(bnds) rf = RingFinder() if self.useMaxSize: # increase maxSize of ring rf.findRings2(ats, bnds, maxSize=len(ats)) else: #limit maxSize of ring to default 20 rf.findRings2(ats, bnds) #check for connected cycles bonds = BondSet(rf.allRingBonds) if detectAll and len(rf.allRingBonds): ats = BondSet(rf.allRingBonds).getAtoms() ANS = {} for a in ats: for b in a.bonds: if b.atom1 in ats and b.atom2 in ats: ANS[b] = 1 #return BondSet(rf.allRingBonds) bonds = BondSet(ANS.keys()) return bonds
def select(self, bnds, bondOrder=1): ats = self.getAtoms(bnds) mols = ats.top.uniq() atype = AtomHybridization() for m in mols: if not m.chains[0].hasBonds: m.buildBondsByDistance() allAts = m.chains.residues.atoms atype.assignHybridization(allAts) rf = RingFinder() rf.findRings2(allAts, allAts.bonds[0]) bo = BondOrder() bo.assignBondOrder(allAts, allAts.bonds[0], rf) return BondSet(bnds.get(lambda x, ord=bondOrder: x.bondOrder == ord))
def getPeptideBondDict(self): resSet = self.chains.residues.get(lambda x, \ d = list(aromDict.keys()):x.type in d) if not resSet: return BondSet() self.cyclecount = numres = len(resSet) #NB: cyclenum is 1 based because 0 means not numbered for i in range(numres): res = resSet[i] ats = res.atoms #bondDict keys are 1 based too keys = aromDict[res.type] bnds = bondDict[i+1] = ats.get(lambda x, \ keys=keys:x.name in keys).bonds[0] for b in bnds: bnds.cyclenum = i + 1 self.bondDict = bondDict
def mergeNPHS(self): lenNPHS = len(self.nphs) if not lenNPHS: return nphs = self.nphs atLen = len(self.allAtoms) self.allAtoms = self.allAtoms - nphs #first add charge to nph's heavy atom chList = list(nphs[0]._charges.keys()) if not len(chList): print('no charges present') return 'XXX' #check that all nphs have a certain kind of charge for at in nphs: chs = list(at._charges.keys()) for c in chList: if c not in chs: chList.remove(c) if len(chList): for chargeSet in chList: for h in nphs: b = h.bonds[0] c = b.atom1 if c == h: c = b.atom2 c._charges[chargeSet] = c._charges[chargeSet] + h._charges[ chargeSet] #next delete nphs for h in nphs: b = h.bonds[0] c = b.atom1 if c == h: c = b.atom2 c.bonds.remove(b) h.bonds = BondSet() h.parent.remove(h) #nb: #these don't show up in deleteMol.getFreeMemoryInformation del h self.nphs = AtomSet() return ' and merged them\n', nphs
def clear(self): """clear BondSet of selected objects""" self.objects = BondSet([])
#torsions: dihe = [] for ind in range(ndihe): dihe.append(dscale * random.uniform(-1,1)) if verbose: print('dihe=', dihe) conf = Conformation(m, origin, TRANS, rot, dihe) new_coords = conf.getCoords() #verify that no new bonds would be formed for this conformation m.allAtoms.updateCoords(new_coords, ind=coord_index) #remove all original bonds and set hasBonds to 0 on all levels del(m.allAtoms.bonds) m.hasBonds=0 for c in m.chains: c.hasBonds=0 for r in m.chains.residues: r.hasBonds=0 for a in m.allAtoms: a.bonds = BondSet([]) a.hasBonds = 0 m.buildBondsByDistance() newLen = len(m.allAtoms.bonds[0]) if verbose: print("originally %d bonds form; after transformation %d bonds form" %(orig, newLen)) new_d = {} for a in m.allAtoms: new_d[a]=set(a.bonds.getAtoms()) ok = True for a in m.allAtoms: if orig_d[a]!=new_d[a]: ok = False if verbose: print("bonds differ for %s: %d v %d" %(a.full_name(), len(orig_d[a]), len(new_d[a]))) #for a in m.allAtoms: # assert orig_d[a]==new_d[a], '%s bonds differ: %d v %d' %(a.full_name(), len(orig_d[a]), len(new_d[a])) if verbose: print("end of try ", new_try+1)
def mergeNPHS(self, atoms, renumber=1): if len(atoms.bonds[0]) == 0: print "WARNING atoms have no bonds....BUILDING THEM!!!" tops = atoms.top.uniq() for t in tops: t.buildBondsByDistance() #MAKE sure there are some hydrogens hs = atoms.get(lambda x: x.element == 'H') if hs is None or len(hs) == 0: return 0 #Check whether there are any hydrogens with no bonds no_bnd_hs = hs.get(lambda x: len(x.bonds) == 0) if len(no_bnd_hs): print "Warning: hydrogens, ", no_bnd_hs.name, " , with no bonds!" if len(no_bnd_hs) == len(hs): return 0 bonded_hs = hs.get(lambda x: len(x.bonds) > 0) #next check is superfluous if bonded_hs is None or len(bonded_hs) == 0: print "Warning: no hydrogens with bonds!" return 0 #Check whether there are any nphs hydrogens nphs = bonded_hs.get(lambda x: x.bonds[0].atom1.element=='C' \ or x.bonds[0].atom2.element=='C') if nphs is None or len(nphs) == 0: return 0 #if there aren't any, just return 0 #if there are nphs, merge the charges + remove nphs tops = nphs.top.uniq() for t in tops: #t_nphs are the nonpolarHydrogens in this molecule, 't' t_nphs = nphs.get(lambda x: x.top == t) #clean up t.allAtoms shortcut t.allAtoms = t.allAtoms - t_nphs #deal with the charges, if there are any chList = t_nphs[0]._charges.keys() for h in t_nphs: chs = h._charges.keys() for c in chList: if c not in chs: chList.remove(c) if not len(chList): print 'no charges on carbons to increment' else: for chargeSet in chList: for h in t_nphs: if len(h.bonds) == 0: print h.full_name(), ' has no bonds!' else: c_atom = h.bonds[0].atom1 if c_atom == h: c_atom = h.bonds[0].atom2 c_atom._charges[ chargeSet] = c_atom.charge + h.charge #have to do this loop separately because there may not be charges len_nphs = len(nphs) for h in nphs: #b = at.bonds[0] #could have hydrogens with >1bond!!!(YIKES) for b in h.bonds: c = b.atom1 if c == h: c = b.atom2 c.bonds.remove(b) h.bonds = BondSet() h.parent.remove(h) del h if renumber: lenAts = len(t.chains.residues.atoms) assert lenAts == len(t.allAtoms) t.allAtoms.number = range(1, lenAts + 1) return len_nphs
else: print("no residue found using string ", selStr) #if verbose: print "built all_res=", all_res.full_name() #check for duplicates d = {} for res in all_res: d[res] = 1 all_res = list(d.keys()) all_res = ResidueSet(all_res).uniq() all_res.sort() if verbose: print("located ", len(all_res), " residues to format:") for z in all_res: print(" %s" % z.full_name()) all_bnds = BondSet() #inactivate bonds between specified atoms: #all_disallowed_pairs eg "1g9v_rec:A:ARG532:CA_CB,CB_CG,C_CA;1g9v_rec:B:ARG532:CA_CB,CB_CG" if len(all_disallowed_pairs): if verbose: print("line 183: all_disallowed_pairs = ", all_disallowed_pairs) disallowed_pairs = all_disallowed_pairs.split( ';') #';' between different residues for dp in disallowed_pairs: #1g9v_rec:A:ARG532:CA_CB,CB_CG,C_CA #find the residue recN, chainN, resN, res_bnds = dp.split( ':') #1g9v_rec, A, ARG532, CA_CB,CB_CB,C_CA if recN != rec.name: print(dp, " does not match ", rec.name) exit() #result, msg = CompoundStringSelector().select(ProteinSet([rec]), dp)
def detectPiInteractions(self, tolerance=0.95, debug=False, use_all_cycles=False): if debug: print("in detectPiInteractions") self.results['pi_pi'] = [] #stacked rings...? self.results['t_shaped'] = [] #one ring perpendicular to the other self.results['cation_pi'] = [] # self.results['pi_cation'] = [] # self.results['macro_cations'] = []# self.results['lig_cations'] = [] # #at this point have self.results if not len(self.results['lig_close_atoms']): return lig_atoms = self.results['lig_close_atoms'].parent.uniq().atoms macro_res = self.results['macro_close_atoms'].parent.uniq() if not len(macro_res): return macro_atoms = macro_res.atoms l_rf = RingFinder() #Ligand l_rf.findRings2(lig_atoms, lig_atoms.bonds[0]) #rf.rings is list of dictionaries, one per ring, with keys 'bonds' and 'atoms' if debug: print("LIG: len(l_rf.rings)=", len(l_rf.rings)) if not len(l_rf.rings): if debug: print("no lig rings found by l_rf!") return acbs = self.aromatic_cycle_bond_selector #acbs = AromaticCycleBondSelector() lig_rings = [] for r in l_rf.rings: ring_bnds = r['bonds'] if use_all_cycles: lig_rings.append(ring_bnds) else: arom_bnds = acbs.select(ring_bnds) if len(arom_bnds)>4: lig_rings.append(arom_bnds) if debug: print("LIG: len(lig_arom_rings)=", len(lig_rings)) self.results['lig_rings'] = lig_rings self.results['lig_ring_atoms'] = AtomSet() #only check for pi-cation if lig_rings exist if len(lig_rings): macro_cations = self.results['macro_cations'] = self.getCations(macro_atoms) macro_cations = macro_cations.get(lambda x: x.element!='H') lig_ring_atoms = AtomSet() u = {} for r in lig_rings: for a in BondSet(r).getAtoms(): u[a] = 1 if len(u): lig_ring_atoms = AtomSet(list(u.keys())) lig_ring_atoms.sort() self.results['lig_ring_atoms'] = lig_ring_atoms if len(macro_cations): if debug: print("check distances from lig_rings to macro_cations here") #macro cations->lig rings pairDict2 = self.distanceSelector.select(lig_ring_atoms,macro_cations) z = {} for key,v in list(pairDict2.items()): val = v.tolist()[0] if val in macro_cations: z[val] = [key] if len(z): self.results['pi_cation'] = (list(z.items())) else: self.results['pi_cation'] = [] #check the distance between the rings and the macro_cations self.results['lig_cations'] = self.getCations(lig_atoms) lig_cations = self.results['lig_cations'] #remove hydrogens lig_cations = lig_cations.get(lambda x: x.element!='H') #Macromolecule m_rf = RingFinder() m_rf.findRings2(macro_res.atoms, macro_res.atoms.bonds[0]) #rf.rings is list of dictionaries, one per ring, with keys 'bonds' and 'atoms' if debug: print("MACRO: len(m_rf.rings)=", len(m_rf.rings)) if not len(m_rf.rings): if debug: print("no macro rings found by m_rf!") return macro_rings = [] for r in m_rf.rings: ring_bnds = r['bonds'] if use_all_cycles: macro_rings.append(ring_bnds) else: arom_bnds = acbs.select(ring_bnds) if len(arom_bnds)>4: macro_rings.append(arom_bnds) if debug: print("len(macro_arom_rings)=", len(macro_rings)) self.results['macro_rings'] = macro_rings self.results['macro_ring_atoms'] = AtomSet() #only check for pi-cation if macro_rings exist if len(macro_rings): macro_ring_atoms = AtomSet() u = {} for r in macro_rings: for a in BondSet(r).getAtoms(): #new method of bondSets u[a] = 1 if len(u): macro_ring_atoms = AtomSet(list(u.keys())) macro_ring_atoms.sort() self.results['macro_ring_atoms'] = macro_ring_atoms if len(lig_cations): if debug: print("check distances from macro_rings to lig_cations here") pairDict3 = self.distanceSelector.select(macro_ring_atoms,lig_cations) z = {} for x in list(pairDict3.items()): #lig cations->macro rings z.setdefault(x[1].tolist()[0], []).append(x[0]) if len(z): self.results['cation_pi'] = (list(z.items())) else: self.results['cation_pi'] = [] #macro_pi_atoms = AtomSet(pairDict3.keys()) #l_cations = AtomSet() #for v in pairDict3.values(): # for x in v: # l_cations.append(x) #self.results['cation_pi'] = pairDict3.items() #self.results['cation_pi'] = (l_cations, macro_pi_atoms) #check for intermol distance <6 Angstrom (J.ComputChem 29:275-279, 2009) #compare each lig_ring vs each macro_ring for lig_ring_bnds in lig_rings: lig_atoms = acbs.getAtoms(lig_ring_bnds) lig_atoms.sort() if debug: print("len(lig_atoms)=", len(lig_atoms)) #--------------------------------- # compute the normal to lig ring #--------------------------------- a1 = numpy.array(lig_atoms[0].coords) a2 = numpy.array(lig_atoms[2].coords) a3 = numpy.array(lig_atoms[4].coords) if debug: print("a1,a2, a3=", a1.tolist(), a2.tolist(), a3.tolist()) for macro_ring_bnds in macro_rings: macro_atoms = acbs.getAtoms(macro_ring_bnds) macro_atoms.sort() if debug: print("len(macro_atoms)=", len(macro_atoms)) pD_dist = self.distanceSelectorWithCutoff.select(macro_ring_atoms, lig_atoms, cutoff=self.dist_cutoff) if not len(pD_dist[0]): if debug: print("skipping ligand ring ", lig_rings.index(lig_ring_bnds), " vs ", end=' ') print("macro ring", macro_rings.index(macro_ring_bnds)) continue #--------------------------------- # compute the normal to macro ring #--------------------------------- b1 = numpy.array(macro_atoms[0].coords) b2 = numpy.array(macro_atoms[2].coords) b3 = numpy.array(macro_atoms[4].coords) if debug: print("b1,b2, b3=", b1.tolist(), b2.tolist(), b3.tolist()) # check for stacking a2_1 = a2-a1 a3_1 = a3-a1 b2_1 = b2-b1 b3_1 = b3-b1 if debug: print("a2_1 = ", a2-a1) if debug: print("a3_1 = ", a3-a1) if debug: print("b2_1 = ", b2-b1) if debug: print("b3_1 = ", b3-b1) n1 = crossProduct(a3_1,a2_1) #to get the normal for the first ring n2 = crossProduct(b3_1,b2_1) #to get the normal for the second ring if debug: print("n1=", n1) if debug: print("n2=", n2) n1 = numpy.array(n1) n2 = numpy.array(n2) n1_dot_n2 = numpy.dot(n1,n2) if debug: print("n1_dot_n2", numpy.dot(n1,n2)) if abs(n1_dot_n2) >= 1*tolerance: if debug: print("The rings are stacked vertically") new_result = (acbs.getAtoms(lig_ring_bnds), acbs.getAtoms(macro_ring_bnds)) self.results['pi_pi'].append(new_result) if abs(n1_dot_n2) <= 0.01*tolerance: if debug: print("The rings are stacked perpendicularly") new_result = (acbs.getAtoms(lig_ring_bnds), acbs.getAtoms(macro_ring_bnds)) self.results['t_shaped'].append(new_result)
def select(self, bnds, cutoff=7.5): cutoffValue = math.cos(cutoff * math.pi / 180.) #print "cutoffValue=", cutoffValue aromaticCs = AtomSet([]) atD = {} ctr = 1 aromaticBnds = BondSet() #taken from autotorsCommands ats = self.getAtoms(bnds) rf = RingFinder() rf.findRings2(ats, bnds) cyclecount = rf.ringCount aromatic_cycles = [] ct = 1 for ring in rf.rings: blist = ring['bonds'] for bnd in blist: at = bnd.atom1 #next find the other bond in this cycle with at as one of the atoms: z2 = filter( lambda x: x != bnd and x.atom1 == at or x.atom2 == at, blist) bnd.nextbond = z2[0] neighbor = z2[0].atom1 if neighbor == at: neighbor = z2[0].atom2 bnd.next1 = neighbor #print "set ", bnd.atom1.name,'-', bnd.atom2.name,".next1 to ", neighbor.name #now each bond has 3 atoms specified for it: its own two and the next1 to atom1 at1 = bnd.next1 at2 = bnd.atom1 at3 = bnd.atom2 pt1 = at1.coords pt2 = at2.coords pt3 = at3.coords a1 = Numeric.subtract(pt2, pt1) b1 = Numeric.subtract(pt3, pt2) p = [ a1[1] * b1[2] - a1[2] * b1[1], a1[2] * b1[0] - a1[0] * b1[2], a1[0] * b1[1] - a1[1] * b1[0] ] result0 = Numeric.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]) bnd.nrmsize = result0 result1 = p bnd.nrms = result1 #next find the other bond w/atom2: z2 = filter(lambda x, bnd=bnd, at2=bnd.atom2, blist=blist: x != bnd and x.atom1 == at2 or x.atom2 == at2, blist) #finally, return the other atom in this bond bnd.nextbond2 = z2[0] if bnd.nextbond2 == bnd: bnd.nextbond2 = z2[1] #neighbor2 = self._getnxtAtom(b.atom2,b.nextbond2) neighbor2 = bnd.nextbond2.atom1 if neighbor2 == bnd.atom2: neighbor2 = bnd.nextbond2.atom2 bnd.next2 = neighbor2 #next have to check whether the normals are parallel #check each pair in each bond, how to keep track?? #have to get normal at b's atom2: so have to get next2 for this bond: #have to loop twice to make sure neighbor has nrms for bnd in blist: p = bnd.nrms psize = bnd.nrmsize q = bnd.nextbond2.nrms qsize = bnd.nextbond2.nrmsize #theta is the critical test for planarity: #if angle between 2 nrms is 0, atoms are planar #NB>test is comparing theta,cos(angle), w/zero bnd.theta = Numeric.dot(p, q) / (psize * qsize) #NB: REPEAT STUFF FROM HERE TO THE END IF aromaticCutOff changes ct = 0 for bnd in blist: #these are values for the default 7.5degrees: #if theta>=0.997 or theta<=-0.997: #print bnd.atom1.name, '-', bnd.atom2.name, '->', bnd.theta if bnd.theta >= cutoffValue or bnd.theta <= -cutoffValue: bnd.posAromatic = 1 ct = ct + 1 else: bnd.posAromatic = 0 #print ' posAromatic=', bnd.posAromatic #after checking all the bonds in current cycle, compare #posAromatic w/number if ct == len(blist): #print ctr," cycle is aromatic" #NB: only C=C bonds are considered aromatic # could change the name and autodock_element here.... for bnd in blist: # THIS IS WRONG!!! #if bnd.atom1.element=='C' and bnd.atom2.element=='C': # aromaticBnds.append(bnd) aromaticBnds.append(bnd) ctr = ctr + 1 #print "len(aromaticBnds)=", len(aromaticBnds) #for b in aromaticBnds: # print b.atom1.name, '-', b.atom2.name return aromaticBnds