Source code for schrodinger.structutils.interactions.hbond

"""
Module for identifying hydrogen and halogen bond interactions.

Copyright Schrodinger, LLC. All rights reserved.
"""

import schrodinger
from schrodinger import structure
from schrodinger.infra import mmbitset
from schrodinger.infra import structure as infrastructure
from schrodinger.structutils import pbc_tools

HYDROGEN_BONDS, HALOGEN_BONDS, AROMATIC_HBONDS = list(range(3))


def _get_standardized_atom_set(st, atoms1=None, st2=None, atoms2=None):
    """
    Checks atom list and structures passed for None values and
    determines the appropriate value for these parameters to be
    used by the infrastructure interaction functions.

    Also checks if an atom list is explicitly passed as None and returns
    a full list of atom indices if so.

    Raises a ValueError when an empty atom list is passed.

    :param st: Structure containing the atoms
    :type structure: `structure.Structure`

    :param atoms1: Iterable of atom objects or indices or None
    :type atoms1: iterable or None

    :param st2: Second structure containing atoms2 atoms or None. if None,
        st will be used.
    :type st2: `structure.Structure` or None

    :param atoms2: Iterable of atom objects or indices or None
    :type atoms2: iterable or None

    :return: (1st structure, 1st atom list, 2nd structure, 2nd atom list)
    :rtype: tuple
    """
    if st2 is None:
        st2 = st

    if atoms1 is None:
        # When None is specified, use all atoms
        atoms1_bs = mmbitset.Bitset(size=st.atom_total)
        atoms1_bs.fill()
    elif atoms1:
        atoms1_bs = mmbitset.Bitset.from_list(st.atom_total, atoms1)
    else:
        raise ValueError("Empty atom subset specified")

    if atoms2 is None:
        if st2 != st:
            atoms2_bs = mmbitset.Bitset(size=st2.atom_total)
            atoms2_bs.fill()
        elif atoms1 is None or atoms1_bs.all():
            atoms2_bs = atoms1_bs
        else:
            # same structure, but a list was specified for atoms1. That
            # means the caller (probably) wants interactions between that list
            # and everyting else
            atoms2_bs = atoms1_bs.copy()
            atoms2_bs.invert()
    elif atoms2:
        atoms2_bs = mmbitset.Bitset.from_list(st2.atom_total, atoms2)
    else:
        raise ValueError("Empty atom subset specified")

    return st, atoms1_bs, st2, atoms2_bs


[docs]def get_maestro_params(interaction_type): """ Gets current Maestro preference values. Usage:: try: params = get_maestro_params(hbonds.HYDROGEN_BONDS) except schrodinger.MaestroNotAvailableError: params = get_default_params(hbonds.HYDROGEN_BONDS) pairs = get_donor_acceptor_bonds(params, st1, atoms1, st2, atoms2) This attempts to read all current Maestro defaults. However, there is no current way to gaurantee that it is complete or exhaustive. """ maestro = schrodinger.get_maestro() if not maestro: msg = 'Cannot use get_maestro_params() outside of maestro' raise schrodinger.MaestroNotAvailableError(msg) def get_option(name, setter): value = maestro.get_command_option('hbondcriteria', name) if value: setter(float(value)) params = get_default_params(interaction_type) if interaction_type == HALOGEN_BONDS: value = maestro.get_command_option('hbondcriteria', 'halogendistance') if value: params[0].setMaximumDistance(float(value)) params[1].setMaximumDistance(float(value)) get_option('donorminimumangleasdonor', params[0].setMinimumDonorAngle) get_option('acceptorminimumangleasdonor', params[0].setMinimumAcceptorAngle) get_option('acceptormaximumangleasdonor', params[0].setMaximumAcceptorAngle) get_option('donorminimumangleasacceptor', params[1].setMinimumDonorAngle) get_option('acceptorminimumangleasacceptor', params[1].setMinimumAcceptorAngle) elif interaction_type == HYDROGEN_BONDS: get_option('distance', params[0].setMaximumDistance) get_option('donorangle', params[0].setMinimumDonorAngle) get_option('acceptorangle', params[0].setMinimumAcceptorAngle) elif interaction_type == AROMATIC_HBONDS: get_option('aromatichbonddistance_o', params[0].setMaximumDistance) get_option('aromatichbonddistance_n', params[1].setMaximumDistance) get_option('aromatichbonddonorminangle_o', params[0].setMinimumDonorAngle) get_option('aromatichbonddonorminangle_n', params[1].setMinimumDonorAngle) get_option('aromatichbonddonormaxangle_n', params[1].setMaximumDonorAngle) value = maestro.get_command_option('hbondcriteria', 'aromatichbondacceptorminangle') if value: params[0].setMinimumAcceptorAngle(float(value)) params[1].setMinimumAcceptorAngle(float(value)) return params
[docs]def get_default_params(interaction_type): """Get query parameters for an interaction type.""" if interaction_type == HALOGEN_BONDS: donor_param = \ infrastructure.AtomQueryParams(infrastructure.HALOGEN_DONOR) acceptor_param = infrastructure.AtomQueryParams( infrastructure.HALOGEN_ACCEPTOR) params = [donor_param, acceptor_param] elif interaction_type == HYDROGEN_BONDS: # The defaults will return hydrogen bonds. params = [infrastructure.AtomQueryParams()] elif interaction_type == AROMATIC_HBONDS: # Detect aromatic H-bonds (this is similar to what Maestro code does) aromatic_O_params = infrastructure.AtomQueryParams( infrastructure.AROMATIC_H_TO_O_ACCEPTOR) aromatic_N_params = infrastructure.AtomQueryParams( infrastructure.AROMATIC_H_TO_SP2_N_ACCEPTOR) params = [aromatic_O_params, aromatic_N_params] else: msg = ("interaction_type must be one of " "hbond.HYDROGEN_BONDS, hbond.HALOGEN_BONDS, or" "hbond.AROMATIC_HBONDS.") raise ValueError(msg) return params
def _get_params(interaction_type=HYDROGEN_BONDS, max_dist=None, min_donor_angle=None, min_acceptor_angle=None, max_acceptor_angle=None): """ Get query parameters matching the given criteria. """ params = get_default_params(interaction_type) for param in params: if max_dist is not None: param.setMaximumDistance(max_dist) if min_donor_angle is not None: param.setMinimumDonorAngle(min_donor_angle) if min_acceptor_angle is not None: param.setMinimumAcceptorAngle(min_acceptor_angle) if max_acceptor_angle is not None: param.setMaximumAcceptorAngle(max_acceptor_angle) return params
[docs]def get_hbond_params( max_dist: float = None, min_donor_angle: float = None, min_acceptor_angle: float = None, max_acceptor_angle: float = None) -> infrastructure.AtomQueryParams: """ Return hydrogen bond `AtomQueryParams` object with the given criteria. :param max_dist: See `get_hydrogen_bonds`. :param min_donor_angle: See `get_hydrogen_bonds`. :param min_acceptor_angle: See `get_hydrogen_bonds`. :param max_acceptor_angle: See `get_hydrogen_bonds`. :return: `AtomQueryParams` with given hydrogen bond criteria. """ return _get_params(HYDROGEN_BONDS, max_dist, min_donor_angle, min_acceptor_angle, max_acceptor_angle)[0]
def _get_interactions(st, atoms1=None, st2=None, atoms2=None, max_dist=None, min_donor_angle=None, min_acceptor_angle=None, max_acceptor_angle=None, interaction_type=HYDROGEN_BONDS, honor_pbc=True): """ Return hydrogen or halogen bonds formed between two sets of atoms. Criteria for donor and acceptor angles and bond distance can be set. If not set, the default will be used. Both intra and inter structure interactions can be found based on the following: 1) If only a single structure is passed and no atom sets are specified, all interaction within that structure will be identified. 2) If a single structure is passed and only atoms1 is specified, interactions between atoms in that set and all other atoms in the structure will be found. 3) If a single structure is passed and both atoms1 and atoms2 are specified, interactions between these two sets within the structure will be found. 4) If two structures are passed, interactions between atoms from st within atoms1 and atoms in st2 within atoms2 will be found. If either atom set is None, all atoms in that structure will be considered. Note that if either atom set is specified as an empty iterator rather than None, a ValueError will be raised. :param st: First structure to detect interactions for. If st2 is also specified, interactions between the specified atom subsets for each structure will be identified. Otherwise, interactions within this structure will be identified. :type st: `structure.Structure` :param atoms1: First set of atom indices to check within st. If not specified, all atoms will be used. :type atom_set: list of ints or `structure._StructureAtom` objects or None :param st2: Structure containing the atoms specified in atoms2. If None, st will be used for both sets. :type st2: `structure.Structure` or None :param atoms2: Second set of atom indices to check within st. If not specified, all atoms will be used. :type atoms2: list of ints or `structure._StructureAtom` objects or None :param max_dist: Maximum allowable bond distance. If not specified, a default value will be used. :type max_dist: float :param min_donor_angle: Minimum allowable donor angle. If not specified, a default value will be used. :type min_donor_angle: float :param min_acceptor_angle: Minimum allowable acceptor angle. If not specified, a default value will be used. :type min_acceptor_angle: float :param max_acceptor_angle: Maximum allowable acceptor angle. If not specified, a default value will be used. :type max_acceptor_angle: float :param interaction_type: Type of interaction to identify. Should be one of l{HYDROGEN_BONDS}, l{HALOGEN_BONDS} :type interaction_type: int :param honor_pbc: Honor Periodic Boundary Conditions, if defined as properties in the structure, and if st is equal to st2. Default is True. :type honor_pbc: bool :return: A list of (donor atom object, acceptor atom object) for each hydrogen or halogen bond between the subsets of atoms. :rtype: list of`structure._StructureAtom`, `structure._StructureAtom`) tuples """ params = _get_params(interaction_type, max_dist, min_donor_angle, min_acceptor_angle, max_acceptor_angle) return get_donor_acceptor_bonds(params, st, atoms1, st2, atoms2, honor_pbc)
[docs]def get_hydrogen_bonds(st, atoms1=None, st2=None, atoms2=None, max_dist=None, min_donor_angle=None, min_acceptor_angle=None, max_acceptor_angle=None, honor_pbc=True): """ Return hydrogen bonds meeting the specified threshold criteria. See get_interactions for details on how structure and atom set parameters are interpreted. Threshold values set to None will use the default values from structureinteraction.h :param st: Structure containing the two atom sets. :type st: `structure.Structure` :param atoms1: First set of atom indices to check within st. If not specified, all atoms will be used. :type atom_set: list of ints or `structure._StructureAtom` objects or None :param st2: Structure containing the atoms specified in atoms2. If None, st will be used for both sets. :type st2: `structure.Structure` or None :param atoms2: Second set of atom indices to check within st. If not specified, all atoms will be used. :type atoms2: list of ints or `structure._StructureAtom` objects or None :param max_dist: Maximum allowable bond distance. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type max_dist: float :param min_donor_angle: Minimum allowable donor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type min_donor_angle: float :param min_acceptor_angle: Minimum allowable acceptor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type min_acceptor_angle: float :param max_acceptor_angle: Maximum allowable acceptor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type max_acceptor_angle: float :param honor_pbc: Honor Periodic Boundary Conditions, if defined as properties in the structure, and if st is equal to st2. Default is True. :type honor_pbc: bool :return: A list of (donor atom object, acceptor atom object) for each hydrogen bond between the subsets of atoms. :rtype: list of (`structure._StructureAtom`, `structure._StructureAtom`) tuples """ return _get_interactions(st, atoms1=atoms1, st2=st2, atoms2=atoms2, max_dist=max_dist, min_donor_angle=min_donor_angle, min_acceptor_angle=min_acceptor_angle, max_acceptor_angle=max_acceptor_angle, interaction_type=HYDROGEN_BONDS, honor_pbc=honor_pbc)
[docs]def match_hbond(atom1, atom2, distance_max=None, donor_angle=None, acceptor_angle=None, honor_pbc=True): """ Return whether atom1-atom2 represents an H-bond where either atom1 or atom2 is the acceptor heavy atom, and the other atom is the donor hydrogen. NOTE: If you are searching for hbonds, it is almost certainly preferable to use get_hydrogen_bonds(st1, st2). To match as hydrogen bond, 1. the atom1-atom2 distance must be less than or equal to 'distance_max', 2. the N-H..X angle must be at least 'donor_angle', and 3. the H..X-Y angle must be at least 'acceptor_angle'. :type atom1: `_StructureAtom` :param atom1: First atom. :type atom2: `_StructureAtom` :param atom2: Second atom (either from the same or a different `Structure`. :type distance_max: float :param distance_max: Maximum accepted `atom1`-`atom2` H-bond distance. :type donor_angle: float :param donor_angle: Minimum accepted N-H..X angle. :type acceptor_angle: float :param acceptor_angle: Minimum accepted H..X-Y angle. :param honor_pbc: Honor Periodic Boundary Conditions, if defined as properties in the structure, and if both atoms are from the same CT. :type honor_pbc: bool :rtype: bool :return: Does atom1-atom2 count as an H-bond under the criteria provided? """ params = _get_params(max_dist=distance_max, min_donor_angle=donor_angle, min_acceptor_angle=acceptor_angle) pbc = pbc_tools.get_pbc(atom1._ct, atom2._ct, honor_pbc) return infrastructure.are_hbonded(atom1._cpp_atom, atom2._cpp_atom, params[0], pbc)
[docs]def get_halogen_bonds(st, atoms1=None, st2=None, atoms2=None, max_dist=None, min_donor_angle=None, min_acceptor_angle=None, max_acceptor_angle=None, honor_pbc=True): """ Return halogen bonds meeting the specified threshold criteria. See get_interactions for details on how structure and atom set parameters are interpreted. Threshold values set to None will use the default values from structureinteraction.h :param st: Structure containing the two atom sets. :type st: `structure.Structure` :param atoms1: First set of atom indices to check within st. If not specified, all atoms will be used. :type atom_set: list of ints or `structure._StructureAtom` objects or None :param st2: Structure containing the atoms specified in atoms2. If None, st will be used for both sets. :type st2: `structure.Structure` or None :param atoms2: Second set of atom indices to check within st. If not specified, all atoms will be used. :type atoms2: list of ints or `structure._StructureAtom` objects or None :param max_dist: Maximum allowable bond distance. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type max_dist: float :param min_donor_angle: Minimum allowable donor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type min_donor_angle: float :param min_acceptor_angle: Minimum allowable acceptor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type min_acceptor_angle: float :param max_acceptor_angle: Maximum allowable acceptor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type max_acceptor_angle: float :param honor_pbc: Honor Periodic Boundary Conditions, if defined as properties in the structure, and if st is equal to st2. Default is True. :type honor_pbc: bool :return: A list of (donor atom object, acceptor atom object) for each halogen bond between the subsets of atoms. :rtype: list of (`structure._StructureAtom`, `structure._StructureAtom`) tuples """ return _get_interactions(st, atoms1=atoms1, st2=st2, atoms2=atoms2, max_dist=max_dist, min_donor_angle=min_donor_angle, min_acceptor_angle=min_acceptor_angle, max_acceptor_angle=max_acceptor_angle, interaction_type=HALOGEN_BONDS, honor_pbc=honor_pbc)
[docs]def get_aromatic_hydrogen_bonds(st, atoms1=None, st2=None, atoms2=None, max_dist=None, min_donor_angle=None, min_acceptor_angle=None, max_acceptor_angle=None, honor_pbc=True): """ Return aromatic hydrogen bonds meeting the specified threshold criteria. See get_interactions for details on how structure and atom set parameters are interpreted. Threshold values set to None will use the default values from structureinteraction.h :param st: Structure containing the two atom sets. :type st: `structure.Structure` :param atoms1: First set of atom indices to check within st. If not specified, all atoms will be used. :type atom_set: list of ints or `structure._StructureAtom` objects or None :param st2: Structure containing the atoms specified in atoms2. If None, st will be used for both sets. :type st2: `structure.Structure` or None :param atoms2: Second set of atom indices to check within st. If not specified, all atoms will be used. :type atoms2: list of ints or `structure._StructureAtom` objects or None :param max_dist: Maximum allowable bond distance. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type max_dist: float :param min_donor_angle: Minimum allowable donor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type min_donor_angle: float :param min_acceptor_angle: Minimum allowable acceptor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type min_acceptor_angle: float :param max_acceptor_angle: Maximum allowable acceptor angle. If not specified, the Maestro preference default value will be used. (Not the current user setting). :type max_acceptor_angle: float :param honor_pbc: Honor Periodic Boundary Conditions, if defined as properties in the structure, and if st is equal to st2. Default is True. :type honor_pbc: bool :return: A list of (donor atom object, acceptor atom object) for each hydrogen bond between the subsets of atoms. :rtype: list of (`structure._StructureAtom`, `structure._StructureAtom`) tuples """ return _get_interactions(st, atoms1=atoms1, st2=st2, atoms2=atoms2, max_dist=max_dist, min_donor_angle=min_donor_angle, min_acceptor_angle=min_acceptor_angle, max_acceptor_angle=max_acceptor_angle, interaction_type=AROMATIC_HBONDS, honor_pbc=honor_pbc)
[docs]def get_donor_acceptor_bonds(params, st, atoms1=None, st2=None, atoms2=None, honor_pbc=True): """ Get the requested type of interactions :param params: Parameters for accepting a donor/acceptor pair. :type params: list(schrodinger.infra.structure.AtomQueryParams) :param st: Structure containing the two atom sets. :type st: `structure.Structure` :param atoms1: First set of atom indices to check within st. If not specified, all atoms will be used. :type atom_set: list of ints or `structure._StructureAtom` objects or None :param st2: Structure containing the atoms specified in atoms2. If None, st will be used for both sets. :type st2: `structure.Structure` or None :param atoms2: Second set of atom indices to check within st. If not specified, all atoms will be used. :type atoms2: list of ints or `structure._StructureAtom` objects or None :param honor_pbc: Honor Periodic Boundary Conditions, if defined as properties in the structure, and if st is equal to st2. Default is True. :type honor_pbc: bool :return: A list of (donor atom object, acceptor atom object) for each hydrogen bond between the subsets of atoms. :rtype: list of (`structure._StructureAtom`, `structure._StructureAtom`) tuples """ st, aset_1, st2, aset_2 = _get_standardized_atom_set( st, atoms1, st2, atoms2) rings = infrastructure.get_sssr(st) if st2 == st: rings2 = rings else: rings2 = infrastructure.get_sssr(st2) pbc = pbc_tools.get_pbc(st, st2, honor_pbc) hbonds = infrastructure.get_hydrogen_bonds(st, aset_1, st2, aset_2, params, rings, rings2, pbc) unique_hbonds = list() for hbond in hbonds: donor_cpp_atom = hbond.getDonor() donor_atom = structure.get_pyatom_from_cppatom(donor_cpp_atom) acceptor_cpp_atom = hbond.getAcceptor() acceptor_atom = structure.get_pyatom_from_cppatom(acceptor_cpp_atom) unique_hbonds.append((donor_atom, acceptor_atom)) return unique_hbonds