def _molecule_from_rdmol( rdmol: Chem.Mol, smiles: str, matches: Iterable[Sequence[int]], split: bool = True, ) -> Generator[Molecule, None, None]: """Construct a PLAMS molecule from the passed rdkit mol's ``MolBlock``.""" for tup in matches: try: i, *_, j = tup # type: int, Any, None | int except ValueError: i = tup[0] j = None # Split the capping atom (j) from the main molecule if j is not None and split: if i > j: i -= 1 rdmol_edit = Chem.EditableMol(rdmol) rdmol_edit.RemoveAtom(j) rdmol_new = rdmol_edit.GetMol() anchor = rdmol_new.GetAtoms()[i] anchor.SetFormalCharge(anchor.GetFormalCharge() - 1) else: rdmol_new = rdmol # Parse the .mol block and convert it into a PLAMS molecule mol_block = Chem.MolToMolBlock(rdmol) iterator = _iter_mol_block(mol_block, size=len(rdmol.GetAtoms())) mol = Molecule() mol.atoms = [Atom(symbol=symbol, coords=xyz, mol=mol) for symbol, xyz in iterator] for bond in rdmol.GetBonds(): at1 = mol.atoms[bond.GetBeginAtomIdx()] at2 = mol.atoms[bond.GetEndAtomIdx()] mol.add_bond(Bond(at1, at2, order=bond.GetBondTypeAsDouble())) # Set properties and yield mol.properties.smiles = smiles mol.properties.dummies = mol.atoms[i] mol.properties.anchor = f"{mol.properties.dummies.symbol}{i + 1}" yield mol
def from_rdmol(rdkit_mol, confid=-1): """ Translate an RDKit molecule into a PLAMS molecule type. :parameter rdkit_mol: RDKit molecule :parameter int confid: conformer identifier from which to take coordinates :type rdkit_mol: rdkit.Chem.Mol :return: a PLAMS molecule :rtype: plams.Molecule """ if isinstance(rdkit_mol, Molecule): return rdkit_mol # Create plams molecule plams_mol = Molecule() total_charge = 0 try: Chem.Kekulize(rdkit_mol) except: pass conf = rdkit_mol.GetConformer(id=confid) for rd_atom in rdkit_mol.GetAtoms(): pos = conf.GetAtomPosition(rd_atom.GetIdx()) ch = rd_atom.GetFormalCharge() pl_atom = Atom(rd_atom.GetAtomicNum(), coords=(pos.x, pos.y, pos.z), charge=ch) if rd_atom.GetPDBResidueInfo(): pl_atom.properties.pdb_info = get_PDBResidueInfo(rd_atom) plams_mol.add_atom(pl_atom) total_charge += ch for bond in rdkit_mol.GetBonds(): at1 = plams_mol.atoms[bond.GetBeginAtomIdx()] at2 = plams_mol.atoms[bond.GetEndAtomIdx()] plams_mol.add_bond(Bond(at1, at2, bond.GetBondTypeAsDouble())) plams_mol.charge = total_charge for propname in rdkit_mol.GetPropNames(): plams_mol.properties[propname] = rdkit_mol.GetProp(propname) return plams_mol
def run_ff_anionic(mol: Molecule, anchor: Atom, s: Settings) -> None: r"""Assign neutral parameters to an anionic species (*e.g.* carboxylate). Consists of 4 distinct steps: * **mol** is capped with a proton: *e.g.* :math:`RCO_2^- \rightarrow RCO_2H`. * Parameters are guessed for both fragments (using MATCH_) and then recombined into **mol**. * The capping proton is removed again. * The atomic charge of **anchor** is adjusted such that the total moleculair charge becomes zero. Performs an inplace update of **mol**. .. _MATCH: http://brooks.chem.lsa.umich.edu/index.php?page=match&subdir=articles/resources/software Parameters ---------- mol : :class:`Molecule<scm.plams.mol.molecule.Molecule>` A cationic molecule. anchor : :class:`Atom<scm.plams.mol.atom.Atom>` The atom in **mol** with the formal negative charge. s : :class:`Settings<scm.plams.core.settings.Settings>` The job Settings to-be passed to :class:`MATCHJob<nanoCAT.ff.match_job.MATCHJob>`. See Also -------- :func:`run_match_job()<nanoCAT.ff.ff_assignment.run_match_job>` Assign atom types and charges to **mol** based on the results of MATCH_. :func:`run_ff_cationic()<nanoCAT.ff.ff_cationic.run_ff_cationic>` Assign neutral parameters to a cationic species (*e.g.* ammonium). """ # noqa if anchor not in mol: raise MoleculeError("Passed 'anchor' is not part of 'mol'") anchor.properties.charge = 0 # Cap the anion with a proton mol_with_h = add_Hs(mol) _cap_h = mol_with_h[-1] cap_h = Atom(atnum=_cap_h.atnum, coords=_cap_h.coords, mol=mol, settings=mol[1].properties.copy()) cap_h.properties.pdb_info.IsHeteroAtom = False cap_h.properties.pdb_info.Name = 'Hxx' mol.add_atom(cap_h) mol.add_bond(Bond(anchor, cap_h, mol=mol)) # Guess parameters and remove the capping proton run_match_job(mol, s) mol.delete_atom(cap_h) # Set the total charge of the system to 0 anchor.properties.charge_float -= sum(at.properties.charge_float for at in mol) return None