예제 #1
0
    def beta_atoms(atom, nitrogen, imine, in_ring, begin_end):
        for bond in openbabel.OBAtomBondIter(atom):

            begin_atom = bond.GetBeginAtom()
            end_atom = bond.GetEndAtom()

            if begin_atom != nitrogen and begin_atom != atom:
                if in_ring == True and begin_end is "begin":
                    imine.insert(0, begin_atom.GetIdx())
                elif in_ring == True and begin_end is "end":
                    imine.append(begin_atom.GetIdx())
                elif in_ring == False and begin_end is "begin":
                    imine.append(begin_atom.GetIdx())
                else:
                    imine.insert(0, begin_atom.GetIdx())
                return imine

            if end_atom != nitrogen and end_atom != atom:
                if in_ring == True and begin_end is "begin":
                    imine.insert(0, end_atom.GetIdx())
                elif in_ring == True and begin_end is "end":
                    imine.append(end_atom.GetIdx())
                elif in_ring == False and begin_end is "begin":
                    imine.append(end_atom.GetIdx())
                else:
                    imine.insert(0, begin_atom.GetIdx())
                return imine
        return imine
예제 #2
0
    def remove_bad_valences(self, ob_mol, atoms, struct):
        '''
        Remove hypervalent bonds without fragmenting
        the molecule, and prioritize stretched bonds.
        Also remove bonds between halogens/hydrogens.
        '''
        # get max valence of the atoms
        max_vals = get_max_valences(atoms)

        # remove any bonds between halogens or hydrogens
        for bond in ob.OBMolBondIter(ob_mol):
            atom_a = bond.GetBeginAtom()
            atom_b = bond.GetEndAtom()
            if (max_vals.get(atom_a.GetIdx(), 1) == 1
                    and max_vals.get(atom_b.GetIdx(), 1) == 1):
                ob_mol.DeleteBond(bond)

        # remove bonds causing larger-than-permitted valences
        #   prioritize atoms with lowest max valence, since they
        #   place the hardest constraint on reachability (e.g O)

        atom_info = sort_atoms_by_valence(atoms, max_vals)
        for max_val, rem_val, atom in atom_info:

            if atom.GetExplicitValence() <= max_val:
                continue
            # else, the atom could have an invalid valence
            #   so check whether we can modify a bond

            bond_info = sort_bonds_by_stretch(ob.OBAtomBondIter(atom))
            for bond_stretch, bond_len, bond in bond_info:

                # do the atoms involved in this bond have bad valences?
                #   since we are modifying the valences in the loop, this
                #   could have changed since calling sort_atoms_by_valence

                a1, a2 = bond.GetBeginAtom(), bond.GetEndAtom()
                max_val_diff = max(  # by how much are the valences over?
                    a1.GetExplicitValence() - max_vals.get(a1.GetIdx(), 1),
                    a2.GetExplicitValence() - max_vals.get(a2.GetIdx(), 1))
                if max_val_diff > 0:

                    bond_order = bond.GetBondOrder()
                    if bond_order > max_val_diff:  # decrease bond order
                        bond.SetBondOrder(bond_order - max_val_diff)

                    elif reachable(a1, a2):  # don't fragment the molecule
                        ob_mol.DeleteBond(bond)

                    # if the current atom now has a permitted valence,
                    # break and let other atoms choose next bonds to remove
                    if atom.GetExplicitValence() <= max_vals[atom.GetIdx()]:
                        break

        # deleting bonds resets this flag
        ob_mol.SetHybridizationPerceived(True)
예제 #3
0
    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})
예제 #4
0
 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()
예제 #5
0
    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])
예제 #6
0
def connect_the_dots(mol, atoms, struct, maxbond=4):
    '''Custom implementation of ConnectTheDots.  This is similar to
    OpenBabel's version, but is more willing to make long bonds 
    (up to maxbond long) to keep the molecule connected.  It also 
    attempts to respect atom type information from struct.
    atoms and struct need to correspond in their order

    Assumes no hydrogens or existing bonds.
    '''
    pt = Chem.GetPeriodicTable()

    if len(atoms) == 0:
        return

    mol.BeginModify()

    #just going to to do n^2 comparisons, can worry about efficiency later
    coords = np.array([(a.GetX(), a.GetY(), a.GetZ()) for a in atoms])
    dists = squareform(pdist(coords))
    types = [struct.channels[t].name for t in struct.c]

    for (i, a) in enumerate(atoms):
        for (j, b) in enumerate(atoms):
            if a == b:
                break
            if dists[i, j] < 0.01:  #reduce from 0.4
                continue  #don't bond too close atoms
            if dists[i, j] < maxbond:
                flag = 0
                if 'Aromatic' in types[i] and 'Aromatic' in types[j]:
                    flag = ob.OB_AROMATIC_BOND
                mol.AddBond(a.GetIdx(), b.GetIdx(), 1, flag)

    atom_maxb = {}
    for (i, a) in enumerate(atoms):
        #set max valance to the smallest max allowed by openbabel or rdkit
        #since we want the molecule to be valid for both (rdkit is usually lower)
        maxb = ob.GetMaxBonds(a.GetAtomicNum())
        maxb = min(maxb, pt.GetDefaultValence(a.GetAtomicNum()))
        if 'Donor' in types[i]:
            maxb -= 1  #leave room for hydrogen
        atom_maxb[a.GetIdx()] = maxb

    #remove any impossible bonds between halogens
    for bond in ob.OBMolBondIter(mol):
        a1 = bond.GetBeginAtom()
        a2 = bond.GetEndAtom()
        if atom_maxb[a1.GetIdx()] == 1 and atom_maxb[a2.GetIdx()] == 1:
            mol.DeleteBond(bond)

    def get_bond_info(biter):
        '''Return bonds sorted by their distortion'''
        bonds = [b for b in biter]
        binfo = []
        for bond in bonds:
            bdist = bond.GetLength()
            #compute how far away from optimal we are
            a1 = bond.GetBeginAtom()
            a2 = bond.GetEndAtom()
            ideal = ob.GetCovalentRad(a1.GetAtomicNum()) + ob.GetCovalentRad(
                a2.GetAtomicNum())
            stretch = bdist - ideal
            binfo.append((stretch, bdist, bond))
        binfo.sort(reverse=True,
                   key=lambda t: t[:2])  #most stretched bonds first
        return binfo

    #prioritize removing hypervalency causing bonds, do more valent
    #constrained atoms first since their bonds introduce the most problems
    #with reachability (e.g. oxygen)
    hypers = sorted([(atom_maxb[a.GetIdx()],
                      a.GetExplicitValence() - atom_maxb[a.GetIdx()], a)
                     for a in atoms],
                    key=lambda aa: (aa[0], -aa[1]))
    for mb, diff, a in hypers:
        if a.GetExplicitValence() <= atom_maxb[a.GetIdx()]:
            continue
        binfo = get_bond_info(ob.OBAtomBondIter(a))
        for stretch, bdist, bond in binfo:
            #can we remove this bond without disconnecting the molecule?
            a1 = bond.GetBeginAtom()
            a2 = bond.GetEndAtom()

            #get right valence
            if a1.GetExplicitValence() > atom_maxb[a1.GetIdx()] or \
                a2.GetExplicitValence() > atom_maxb[a2.GetIdx()]:
                #don't fragment the molecule
                if not reachable(a1, a2):
                    continue
                mol.DeleteBond(bond)
                if a.GetExplicitValence() <= atom_maxb[a.GetIdx()]:
                    break  #let nbr atoms choose what bonds to throw out

    binfo = get_bond_info(ob.OBMolBondIter(mol))
    #now eliminate geometrically poor bonds
    for stretch, bdist, bond in binfo:
        #can we remove this bond without disconnecting the molecule?
        a1 = bond.GetBeginAtom()
        a2 = bond.GetEndAtom()

        #as long as we aren't disconnecting, let's remove things
        #that are excessively far away (0.45 from ConnectTheDots)
        #get bonds to be less than max allowed
        #also remove tight angles, because that is what ConnectTheDots does
        if stretch > 0.45 or forms_small_angle(a1, a2) or forms_small_angle(
                a2, a1):
            #don't fragment the molecule
            if not reachable(a1, a2):
                continue
            mol.DeleteBond(bond)

    mol.EndModify()
예제 #7
0
    def fill_rem_valences(self, ob_mol, atoms, struct):
        '''
        Fill empty valences with hydrogens up to the
        amount expected by the atom type, or a typical
        amount according to openbabel, and then fill
        remaining empty valences with higher bond orders.
        '''
        max_vals = get_max_valences(atoms)

        for ob_atom, atom_type in zip(atoms, struct.atom_types):

            if struct.typer.explicit_h:
                # all Hs should already be present
                continue

            max_val = max_vals.get(ob_atom.GetIdx(), 1)

            if Atom.h_count in struct.typer:
                # this should have already been set
                #   by set_min_h_counts, but whatever
                h_count = Atom.h_count(ob_atom)
                if h_count < atom_type.h_count:
                    n = ob_atom.GetImplicitHCount()
                    ob_atom.SetImplicitHCount(n + atom_type.h_count - h_count)

            elif ob_atom.GetExplicitValence() < max_val:
                # this uses explicit valence and formal charge,
                #   and only ever INCREASES hydrogens, since it
                #   never sets implicit H to a negative value
                # but it does overwrite the existing value, so
                #   we need to save it beforehand and then add
                n = ob_atom.GetImplicitHCount()
                ob.OBAtomAssignTypicalImplicitHydrogens(ob_atom)
                n += ob_atom.GetImplicitHCount()
                ob_atom.SetImplicitHCount(n)

        # these have possibly changed
        max_vals = get_max_valences(atoms)

        # now increment bond orders to fill remaining valences
        atom_info = sort_atoms_by_valence(atoms, max_vals)
        for max_val, rem_val, atom in reversed(atom_info):

            if atom.GetExplicitValence() >= max_val:
                continue
            # else, the atom could have an empty valence
            #   so check whether we can augment a bond,
            #   prioritizing bonds that are too short

            bond_info = sort_bonds_by_stretch(ob.OBAtomBondIter(atom))
            for bond_stretch, bond_len, bond in reversed(bond_info):

                if bond.GetBondOrder() >= 3:
                    continue  # don't go above triple

                # do the atoms involved in this bond have empty valences?
                #   since we are modifying the valences in the loop, this
                #   could have changed since calling sort_atoms_by_valence

                a1, a2 = bond.GetBeginAtom(), bond.GetEndAtom()
                min_val_diff = min(  # by how much are the valences under?
                    max_vals.get(a1.GetIdx(), 1) - a1.GetExplicitValence(),
                    max_vals.get(a2.GetIdx(), 1) - a2.GetExplicitValence())
                if min_val_diff > 0:  # increase bond order

                    bond_order = bond.GetBondOrder()  # don't go above triple
                    bond.SetBondOrder(min(bond_order + min_val_diff, 3))

                    # if the current atom now has its preferred valence,
                    #   break and let other atoms choose next bonds to augment
                    if atom.GetExplicitValence() == max_vals[atom.GetIdx()]:
                        break
예제 #8
0
def array_of_imine_torsions(imine_nitrogens, molecule):
    def beta_atoms(atom, nitrogen, imine, in_ring, begin_end):
        for bond in openbabel.OBAtomBondIter(atom):

            begin_atom = bond.GetBeginAtom()
            end_atom = bond.GetEndAtom()

            if begin_atom != nitrogen and begin_atom != atom:
                if in_ring == True and begin_end is "begin":
                    imine.insert(0, begin_atom.GetIdx())
                elif in_ring == True and begin_end is "end":
                    imine.append(begin_atom.GetIdx())
                elif in_ring == False and begin_end is "begin":
                    imine.append(begin_atom.GetIdx())
                else:
                    imine.insert(0, begin_atom.GetIdx())
                return imine

            if end_atom != nitrogen and end_atom != atom:
                if in_ring == True and begin_end is "begin":
                    imine.insert(0, end_atom.GetIdx())
                elif in_ring == True and begin_end is "end":
                    imine.append(end_atom.GetIdx())
                elif in_ring == False and begin_end is "begin":
                    imine.append(end_atom.GetIdx())
                else:
                    imine.insert(0, begin_atom.GetIdx())
                return imine
        return imine

    def get_torsions(imine_struct):
        torsion_list = list()
        for torsion in imine_struct:
            atom1 = molecule.GetAtom(torsion[0])
            atom2 = molecule.GetAtom(torsion[1])
            atom3 = molecule.GetAtom(torsion[2])
            atom4 = molecule.GetAtom(torsion[3])
            angle = molecule.GetTorsion(atom1, atom2, atom3, atom4)
            torsion_list.append(angle)
        torsion_array = np.array(torsion_list, dtype='float')
        return torsion_array

    imine_struct = list()
    for nitrogen in imine_nitrogens:
        imine = list()
        imine.append(nitrogen.GetIdx())

        for bond in openbabel.OBAtomBondIter(nitrogen):
            begin_atom = bond.GetBeginAtom()
            end_atom = bond.GetEndAtom()

            if begin_atom != nitrogen:
                in_ring = begin_atom.MemberOfRingSize() != 0
                begin_end = "begin"
                if in_ring == True:
                    imine.insert(0, begin_atom.GetIdx())
                    imine = beta_atoms(begin_atom, nitrogen, imine, in_ring,
                                       begin_end)
                else:
                    imine.append(begin_atom.GetIdx())

            if end_atom != nitrogen:
                in_ring = end_atom.MemberOfRingSize() != 0
                begin_end = "end"
                if in_ring == True:
                    imine.append(end_atom.GetIdx())
                    imine = beta_atoms(end_atom, nitrogen, imine, in_ring,
                                       begin_end)
                else:
                    imine.insert(0, end_atom.GetIdx())

        imine_struct.append(imine)
    torsion_list = get_torsions(imine_struct)
    return torsion_list