예제 #1
0
def modified_minimum_scan_rdkit(ligand: Molecule, bond_tuple: Tuple[int, int],
                                anchor: Atom) -> None:
    """A modified version of the :func:`.global_minimum_scan_rdkit` function.

    * Uses the ligand vector as criteria rather than the energy.
    * Geometry optimizations are constrained during the conformation search.
    * Finish with a final unconstrained geometry optimization.

    See Also
    --------
    :func:`global_minimum_scan_rdkit<scm.plams.recipes.global_minimum.minimum_scan_rdkit>`:
        Optimize the molecule (RDKit UFF) with 3 different values for the given dihedral angle and
        find the lowest energy conformer.

        :param |Molecule| mol: The input molecule
        :param tuple bond_tuple: A 2-tuples containing the atomic indices of valid bonds
        :return |Molecule|: A copy of *mol* with a newly optimized geometry

    """
    # Define a number of variables and create 3 copies of the ligand
    angles = (-120, 0, 120)
    mol_list = [ligand.copy() for _ in range(3)]
    for angle, mol in zip(angles, mol_list):
        bond = mol[bond_tuple]
        atom = mol[bond_tuple[0]]
        mol.rotate_bond(bond, atom, angle, unit='degree')
    rdmol_list = [molkit.to_rdmol(mol, properties=False) for mol in mol_list]

    # Optimize the (constrained) geometry for all dihedral angles in angle_list
    # The geometry that yields the minimum energy is returned
    fixed = _find_idx(mol, bond)
    for rdmol in rdmol_list:
        ff = UFF(rdmol)
        for f in fixed:
            ff.AddFixedPoint(f)
        ff.Minimize()

    # Find the conformation with the optimal ligand vector
    cost_list = []
    try:
        i = ligand.atoms.index(anchor)
    except ValueError:
        i = -1  # Default to the origin as anchor

    for rdmol in rdmol_list:
        xyz = rdmol_as_array(rdmol)
        if i == -1:  # Default to the origin as anchor
            xyz = np.vstack([xyz, [0, 0, 0]])
        rotmat = optimize_rotmat(xyz, i)
        xyz[:] = xyz @ rotmat.T
        xyz -= xyz[i]
        cost = np.exp(xyz[:, 1:]).sum()
        cost_list.append(cost)

    # Perform an unconstrained optimization on the best geometry and update the geometry of ligand
    j = np.argmin(cost_list)
    rdmol_best = rdmol_list[j]
    UFF(rdmol).Minimize()
    ligand.from_rdmol(rdmol_best)
예제 #2
0
def fix_h(mol: Molecule) -> None:
    """If a C=C-H angle is smaller than :math:`20` degrees, set it back to :math:`120` degrees.

    Performs an inplace update of **plams_mol**.

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

    """
    h_list = [
        atom for atom in mol if atom.atnum == 1
        and 2.0 in [bond.order for bond in mol.neighbors(atom)[0].bonds]
    ]

    rdmol = molkit.to_rdmol(mol)
    conf = rdmol.GetConformer()
    get_idx = mol.atoms.index
    set_angle = rdMolTransforms.SetAngleDeg
    get_angle = rdMolTransforms.GetAngleDeg

    update = False
    for atom in h_list:
        at1 = atom  # Central atom
        at2 = mol.neighbors(at1)[0]  # Neighbours
        at3 = [atom for atom in mol.neighbors(at2)
               if atom != at1]  # Neighbours of neighbours

        # Create 2 sets of 3 atomic indices for defining angles: at1-at2=at3
        idx_tup1 = get_idx(at3[0]), get_idx(at2), get_idx(at1)
        idx_tup2 = get_idx(at3[1]), get_idx(at2), get_idx(at1)

        if get_angle(conf, *idx_tup1) <= 20.0:
            set_angle(conf, *idx_tup1, 120.0)
            update = True
        elif get_angle(conf, *idx_tup2) <= 20.0:
            set_angle(conf, *idx_tup2, 120.0)
            update = True

    if update:
        mol.from_rdmol(rdmol)
예제 #3
0
def fix_carboxyl(mol: Molecule) -> None:
    """Resets carboxylate OCO angles if it is smaller than :math:`60` degrees.

    Performs an inplace update of **plams_mol**.

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

    """
    rdmol = molkit.to_rdmol(mol)
    conf = rdmol.GetConformer()
    matches = rdmol.GetSubstructMatches(_CARBOXYLATE)

    if matches:
        get_angle = rdMolTransforms.GetAngleDeg
        set_angle = rdMolTransforms.SetAngleDeg
        for idx in matches:
            if get_angle(conf, idx[3], idx[1], idx[0]) < 60:
                set_angle(conf, idx[2], idx[1], idx[3], 180.0)
                set_angle(conf, idx[0], idx[1], idx[3], 120.0)
        mol.from_rdmol(rdmol)