Beispiel #1
0
 def _get_square(self, first: Chem.Atom, second: Chem.Atom) -> Union[Tuple[int, int], None]:
     for third in [neigh for neigh in second.GetNeighbors() if neigh.GetIdx() != first.GetIdx()]:
         fourths = self._get_triangles(first, third)
         if fourths and len(fourths) > 1:
             fourth = [f for f in fourths if f != second.GetIdx()][0]
             return third.GetIdx(), fourth
     else:
         return None
 def get_other(bond: Chem.Bond, atom: Chem.Atom) -> Chem.Atom:
     """Given an bond and an atom return the other."""
     if bond.GetEndAtomIdx() == atom.GetIdx(
     ):  # atom == itself gives false.
         return bond.GetBeginAtom()
     else:
         return bond.GetEndAtom()
Beispiel #3
0
 def from_rdkit(cls, rd_atom: Chem.Atom) -> "Atom":
     """
     Build a QUBEKit atom from an rdkit atom instance.
     """
     atomic_number = rd_atom.GetAtomicNum()
     index = rd_atom.GetIdx()
     formal_charge = rd_atom.GetFormalCharge()
     aromatic = rd_atom.GetIsAromatic()
     bonds = [a.GetIdx() for a in rd_atom.GetNeighbors()]
     # check for names in the normal places pdb, mol2 and mol
     if rd_atom.HasProp("_Name"):
         name = rd_atom.GetProp("_Name")
     elif rd_atom.HasProp("_TriposAtomName"):
         name = rd_atom.GetProp("_TriposAtomName")
     else:
         try:
             name = rd_atom.GetMonomerInfo().GetName().strip()
         except AttributeError:
             name = None
     # stereochem
     if rd_atom.HasProp("_CIPCode"):
         stereo_code = rd_atom.GetProp("_CIPCode")
     else:
         stereo_code = None
     return cls(
         atomic_number=atomic_number,
         atom_index=index,
         atom_name=name,
         formal_charge=formal_charge,
         aromatic=aromatic,
         stereochemistry=stereo_code,
         bonds=bonds,
     )
    def _get_triangle(self, first: Chem.Atom, second: Chem.Atom) -> Union[int, None]:
        """
        Get the third atom...

        :param first: atom
        :param second: atom
        :return: atom index of third
        """
        get_neigh_idxs = lambda atom: [neigh.GetIdx() for neigh in atom.GetNeighbors()]
        f_neighs = get_neigh_idxs(first)
        s_neighs = get_neigh_idxs(second)
        a = set(f_neighs) - {first.GetIdx(), second.GetIdx()}
        b = set(s_neighs) - {first.GetIdx(), second.GetIdx()}
        others = list(a.intersection(b))
        if len(others) == 0: # is disjoined
            return None
        else:
            return others[0]
 def _get_measurements(self, conf: Chem.Conformer, a: Chem.Atom,
                       b: Chem.Atom, c: Chem.Atom, d: Chem.Atom):
     ai = a.GetIdx()
     bi = b.GetIdx()
     ci = c.GetIdx()
     di = d.GetIdx()
     dist = 0
     angle = 0
     tor = 0
     try:
         dist = Chem.rdMolTransforms.GetBondLength(conf, ai, bi)
         angle = 180 - Chem.rdMolTransforms.GetAngleDeg(conf, ai, bi, ci)
         tor = Chem.rdMolTransforms.GetDihedralDeg(conf, ai, bi, ci, di)
     except ValueError:
         pass
     if str(tor) == 'nan':  #quicker than isnan.
         tor = 0
     return self._Measure(distance=dist, angle=angle, torsion=tor)
Beispiel #6
0
    def _get_triangles(self, first: Chem.Atom, second: Chem.Atom) -> Union[List[int], None]:
        """
        Get the third atoms... (Square situation)

        :param first: atom
        :param second: atom
        :return: atom index of third
        """
        get_neigh_idxs = lambda atom: [neigh.GetIdx() for neigh in atom.GetNeighbors() if
                                       self._is_count_valid(neigh)]
        f_neighs = get_neigh_idxs(first)
        s_neighs = get_neigh_idxs(second)
        a = set(f_neighs) - {first.GetIdx(), second.GetIdx()}
        b = set(s_neighs) - {first.GetIdx(), second.GetIdx()}
        others = list(a.intersection(b))
        if len(others) == 0:  # is disjoined
            return None
        else:
            return list(others)
    def _is_square(self, first: Chem.Atom, second: Chem.Atom) -> bool:
        """
        Get bool of whether two atoms share a common over-neighbor. Ie. joining them would make a square.
        Direct bond does not count.

        :param first:
        :param second:
        :return:
        """
        for third in [neigh for neigh in second.GetNeighbors() if neigh.GetIdx() != first.GetIdx()]:
            if self._is_triangle(first, third) is True:
                return True
        else:
            return False
Beispiel #8
0
    def _add_bond_regardlessly(self,
                               mol,
                               first: Chem.Atom,
                               second: Chem.Atom,
                               bond_type,
                               provenance='other_novel'):
        """
        This methods does no checking and operates dangerously!

        :param mol:
        :param first:
        :param second:
        :param bond_type:
        :param provenance:
        :return:
        """
        first_idx = first.GetIdx()
        second_idx = second.GetIdx()
        # add if absent... (error prevention)
        present_bond = mol.GetBondBetweenAtoms(first_idx, second_idx)
        if present_bond is None:
            mol.AddBond(first_idx, second_idx, bond_type)
        new_bond = mol.GetBondBetweenAtoms(first_idx, second_idx)
        BondProvenance.set_bond(new_bond, provenance)
Beispiel #9
0
def get_substructures_from_atom(
        atom: Chem.Atom,
        max_size: int,
        substructure: Set[int] = None) -> Set[FrozenSet[int]]:
    """
    Recursively gets all substructures up to a maximum size starting from an atom in a substructure.

    :param atom: The atom to start at.
    :param max_size: The maximum size of the substructure to fine.
    :param substructure: The current substructure that atom is in.
    :return: A set of substructures starting at atom where each substructure is a frozenset of indices.
    """
    assert max_size >= 1

    if substructure is None:
        substructure = {atom.GetIdx()}

    substructures = {frozenset(substructure)}

    if len(substructure) == max_size:
        return substructures

    # Get neighbors which are not already in the substructure
    new_neighbors = [
        neighbor for neighbor in atom.GetNeighbors()
        if neighbor.GetIdx() not in substructure
    ]

    for neighbor in new_neighbors:
        # Define new substructure with neighbor
        new_substructure = deepcopy(substructure)
        new_substructure.add(neighbor.GetIdx())

        # Skip if new substructure has already been considered
        if frozenset(new_substructure) in substructures:
            continue

        # Recursively get substructures including this substructure plus neighbor
        new_substructures = get_substructures_from_atom(
            neighbor, max_size, new_substructure)

        # Add those substructures to current set of substructures
        substructures |= new_substructures

    return substructures
    def atom_to_feat(self, atm: Chem.Atom, owning_mol: Chem.Mol, idx):
        # nb the func GetOwningMol could not be used for mol as would return different object each time
        this_atms_idx = atm.GetIdx()
        assert idx == this_atms_idx

        feat = torch.zeros(len(self), dtype=torch.float32)

        # One hot encoding of element
        try:
            feat[self.atms_to_idx[atm.GetSymbol()]] = 1.
        except KeyError as ex:
            warnings.warn(f"Ignoring the symbol {atm.GetSymbol()}")
        idx_up_to = self.number_atom_options

        # Atomic Number
        feat[idx_up_to] = float(atm.GetAtomicNum())
        idx_up_to += 1

        # Acceptor/donor
        acceptor_ids, donor_ids = self.get_acceptor_and_donor_ids(owning_mol)

        # Acceptor
        feat[idx_up_to] = float(this_atms_idx in acceptor_ids)
        idx_up_to += 1

        # Donor
        feat[idx_up_to] = float(this_atms_idx in donor_ids)
        idx_up_to += 1


        # Hybridization
        hyb_idx = self.hybridization(atm.GetHybridization())
        if hyb_idx is not None:
            feat[idx_up_to + hyb_idx] = 1.
        idx_up_to += self.number_hyb_options

        # Aromatic
        feat[idx_up_to] = float(atm.GetIsAromatic())
        idx_up_to += 1

        # Number of Hydrogens
        feat[idx_up_to] = float(atm.GetNumImplicitHs())
        idx_up_to += 1

        return feat
Beispiel #11
0
 def downgrade_ring(self, atom: Chem.Atom):
     ## very crappy way of doing this
     log.debug(f'downgrading whole ring!')
     atom.SetIsAromatic(False)
     ringinfo = self._get_ring_info(mode='atom')
     get_atomrings = lambda ai: [ring for ring in ringinfo if ai in ring]
     atomrings = get_atomrings(atom.GetIdx())
     for atomring in atomrings:
         rnieghs = self._get_ring_neighbors(atomring)
         for n1, n2 in rnieghs:
             self.mol.GetAtomWithIdx(n1).SetIsAromatic(False)
             self.mol.GetAtomWithIdx(n2).SetIsAromatic(False)
             self.mol.GetBondBetweenAtoms(n1, n2).SetBondType(
                 Chem.BondType.SINGLE)
     for atomring in atomrings:
         rnieghs = self._get_ring_neighbors(atomring)
         for n1, n2 in rnieghs:
             if self._get_valence_difference(self.mol.GetAtomWithIdx(n1)) <= -2 and \
                     self._get_valence_difference(self.mol.GetAtomWithIdx(n2)) <= -2:
                 self.mol.GetBondBetweenAtoms(n1, n2).SetBondType(
                     Chem.BondType.DOUBLE)
Beispiel #12
0
    def _add_bond_if_possible(self,
                              mol,
                              first: Chem.Atom,
                              second: Chem.Atom,
                              provenance='other_novel'):
        """
        This method is used by _copy_bonding, but triggered when force=False

        :param mol:
        :param first:
        :param second:
        :param provenance:
        :return:
        """
        # --- Prep
        first_idx = first.GetIdx()
        second_idx = second.GetIdx()
        present_bond = mol.GetBondBetweenAtoms(first_idx, second_idx)
        if second.HasProp('_ring_bond'):
            bt = getattr(Chem.BondType, second.GetProp('_ring_bond'))
        else:
            bt = None
        # === Bond already exists series ============================================
        # --- Bond already exists and not bond type is specified
        if present_bond is not None and bt is None:
            self.journal.debug(
                f'Bond between {first_idx} and {second_idx} already exists')
            return True  # exists
        # --- Bond already exists but has an error
        elif present_bond is not None and present_bond.GetBondType() is None:
            present_bond.SetBondType(Chem.BondType.SINGLE)
            return True
        # --- Bond already exists and matches the expected bond type
        elif present_bond is not None and bt.name == present_bond.GetBondType(
        ).name:
            return True
        # --- Bond already exists but does not match the expected bond type
        elif present_bond is not None:
            present_bond.SetBondType(bt)
            return True
        # === Assess if new bond should be added ============================================
        # --- Don't add if it would make a triangle
        elif self._is_would_be_triangle(first, second):
            self.journal.debug(
                f'Bond between {first_idx} and {second_idx} would make a triangle, skipping'
            )
            return False
        # --- Don't add if it would make a square
        elif self._is_would_be_square(first, second):
            self.journal.debug(
                f'Bond between {first_idx} and {second_idx} would make a square, skipping'
            )
            return False
        # --- Don't add if it would ruin the warhead
        elif self._is_connected_warhead(second, first):
            self.journal.debug(
                f'Bond between {first_idx} and {second_idx} would break a warhead, skipping'
            )
            return False
        # --- New bond is green lit
        elif self._assess_atom_for_possible_bonding(
                first, bt) and self._assess_atom_for_possible_bonding(
                    second, bt):
            mol.AddBond(first_idx, second_idx,
                        bt if bt is not None else Chem.BondType.SINGLE)
            new_bond = mol.GetBondBetweenAtoms(first_idx, second_idx)
            BondProvenance.set_bond(new_bond, provenance)
            return True
        # --- New bond is no go
        else:
            # len(Chem.GetMolFrags(mol, sanitizeFrags=False)) ought to be checked.
            # however, join_neighboring gets called by emergency bonding so should be fine.
            return False  # too bonded etc.
Beispiel #13
0
def atom_coords(atom: Chem.Atom, conf: Chem.Conformer) -> List[float]:
    """Returns the x, y, and z coordinates of an atom in a molecule conformer.
    """
    p = conf.GetAtomPosition(atom.GetIdx())
    fts = [p.x, p.y, p.z]
    return fts
def index(atom: Atom) -> int:
    """Index within the parent molecule (int).
    """
    return atom.GetIdx()