def assess_atom(atom: Chem.Atom, bt: Chem.BondType) -> Tuple[bool, Chem.BondType]: if atom.GetAtomicNum() > 8: return True, bt elif len(atom.GetNeighbors()) <= 2 and atom.GetIsAromatic(): return True, Chem.BondType.SINGLE elif len(atom_i.GetNeighbors()) <= 3 and not atom.GetIsAromatic(): return True, bt else: return False, bt # too bonded already!
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 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!
def get_atom_hybridization(self, atom: Atom): if self.is_hydrogen(atom): return self.get_hydrogen_hybridization(atom) if atom.GetIsAromatic(): return "_R" if self.has_hybridization(atom): return "_%s" % self.get_hybridization(atom) return ""
def atom_features(atom: Chem.Atom): return np.array( encoding_onehot_unk(atom.GetSymbol(), [ 'C', 'N', 'O', 'S', 'F', 'Si', 'P', 'Cl', 'Br', 'Mg', 'Na', 'Ca', 'Fe', 'As', 'Al', 'I', 'B', 'V', 'K', 'Tl', 'Yb', 'Sb', 'Sn', 'Ag', 'Pd', 'Co', 'Se', 'Ti', 'Zn', 'H', 'Li', 'Ge', 'Cu', 'Au', 'Ni', 'Cd', 'In', 'Mn', 'Zr', 'Cr', 'Pt', 'Hg', 'Pb', 'Unknown' ]) + encoding_onehot(atom.GetDegree(), [0, 1, 2, 3, 4, 5]) + encoding_onehot_unk(atom.GetTotalNumHs(), [0, 1, 2, 3, 4]) + encoding_onehot_unk(atom.GetImplicitValence(), [0, 1, 2, 3, 4, 5]) + [atom.GetIsAromatic()])
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!
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
def atom_to_string(atom: Chem.Atom) -> str: return '{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}'.format( atom.GetChiralTag(), atom.GetDegree(), atom.GetExplicitValence(), atom.GetFormalCharge(), atom.GetHybridization(), atom.GetIsAromatic(), atom.GetMass(), atom.GetNumExplicitHs(), atom.GetNumImplicitHs(), atom.GetNumRadicalElectrons(), atom.GetTotalDegree(), atom.GetTotalNumHs(), atom.GetTotalValence(), atom.IsInRing() )
def atom_feature(atom: Atom) -> Tuple[torch.Tensor, int]: ''' Extract atom feature vector. Returns: (feature_vec, feature_dim) Features: # item # dim atomic number 25 exp-valence 7 imp-valence 4 hybridization 7 Hs 5 degree 6 formal charge 6 in ring 1 aromatic 1 mass / 100 1 (sum) 63 ''' index = torch.tensor([ ATOM_MAP[atom.GetAtomicNum()], atom.GetExplicitValence(), atom.GetImplicitValence(), HYBRID_MAP[atom.GetHybridization()], atom.GetTotalNumHs(), atom.GetDegree(), atom.GetFormalCharge() + 2, # -2 ~ 3 int(atom.IsInRing()), int(atom.GetIsAromatic()) ]) + OFFSET vec = torch.zeros(ATOM_FDIM) vec[index] = 1.0 vec[-1] = atom.GetMass() / 100 return vec, ATOM_FDIM
def is_aromatic(atom: Atom) -> int: """If the atom is aromatic (0 or 1). """ return int(atom.GetIsAromatic())