Ejemplo n.º 1
0
def _get_ligand(mol: Molecule) -> Molecule:
    """Extract a single ligand from **mol** as a copy."""
    at_list = []
    res = mol.atoms[-1].properties.pdb_info.ResidueNumber
    for at in reversed(mol.atoms):
        if at.properties.pdb_info.ResidueNumber == res:
            at_list.append(at)
        else:
            ret = Molecule()
            ret.atoms = at_list
            ret.bonds = list(
                set(chain.from_iterable(at.bonds for at in at_list)))
            return ret.copy()
Ejemplo n.º 2
0
def get_asa_fragments(qd: Molecule) -> Tuple[List[Molecule], Molecule]:
    """Construct the fragments for an activation strain analyses.

    Parameters
    ----------
    qd : |plams.Molecule|
        A Molecule whose atoms' properties should be marked with `pdb_info.ResidueName`.
        Atoms in the core should herein be marked with ``"COR"``.

    Returns
    -------
    :class:`list` [|plams.Molecule|] and |plams.Molecule|
        A list of ligands and the core.
        Fragments are defined based on connectivity patterns (or lack thereof).

    """
    # Delete all atoms within the core
    mol_complete = qd.copy()
    core = Molecule()
    core.properties = mol_complete.properties.copy()

    core_atoms = [at for at in mol_complete if at.properties.pdb_info.ResidueName == 'COR']
    for atom in core_atoms:
        mol_complete.delete_atom(atom)
        atom.mol = core

    core.atoms = core_atoms
    mol_complete.properties.name += '_frags'
    core.properties.name += '_core'

    # Fragment the molecule
    ligand_list = mol_complete.separate()

    # Set atomic properties
    for at1, at2 in zip(chain(*ligand_list), mol_complete):
        at1.properties.symbol = at2.properties.symbol
        at1.properties.charge_float = at2.properties.charge_float
    for at1, at2 in zip(core, qd):
        at1.properties.symbol = at2.properties.symbol
        at1.properties.charge_float = at2.properties.charge_float

    # Set the prm parameter which points to the created .prm file
    name = mol_complete.properties.name[:-1]
    path = mol_complete.properties.path
    prm = mol_complete.properties.prm
    for mol in ligand_list:
        mol.properties.name = name
        mol.properties.path = path
        mol.properties.prm = prm

    return ligand_list, core
Ejemplo n.º 3
0
def set_qd(qd: Molecule, mol_dict: Settings) -> Molecule:
    """Update quantum dots imported by :func:`.read_mol`."""
    # Create ligand (and anchor) molecules
    ligand = molkit.from_smiles(mol_dict.ligand_smiles)
    ligand_rdmol = molkit.to_rdmol(ligand)
    anchor = molkit.from_smiles(mol_dict.ligand_anchor)
    anchor_rdmol = molkit.to_rdmol(anchor)
    qd_rdmol = molkit.to_rdmol(qd)

    # Create arrays of atomic indices of the core and ligands
    lig_idx = 1 + np.array(qd_rdmol.GetSubstructMatches(ligand_rdmol))
    core_idx = np.arange(1, len(qd))[~lig_idx]
    lig_idx = lig_idx.ravel().tolist()
    core_idx = core_idx.tolist()

    # Guess bonds
    if mol_dict.guess_bonds:
        qd.guess_bonds(atom_subset=[qd[i] for i in lig_idx])

    # Reorder all atoms: core atoms first followed by ligands
    qd.atoms = [qd[i] for i in core_idx] + [qd[j] for i in lig_idx for j in i]

    # Construct a list with the indices of all ligand anchor atoms
    core_idx_max = 1 + len(core_idx)
    _anchor_idx = ligand_rdmol.GetSubstructMatch(anchor_rdmol)[0]
    start = core_idx_max + _anchor_idx
    stop = core_idx_max + _anchor_idx + np.product(lig_idx.shape)
    step = len(ligand)
    anchor_idx = list(range(start, stop, step))

    # Update the properties of **qd**
    for i in anchor_idx:
        qd[i].properties.anchor = True
    qd.properties.indices = list(range(1, core_idx_max)) + anchor_idx
    qd.properties.job_path = []
    qd.properties.name = mol_dict.name
    qd.properties.path = mol_dict.path
    qd.properties.ligand_smiles = Chem.CanonSmiles(mol_dict.ligand_smiles)
    qd.properties.ligand_anchor = f'{ligand[_anchor_idx].symbol}{_anchor_idx}'

    # Update the pdb_info of all atoms
    for i, at in enumerate(qd, 1):
        at.properties.pdb_info.SerialNumber = i
        if i <= core_idx_max:  # A core atom
            at.properties.pdb_info.ResidueNumber = 1
        else:  # A ligand atom
            at.properties.pdb_info.ResidueNumber = 2 + int(
                (i - core_idx_max) / len(ligand))
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
def canonicalize_mol(mol: Molecule,
                     inplace: bool = True) -> Optional[Molecule]:
    """Take a PLAMS molecule and sort its atoms based on their canonical rank.

    .. _rdkit.Chem.CanonicalRankAtoms: https://www.rdkit.org/docs/source/rdkit.Chem.rdmolfiles.html#rdkit.Chem.rdmolfiles.CanonicalRankAtoms

    Examples
    --------
    .. code:: python

        >>> from scm.plams import Molecule, from_smiles

        # Methane
        >>> mol: Molecule = from_smiles('C')
        >>> print(mol)  # doctest: +SKIP
        Atoms:
            1         H      0.640510      0.640510     -0.640510
            2         H      0.640510     -0.640510      0.640510
            3         C      0.000000      0.000000      0.000000
            4         H     -0.640510      0.640510      0.640510
            5         H     -0.640510     -0.640510     -0.640510

        >>> canonicalize_mol(mol)
        >>> print(mol)  # doctest: +SKIP
        Atoms:
            1         C      0.000000      0.000000      0.000000
            2         H     -0.640510     -0.640510     -0.640510
            3         H     -0.640510      0.640510      0.640510
            4         H      0.640510     -0.640510      0.640510
            5         H      0.640510      0.640510     -0.640510

    Parameters
    ----------
    mol : |plams.Molecule|_
        A PLAMS molecule.

    inplace : bool
        If ``True``, perform an inplace update of **mol** rather than returning
        a new :class:`Molecule` instance.

    Returns
    -------
    |plams.Molecule|_
        Optional: if ``inplace=False``, return a copy of **mol** with its atoms sorted by their
        canonical rank.

    See Also
    --------
    * rdkit.Chem.CanonicalRankAtoms_: Returns the canonical atom ranking for each atom of a
      molecule fragment.

    """  # noqa
    rdmol = molkit.to_rdmol(mol)
    idx_collection = Chem.CanonicalRankAtoms(rdmol)

    # Reverse sort Molecule.atoms by the atomic indices in idx_collection
    if inplace:
        mol.atoms = [
            at
            for _, at in sorted(zip(idx_collection, mol.atoms), reverse=True)
        ]
        return
    else:
        ret = mol.copy()
        ret.atoms = [
            at
            for _, at in sorted(zip(idx_collection, ret.atoms), reverse=True)
        ]
        return ret