Exemplo n.º 1
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,
     )
Exemplo n.º 2
0
    def _is_count_valid(self, atom: Chem.Atom) -> bool:
        """
        Some atoms are not to be counted as they will be deleted.

        :param atom:
        :return:
        """
        if atom.HasProp('DELETE'):
            return False
        elif atom.HasProp('_ori_i') and atom.GetIntProp('_ori_i') == -1:
            return False
        else:
            return True
Exemplo n.º 3
0
 def _get_xyz(cls, atom: Chem.Atom) -> Tuple[float]:
     if atom.HasProp('_x'):
         return (atom.GetDoubleProp('_x'),
                 atom.GetDoubleProp('_y'),
                 atom.GetDoubleProp('_z'))
     else:
         return ()
Exemplo n.º 4
0
def charge_gasteiger_h(atom: Atom) -> float:
    """Gasteiger partial charge for implicit hydrogens (float).
    """
    if not atom.HasProp('_GasteigerHCharge'):
        mol = atom.GetOwningMol()
        AllChem.ComputeGasteigerCharges(mol)
    return atom.GetDoubleProp('_GasteigerHCharge')
Exemplo n.º 5
0
def is_hbond_donor(atom: Atom) -> int:
    """If the atom is a hydrogen bond donor (0 or 1).
    """
    if not atom.HasProp('_Feature_Donor'):
        mol = atom.GetOwningMol()
        _ChemicalFeatureGenerator().assign_features(mol)
    return atom.GetIntProp('_Feature_Donor')
Exemplo n.º 6
0
 def _get_origin(cls, atom: Chem.Atom) -> List[str]:
     if atom.HasProp('_Origin'):
         o = atom.GetProp('_Origin')
         if o != 'none':
             return json.loads(o)
         else:
             return []
     else:
         return []
Exemplo n.º 7
0
def stereochemistry(atom: Atom) -> str:
    """CIP sterochemistry label (string).
    """
    mol = atom.GetOwningMol()
    if not mol.HasProp('_CIPLabelsAssigned'):
        rdCIPLabeler.AssignCIPLabels(mol)
        mol.SetProp('_CIPLabelsAssigned', '1')

    return atom.GetProp('_CIPCode') if atom.HasProp('_CIPCode') else ''
Exemplo n.º 8
0
        def assess_atom(atom: Chem.Atom, bt: Chem.BondType) -> Tuple[bool, Chem.BondType]:
            """
            True means add, False means delete

            :param atom:
            :param bt:
            :return:
            """
            n_neigh = sum([self._is_count_valid(neigh) for neigh in atom.GetNeighbors()])
            if atom.GetAtomicNum() > 8:
                return True, bt
            elif atom.HasProp('DELETE'):  # if it is to be deleted it should be fine.
                return True, bt
            elif n_neigh <= 2 and atom.GetIsAromatic():
                return True, Chem.BondType.SINGLE
            elif n_neigh <= 3 and not atom.GetIsAromatic():
                return True, bt
            else:
                return False, bt  # too bonded already!
Exemplo n.º 9
0
    def _assess_atom_for_possible_bonding(self, atom: Chem.Atom,
                                          bt: Chem.BondType) -> bool:
        """
        Method for add_bond_if_possible
        True means add, False means delete

        :param atom:
        :param bt:
        :return:
        """
        n_neigh = sum(
            [self._is_count_valid(neigh) for neigh in atom.GetNeighbors()])
        if atom.GetAtomicNum() > 8:
            return True
        elif atom.HasProp(
                'DELETE'):  # if it is to be deleted it should be fine.
            return True
        elif n_neigh <= 2 and atom.GetIsAromatic():
            return True, Chem.BondType.SINGLE
        elif n_neigh <= 3 and not atom.GetIsAromatic():
            return True
        else:
            return False  # too bonded already!
Exemplo n.º 10
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.