Ejemplo n.º 1
0
def optimize_ligand(ligand: Molecule) -> None:
    """Optimize a ligand molecule."""
    anchor = ligand.properties.dummies

    # Split the branched ligand into linear fragments and optimize them individually
    bonds = split_mol(ligand, anchor)
    context = SplitMol(ligand, bonds)
    with context as mol_frags:
        cap_dict = ChainMap(*context._at_pairs)
        for mol in mol_frags:
            cap_list = [cap for at, cap in cap_dict.items() if at in mol]
            mol.set_dihed(180.0, anchor, cap_list)

    # Find the optimal dihedrals angle between the fragments
    for bond in bonds:
        modified_minimum_scan_rdkit(ligand, ligand.get_index(bond), anchor)

    # RDKit UFF can sometimes mess up the geometries of carboxylates: fix them
    fix_carboxyl(ligand)

    # Allign the ligand with the Cartesian X-axis.
    allign_axis(ligand, anchor)
Ejemplo n.º 2
0
def ligand_to_qd(core: Molecule, ligand: Molecule, path: str,
                 allignment: str = 'sphere',
                 idx_subset: Optional[Iterable[int]] = None) -> Molecule:
    """Function that handles quantum dot (qd, *i.e.* core + all ligands) operations.

    Combine the core and ligands and assign properties to the quantom dot.

    Parameters
    ----------
    core : |plams.Molecule|_
        A core molecule.

    ligand : |plams.Molecule|_
        A ligand molecule.

    allignment : :class:`str`
        How the core vector(s) should be defined.
        Accepted values are ``"sphere"`` and ``"surface"``:

        * ``"sphere"``: Vectors from the core anchor atoms to the center of the core.
        * ``"surface"``: Vectors perpendicular to the surface of the core.

        Note that for a perfect sphere both approaches are equivalent.

    idx_subset : :class:`Iterable<collections.anc.Iterable>` [:class:`int`], optional
        An iterable with the (0-based) indices defining a subset of atoms in **core**.
        Only relevant in the construction of the convex hull when ``allignment=surface``.

    Returns
    -------
    |plams.Molecule|_
        A quantum dot consisting of a core molecule and *n* ligands

    """
    def get_name() -> str:
        core_name = core.properties.name
        anchor = str(qd[-1].properties.pdb_info.ResidueNumber - 1)
        lig_name = ligand.properties.name
        return f'{core_name}__{anchor}_{lig_name}'

    idx_subset_ = idx_subset if idx_subset is not None else ...

    # Define vectors and indices used for rotation and translation the ligands
    vec1 = np.array([-1, 0, 0], dtype=float)  # All ligands are already alligned along the X-axis
    idx = ligand.get_index(ligand.properties.dummies) - 1
    ligand.properties.dummies.properties.anchor = True

    # Attach the rotated ligands to the core, returning the resulting strucutre (PLAMS Molecule).
    if allignment == 'sphere':
        vec2 = np.array(core.get_center_of_mass()) - sanitize_dim_2(core.properties.dummies)
        vec2 /= np.linalg.norm(vec2, axis=1)[..., None]
    elif allignment == 'surface':
        if isinstance(core.properties.dummies, np.ndarray):
            anchor = core.properties.dummies
        else:
            anchor = core.as_array(core.properties.dummies)
        vec2 = -get_surface_vec(np.array(core)[idx_subset_], anchor)
    else:
        raise ValueError(repr(allignment))

    lig_array = rot_mol(ligand, vec1, vec2, atoms_other=core.properties.dummies, core=core, idx=idx)
    qd = core.copy()
    array_to_qd(ligand, lig_array, mol_out=qd)
    qd.round_coords()

    # Set properties
    qd.properties = Settings({
        'indices': [i for i, at in enumerate(qd, 1) if
                    at.properties.pdb_info.ResidueName == 'COR' or at.properties.anchor],
        'path': path,
        'name': get_name(),
        'job_path': [],
        'prm': ligand.properties.get('prm')
    })

    # Print and return
    _evaluate_distance(qd, qd.properties.name)
    return qd