def map_atoms(mol_a: Mol, mol_b: Mol) -> Dict[int, int]: """Find all atoms in mol_a in mol_b Args: mol_a: mol_b: Returns: Dictionary where key are atom indices from mol_a and values are atom indices from mol_b """ a_conf = mol_a.GetConformer() b_conf = mol_b.GetConformer() # find mapping of atoms from mol to parent a2b_atoms = {} b2a_atoms = {} for a in mol_a.GetAtoms(): a_pos = a_conf.GetAtomPosition(a.GetIdx()) for b in mol_b.GetAtoms(): if b.GetIdx() in b2a_atoms: # Don't try to map same atom twice continue bp_pos = b_conf.GetAtomPosition(b.GetIdx()) if b.Match( a ) and a_pos.x == bp_pos.x and a_pos.y == bp_pos.y and a_pos.z == bp_pos.z: a2b_atoms[a.GetIdx()] = b.GetIdx() b2a_atoms[b.GetIdx()] = a.GetIdx() return a2b_atoms
def from_rdmol(cls, rdmol: Chem.Mol, atomidx2nodename=None): """ :param rdmol: :param atomidx2nodename: the dict to convert atomidx in rdmol to graph node, atomidx2nodename[rdmolid] == nodename if not None then nodename will be set just based on atomidx :return: """ g = nx.Graph() if atomidx2nodename: index_dict_no = atomidx2nodename for atom in rdmol.GetAtoms(): g.add_node( index_dict_no[atom.GetIdx()], symbol=atom.GetSymbol(), ) for bond in rdmol.GetBonds(): g.add_edge( index_dict_no[bond.GetBeginAtomIdx()], index_dict_no[bond.GetEndAtomIdx()], ) else: for atom in rdmol.GetAtoms(): g.add_node( atom.GetIdx(), symbol=atom.GetSymbol(), ) for bond in rdmol.GetBonds(): g.add_edge( bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), ) if not nx.is_connected(g): raise GraphError('the graph is not connected!') return cls(g)
def process(self, mol: chem.Mol, atom_map: Dict[int, int]) -> GCNGraph: n = mol.GetNumAtoms() + 1 # allocate a new node for graph embedding # all edges (including all self-loops) as index begin_idx = [u.GetBeginAtomIdx() for u in mol.GetBonds()] + [n - 1] * (n - 1) end_idx = [u.GetEndAtomIdx() for u in mol.GetBonds()] + list(range(n - 1)) assert len(begin_idx) == len(end_idx) ran = list(range(n)) index = [begin_idx + end_idx + ran, end_idx + begin_idx + ran] # construct coefficients adjacent matrix deg = torch.tensor( [sqrt(1 / (len(u.GetNeighbors()) + 2)) for u in mol.GetAtoms()] + [sqrt(1 / n)], device=self.device) coeff = deg.reshape(-1, 1) @ deg[None, :] # pairwise coefficients adj = torch.zeros((n, n), device=self.device) adj[index] = coeff[index] # node embedding num = torch.tensor( [atom_map[u.GetAtomicNum()] for u in mol.GetAtoms()] + [len(atom_map)], device=self.device) return GCNGraph(n, adj, num)
def get_conjugate_group_with_halogen(m: Mol): natoms = len(m.GetAtoms()) adjmat = np.zeros((natoms, natoms), dtype=bool) for i in range(natoms): for j in range(i + 1, natoms): if isinstance(m.GetBondBetweenAtoms(i, j), Bond): adjmat[i][j] = True adjmat[j][i] = True supp = ResonanceMolSupplier(m, ) # supp = ResonanceMolSupplier(m, Chem.KEKULE_ALL) # supp = ResonanceMolSupplier(m, Chem.ALLOW_CHARGE_SEPARATION) cg_dict = {} a: Atom for a in m.GetAtoms(): aid = a.GetIdx() cgid = supp.GetAtomConjGrpIdx(aid) if cgid < 1e5: cg_dict[aid] = cgid cgids = set(cg_dict.values()) cgs = [] for cgid in cgids: cg = [i for i in cg_dict.keys() if cg_dict[i] == cgid] atom: Atom for atom in m.GetAtoms(): if atom.GetIdx() not in cg: if any(adjmat[atom.GetIdx()][cg_aid] for cg_aid in cg) and atom.GetSymbol() in ("I", "F", "Cl", "Br"): cg.append(atom.GetIdx()) cgmol, old_id_2_new_id = RdFunc.get_sub_rdmol(m, cg) cgs.append([cgmol, old_id_2_new_id]) return sorted(cgs, key=lambda x: x[0].GetNumAtoms(), reverse=True)
def remap_reaction_to_canonical(input_mol: Mol, target_mol: Mol) -> Tuple[Mol, Mol]: """ Re-maps reaction according to order of atoms in RdKit - this makes sure that stereochemical SMILES are canonical. Note: this method does not transfer any information from target molecule to the input molecule (the input molecule is mapped according to its order of atoms in its canonical SMILES) """ # converting Mol to smiles and again to Mol makes atom order canonical input_mol = Chem.MolFromSmiles(Chem.MolToSmiles(input_mol)) target_mol = Chem.MolFromSmiles(Chem.MolToSmiles(target_mol)) map2map = {} for i, a in enumerate(input_mol.GetAtoms()): map2map[int(a.GetAtomMapNum())] = i + 1 a.SetAtomMapNum(i + 1) max_map = max(map2map.values()) for i, a in enumerate(target_mol.GetAtoms()): old_map = int(a.GetAtomMapNum()) if old_map in map2map: new_map = map2map[old_map] else: new_map = max_map + 1 max_map += 1 a.SetAtomMapNum(new_map) return input_mol, target_mol
def map_reac_to_prod(mol_reac: Chem.Mol, mol_prod: Chem.Mol): """ Build a dictionary of mapping atom indices in the reactants to the products. :param mol_reac: An RDKit molecule of the reactants. :param mol_prod: An RDKit molecule of the products. :return: A dictionary of corresponding reactant and product atom indices. """ only_prod_ids = [] prod_map_to_id = {} mapnos_reac = set([atom.GetAtomMapNum() for atom in mol_reac.GetAtoms()]) for atom in mol_prod.GetAtoms(): mapno = atom.GetAtomMapNum() if mapno > 0: prod_map_to_id[mapno] = atom.GetIdx() if mapno not in mapnos_reac: only_prod_ids.append(atom.GetIdx()) else: only_prod_ids.append(atom.GetIdx()) only_reac_ids = [] reac_id_to_prod_id = {} for atom in mol_reac.GetAtoms(): mapno = atom.GetAtomMapNum() if mapno > 0: try: reac_id_to_prod_id[atom.GetIdx()] = prod_map_to_id[mapno] except KeyError: only_reac_ids.append(atom.GetIdx()) else: only_reac_ids.append(atom.GetIdx()) return reac_id_to_prod_id, only_prod_ids, only_reac_ids
def setup_relative_restraints_by_distance(mol_a: Chem.Mol, mol_b: Chem.Mol, cutoff: float = 0.1, terminal: bool = False): """ Setup restraints between atoms in two molecules using a cutoff distance between atoms Parameters ---------- mol_a: Chem.Mol First molecule mol_b: Chem.Mol Second molecule cutoff: float=0.1 Distance between atoms to consider as a match terminal: bool=false Map terminal atoms Returns ------- np.array (N, 2) Atom mapping between atoms in mol_a to atoms in mol_b. """ ligand_coords_a = get_romol_conf(mol_a) ligand_coords_b = get_romol_conf(mol_b) core_idxs_a = [] core_idxs_b = [] for idx, a in enumerate(mol_a.GetAtoms()): if not terminal and a.GetDegree() == 1: continue for b_idx, b in enumerate(mol_b.GetAtoms()): if not terminal and b.GetDegree() == 1: continue if np.linalg.norm(ligand_coords_a[idx] - ligand_coords_b[b_idx]) < cutoff: core_idxs_a.append(idx) core_idxs_b.append(b_idx) assert len(core_idxs_a) == len(core_idxs_b), "Core sizes were inconsistent" rij = cdist(ligand_coords_a[core_idxs_a], ligand_coords_b[core_idxs_b]) row_idxs, col_idxs = linear_sum_assignment(rij) core_idxs = [] for core_a, core_b in zip(row_idxs, col_idxs): core_idxs.append((core_idxs_a[core_a], core_idxs_b[core_b])) core_idxs = np.array(core_idxs, dtype=np.int32) return core_idxs
def embed_r_groups(mol: Mol, parent: Mol): """Assign coordinates to R groups Args: mol: Molecule with atoms with '*' as symbol parent: Molecule that was used to generate `mol` Raises: LookupError: When atoms bonded to R group can not be found in parent """ mol2parent_idxs = map_atoms(mol, parent) parent2mol_idxs = {v for v in mol2parent_idxs.values()} idx2parent_atom = {a.GetIdx(): a for a in parent.GetAtoms()} mol_conf = mol.GetConformer() parent_conf = parent.GetConformer() r_pos_cache = set() for r_atom in mol.GetAtoms(): if r_atom.GetSymbol() == '*': for r_bond in r_atom.GetBonds(): # atom bonded to r group in mol r_bonded_atom = r_bond.GetOtherAtom(r_atom) r_bonded_idx = r_bonded_atom.GetIdx() if r_bonded_idx in mol2parent_idxs: # same atom in parent r_bonded_atom_parent_idx = mol2parent_idxs[r_bonded_idx] r_bonded_atom_parent = idx2parent_atom[ r_bonded_atom_parent_idx] for r_bonded_atom_parent_bond in r_bonded_atom_parent.GetBonds( ): # Atom bonded to atom which is bonded to R group in parent r_bonded_atom_parend_bonded_atom = r_bonded_atom_parent_bond.GetOtherAtom( r_bonded_atom_parent) r_bonded_atom_parend_bonded_atom_idx = r_bonded_atom_parend_bonded_atom.GetIdx( ) if r_bonded_atom_parend_bonded_atom_idx not in parent2mol_idxs: # atom in parent but not in mol which was replaced R group point = parent_conf.GetAtomPosition( r_bonded_atom_parend_bonded_atom_idx) if serialize_point(point) not in r_pos_cache: # Position has not been used for other R group mol_conf.SetAtomPosition( r_atom.GetIdx(), point) r_pos_cache.add(serialize_point(point)) break else: raise LookupError( 'Atom bonded to R group not found in parent')
def get_atom(self, mol: Chem.Mol, name: str) -> Chem.Atom: for atom in mol.GetAtoms(): if atom.HasProp('_AtomName') and name == atom.GetProp( '_AtomName'): # Nonstandard. do not copy return atom else: raise ValueError(f'Atom {name} not found')
def _categorise(self, mol: Chem.Mol, uniques: set) -> Dict[str, Union[set, Dict]]: """ What do the novel atoms do in terms of connectivity. Complicated dict output (called ``categories`` in the methods). Really ought to be SetProp of the atoms. * ``uniques`` are set of atoms to classify on * ``internals`` are unique atoms that are connected solely to unique atoms * ``attachments`` are non-unique atoms to which a unique atom connects * ``pairs`` is a dict of unique atom idx --> dict of ``idx`` --> attachment idx and ``type`` bond type. :param mol: molecule to describe :param uniques: set of indices that are new to this molecule :return: """ # pairs = {} internals = set() attachments = set() dummies = set() for i in uniques: # novel atoms unique_atom = mol.GetAtomWithIdx(i) if unique_atom.GetSymbol() == self.dummy_symbol: dummies.add(i) neighbours = {n.GetIdx() for n in unique_atom.GetNeighbors()} if len(neighbours - uniques ) == 0: # unlessone of the connections is not unique. internals.add(i) else: i_attached = neighbours - uniques attachments |= i_attached pairs[i] = [{ 'idx': j, 'type': mol.GetBondBetweenAtoms(i, j).GetBondType() } for j in i_attached] anchors = uniques - internals # store for safekeeping for atom in mol.GetAtoms(): i = atom.GetIdx() if i in internals: # novel and not connected atom.SetProp('_Category', 'internal') elif i in attachments: # not-novel but connected atom.SetProp('_Category', 'overlapping-attachment') elif i in pairs: # dict not set tho atom.SetProp('_Category', 'internal-attachment') else: # overlapping atom.SetProp('_Category', 'overlapping') # if self._debug_draw: # depracated... but this could be useful... # high = list(internals) + list(attachments) + list(anchors) # color = {**{i: (0, 0.8, 0) for i in internals}, # **{i: (0, 0, 0.8) for i in attachments}, # **{i: (0.8, 0, 0.8) for i in anchors}} # print('Purple: anchor atoms, Blue: attachments, Green: internals') # self.draw_nicely(mol, highlightAtoms=high, highlightAtomColors=color) # print({atom.GetIdx(): atom.GetProp('_Category') for atom in mol.GetAtoms()}) return dict(uniques=uniques, internals=internals, attachments=attachments, pairs=pairs, dummies=dummies)
def rdkit_to_openbabel_mol(mol: Chem.Mol) -> openbabel.OBMol: """Convert a RDKit molecule to an OpenBabel molecule. :param mol: RDKit molecule """ obmol = openbabel.OBMol() # Add hydrogen atoms to complete molecule if needed rdkitmol = Chem.Mol(mol) # Perceive valence and ring information before assigning hydrogens rdkitmol.UpdatePropertyCache(strict=False) rdkitmol = Chem.AddHs(rdkitmol) # Kekulize molecule Chem.rdmolops.Kekulize(rdkitmol, clearAromaticFlags=True) # Add atoms for atom in mol.GetAtoms(): # Create new atom and assign values obatom = obmol.NewAtom() obatom.SetAtomicNum(atom.GetAtomicNum()) obatom.SetIsotope(atom.GetIsotope()) obatom.SetFormalCharge(atom.GetFormalCharge()) obatom.SetPartialCharge(atom.GetDoubleProp('_PartialCharge')) obatom.SetSpinMultiplicity(atom.GetNumRadicalElectrons() + 1) for bond in mol.GetBonds(): obmol.AddBond(bond.GetBeginAtomIdx() + 1, bond.GetEndAtomIdx() + 1, int(bond.GetBondTypeAsDouble())) obmol.AssignSpinMultiplicity(True) return obmol
def max_from_mol(self, mol: Chem.Mol = None): if mol is None: mol = self.positioned_mol return [ atom.GetDoubleProp('_Max') if atom.HasProp('_Max') else 0 for atom in mol.GetAtoms() ]
def assign_names(cls, mol: Chem.Mol, names: List[str]) -> None: """ Stores names of atoms as given in the list. totally non-standard way. PDBInfo is correct way. But too much effort. """ for name, atom in zip(names, mol.GetAtoms()): atom.SetProp('_AtomName', name) # Nonstandard. do not copy
def CalculateZagreb1(mol: Chem.Mol) -> float: """Get Zagreb index with order 1. Or ZM1. """ deltas = [x.GetDegree() for x in mol.GetAtoms()] return sum(numpy.array(deltas)**2)
def _get_new_index(self, mol: Chem.Mol, old: int, search_collapsed=True, name_restriction: Optional[str] = None) -> int: """ Given an old index check in ``_ori_i`` for what the current one is. NB. ring placeholder will be -1 and these also have ``_ori_is``. a JSON of the ori_i they summarise. :param mol: :param old: old index :param search_collapsed: seach also in ``_ori_is`` :parm name_restriction: restrict to original name. :return: """ for i, atom in enumerate(mol.GetAtoms()): if name_restriction is not None and atom.GetProp('_ori_name') != name_restriction: pass elif atom.GetIntProp('_ori_i') == old: return i elif search_collapsed and \ atom.HasProp('_ori_is') and \ old in json.loads(atom.GetProp('_ori_is')): return i else: pass else: raise ValueError(f'Cannot find {old}')
def _CalculateMoranAutocorrelation(mol: Chem.Mol, lag: int = 1, propertylabel: str = 'm') -> float: """Calculate weighted Moran autocorrelation descriptors. :param lag: topological distance between atom i and atom j. :param propertylabel: type of weighted property """ Natom = mol.GetNumAtoms() prolist = [] for i in mol.GetAtoms(): temp = GetRelativeAtomicProperty(i.GetSymbol(), propertyname=propertylabel) prolist.append(temp) aveweight = sum(prolist) / Natom tempp = [numpy.square(x - aveweight) for x in prolist] GetDistanceMatrix = Chem.GetDistanceMatrix(mol) res = 0.0 index = 0 for i in range(Natom): for j in range(Natom): if GetDistanceMatrix[i, j] == lag: atom1 = mol.GetAtomWithIdx(i) atom2 = mol.GetAtomWithIdx(j) temp1 = GetRelativeAtomicProperty(element=atom1.GetSymbol(), propertyname=propertylabel) temp2 = GetRelativeAtomicProperty(element=atom2.GetSymbol(), propertyname=propertylabel) res = res + (temp1 - aveweight) * (temp2 - aveweight) index += 1 else: res = res + 0.0 if sum(tempp) == 0 or index == 0: result = 0 else: result = (res / index) / (sum(tempp) / Natom) return round(result, 3)
def _prevent_allene(self, mol: Chem.Mol) -> Chem.Mol: if not isinstance(mol, Chem.RWMol): mol = Chem.RWMol(mol) for atom in mol.GetAtoms(): if atom.GetAtomicNum() < 14: n = [] for bond in atom.GetBonds(): if bond.GetBondType().name in ('DOUBLE', 'TRIPLE'): n.append(bond) else: pass if len(n) > 2: #this is a mess! log.info(f'Allene issue: {n} double bonds on {atom.GetSymbol()} atom {atom.GetIdx()}!') for bond in n: bond.SetBondType(Chem.BondType().SINGLE) elif len(n) == 2: # downgrade the higher bonded one! others = [a for bond in n for a in (bond.GetBeginAtom(), bond.GetEndAtom()) if a.GetIdx() != atom.GetIdx()] others = sorted(others, key=lambda atom: sum([b.GetBondTypeAsDouble() for b in atom.GetBonds()])) log.info(f'Allene removed between {atom.GetIdx()} and {[a.GetIdx() for a in others]}') mol.GetBondBetweenAtoms(atom.GetIdx(), others[-1].GetIdx()).SetBondType(Chem.BondType.SINGLE) else: pass else: continue return mol
def mol_to_nx(mol: Chem.Mol): try: from tf_routes import preprocessing return preprocessing._mol_to_nx_full_weight(mol) except ModuleNotFoundError: G = nx.Graph() for atom in mol.GetAtoms(): G.add_node( atom.GetIdx(), atomic_num=atom.GetAtomicNum(), formal_charge=atom.GetFormalCharge(), chiral_tag=atom.GetChiralTag(), hybridization=atom.GetHybridization(), num_explicit_hs=atom.GetNumExplicitHs(), is_aromatic=atom.GetIsAromatic(), ) for bond in mol.GetBonds(): G.add_edge( bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond_type=bond.GetBondType(), ) return G
def CalculateHalogenNumber(mol: Chem.Mol) -> float: """Calculate number of Halogens.""" i = 0 for atom in mol.GetAtoms(): if atom.GetAtomicNum() in [9, 17, 35, 53]: i += 1 return i
def CalculateHeavyMolWeight(mol: Chem.Mol) -> float: """Calculate molecular weight of heavy atoms.""" MolWeight = 0 for atom in mol.GetAtoms(): if atom.GetAtomicNum() != 1: MolWeight += atom.GetMass() return MolWeight
def _CalculateElementNumber(mol: Chem.Mol, AtomicNumber=6) -> float: """Calculate number of atoms with specified element.""" i = 0 for atom in mol.GetAtoms(): if atom.GetAtomicNum() == AtomicNumber: i += 1 return i
def CalculateHeteroNumber(mol: Chem.Mol) -> float: """Calculate number of Heteroatoms.""" i = 0 for atom in mol.GetAtoms(): if atom.GetAtomicNum() not in [1, 6]: i += 1 return mol.GetNumAtoms() - i
def generate_orca_script_for_gas_phase(mol: Chem.Mol, conformer_id: int = 0) -> str: """Returns a psi4 Molecule object instance for a single conformer from a rdkit mol object. Parameters ---------- mol : Chem.Mol rdkit mol object conformer_id : int specifies the conformer to use Returns ------- mol : psi4.core.Molecule a psi4 molecule object instance """ assert type(mol) == Chem.Mol or type(mol) == Chem.PropertyMol.PropertyMol atoms = mol.GetAtoms() header_str = "! B3LYP 6-31G* \n" xyz_string_str = f"*xyz 0 1\n" for _, atom in enumerate(atoms): pos = mol.GetConformer(conformer_id).GetAtomPosition(atom.GetIdx()) xyz_string_str += f"{atom.GetSymbol()} {pos.x:.7f} {pos.y:.7f} {pos.z:.7f}\n" xyz_string_str += "*" orca_input = header_str + xyz_string_str return orca_input
def find_symmetry_classes(rdkit_mol: Chem.Mol) -> Dict[int, str]: """ Generate list of tuples of symmetry-equivalent (homotopic) atoms in the molecular graph based on: https://sourceforge.net/p/rdkit/mailman/message/27897393/ Our thanks to Dr Michal Krompiec for the symmetrisation method and its implementation. :param rdkit_mol: molecule to find symmetry classes for (rdkit mol class object) :return: A dict where the keys are the atom indices and the values are their type (type is arbitrarily based on index; only consistency is needed, no specific values) """ # Check CIPRank is present for first atom (can assume it is present for all afterwards) if not rdkit_mol.GetAtomWithIdx(0).HasProp("_CIPRank"): Chem.AssignStereochemistry(rdkit_mol, cleanIt=True, force=True, flagPossibleStereoCenters=True) # Array of ranks showing matching atoms cip_ranks = np.array( [int(atom.GetProp("_CIPRank")) for atom in rdkit_mol.GetAtoms()]) # Map the ranks to the atoms to produce a list of symmetrical atoms atom_symmetry_classes = [ np.where(cip_ranks == rank)[0].tolist() for rank in range(max(cip_ranks) + 1) ] # Convert from list of classes to dict where each key is an atom and each value is its class (just a str) atom_symmetry_classes_dict = {} # i will be used to define the class (just index based) for i, sym_class in enumerate(atom_symmetry_classes): for atom in sym_class: atom_symmetry_classes_dict[atom] = str(i) return atom_symmetry_classes_dict
def _renumber_original_indices(self, mol: Chem.Mol, mapping: Dict[int, int], name_restriction: Optional[str] = None): """ :param mol: :param mapping: old index to new index dictionary :param name_restriction: :return: """ for atom in mol.GetAtoms(): if name_restriction is not None and atom.HasProp('_ori_name') and atom.GetProp( '_ori_name') != name_restriction: continue i = atom.GetIntProp('_ori_i') if i == -1: # original indices ori = json.loads(atom.GetProp('_ori_is')) alt = [dd if dd not in mapping else mapping[dd] for dd in ori] atom.SetProp('_ori_is', json.dumps(alt)) # original neighbors ori = json.loads(atom.GetProp('_neighbors')) alt = [[dd if dd not in mapping else mapping[dd] for dd in inner] for inner in ori] atom.SetProp('_neighbors', json.dumps(alt)) elif i in mapping: atom.SetIntProp('_ori_i', mapping[i]) else: pass
def to_graph(mol: Chem.Mol): """Convert a molecule to a network x graph. A list of properties are added to every nodes and edges. Args: mol (Chem.Mol): a molecule. Returns: mol_graph (networkx.Graph): a graph representing the molecule. """ nx = _get_networkx() mol_graph = nx.Graph() for atom in mol.GetAtoms(): mol_graph.add_node( atom.GetIdx(), atomic_num=atom.GetAtomicNum(), formal_charge=atom.GetFormalCharge(), chiral_tag=atom.GetChiralTag(), hybridization=atom.GetHybridization(), num_explicit_hs=atom.GetNumExplicitHs(), implicit_valence=atom.GetImplicitValence(), degree=atom.GetDegree(), symbol=atom.GetSymbol(), ring_atom=atom.IsInRing(), is_aromatic=atom.GetIsAromatic(), ) for bond in mol.GetBonds(): mol_graph.add_edge( bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond_type=bond.GetBondType(), ) return mol_graph
def _offset_origins(self, mol: Chem.Mol): """ This is to prevent clashes. :param mol: :return: """ self._collapsed_ring_offset += 100 old2new = {} for atom in mol.GetAtoms(): if atom.GetIntProp('_ori_i') != -1: o = atom.GetIntProp('_ori_i') n = o + self._collapsed_ring_offset atom.SetIntProp('_ori_i', n) old2new[o] = n for atom in self._get_collapsed_atoms(mol): old = json.loads(atom.GetProp('_ori_is')) new = [i + self._collapsed_ring_offset for i in old] atom.SetProp('_ori_is', json.dumps(new)) old2new = {**old2new, **dict(zip(old, new))} if self._debug_draw: print('UPDATE', old2new) old_neighss = json.loads(atom.GetProp('_neighbors')) new_neighss = [[old2new[i] if i in old2new else i for i in old_neighs] for old_neighs in old_neighss] atom.SetProp('_neighbors', json.dumps(new_neighss))
def _generate_conformations_from_mol(self, mol: Chem.Mol, nr_of_conformations: int, enforceChirality: bool = True): """ Helper function - does not need to be called directly. Generates conformations from a rdkit mol object. """ charge = 0 for at in mol.GetAtoms(): if at.GetFormalCharge() != 0: charge += int(at.GetFormalCharge()) if charge != 0: print(Chem.MolToSmiles(mol)) raise NotImplementedError("Charged system") mol.SetProp("smiles", Chem.MolToSmiles(mol)) mol.SetProp("charge", str(charge)) mol.SetProp("name", str(self.name)) # generate numConfs for the smiles string new_confs = Chem.rdDistGeom.EmbedMultipleConfs( mol, numConfs=nr_of_conformations, enforceChirality=enforceChirality, ignoreSmoothingFailures=True, ) # NOTE enforceChirality! # AllChem.AlignMolConformers(mol) assert int(mol.GetNumConformers()) != 0 assert int(mol.GetNumConformers()) == nr_of_conformations return mol
def get_node_infos(molecule: Chem.Mol) -> List[NodeInfo]: return [ NodeInfo( symbol=ase.data.chemical_symbols[atom.GetAtomicNum()], chiral_tag=atom.GetChiralTag(), ) for atom in molecule.GetAtoms() ]
def from_mol_to_ani_input(mol: Chem.Mol) -> dict: """ Generates atom_list and coord_list entries from rdkit mol. Parameters ---------- mol : rdkit.Chem.Mol Returns ------- { 'ligand_atoms' : atoms, 'ligand_coords' : coord_list} """ atom_list = [] coord_list = [] for a in mol.GetAtoms(): atom_list.append(a.GetSymbol()) pos = mol.GetConformer().GetAtomPosition(a.GetIdx()) coord_list.append([pos.x, pos.y, pos.z]) _ = write_pdb(mol, 'tmp.pdb') topology = md.load('tmp.pdb').topology os.remove('tmp.pdb') return { 'ligand_atoms': ''.join(atom_list), 'ligand_coords': np.array(coord_list) * unit.angstrom, 'ligand_topology': topology, }