Source code for schrodinger.application.desmond.stage.app.absolute_binding.struc

"""
Absolute binding FEP structure module.

Copyright Schrodinger, LLC. All rights reserved.
"""
from typing import List
from typing import Tuple

from schrodinger.application.desmond import constants
from schrodinger.application.desmond import struc
from schrodinger.application.desmond import util
from schrodinger.application.desmond.constants import \
    ANALYSIS_REFERENCE_COORD_PROPERTIES
from schrodinger.application.desmond.constants import FEP_ABSOLUTE_ENERGY
from schrodinger.application.desmond.constants import FEP_ABSOLUTE_LIGAND
from schrodinger.application.desmond.constants import \
    FEP_DG_CROSSLINK_CORRECTION
from schrodinger.application.desmond.constants import FEP_HASH_ID
from schrodinger.application.desmond.constants import FEP_MAPPING
from schrodinger.application.desmond.constants import FEP_ORIGINAL_TITLE
from schrodinger.application.desmond.constants import FEP_RESTRAIN
from schrodinger.application.desmond.constants import GCMC_LIGAND
from schrodinger.application.desmond.constants import REFERENCE_COORD_PROPERTIES
from schrodinger.application.desmond.constants import REST_HOTREGION
from schrodinger.structure import Structure
from schrodinger.structutils.build import reorder_water


[docs]def prepare_input_structures( input_sts: List[Structure]) -> Tuple[List[Structure], List[Structure]]: """ Prepare and return the input ligand and receptor structures for absolute binding fep. Also include the solvent and membrane structures if present. Raises a `ValueError` if no receptor or ligands were found. :param input_sts: The list of input structures containing at least one receptor and one or more ligands. It may also include solvent or membrane as separate structures. :return: List of environment structures and list of ligands used in the simulation. """ from schrodinger.application.scisol.packages.fep import fepmae input_sts = [st.copy() for st in input_sts] receptor_st, solvent_st, membrane_st, ligand_sts = fepmae.filter_receptors_and_ligands( input_sts) if not ligand_sts: raise ValueError("ERROR: Missing ligand structure.") # Cleans up FEP-related atom properties. FEP_ATOM_PROPNAMES = [ FEP_MAPPING, REST_HOTREGION, FEP_ABSOLUTE_ENERGY, FEP_ABSOLUTE_LIGAND, GCMC_LIGAND, FEP_RESTRAIN, constants.FEP_ABSOLUTE_BINDING_LIGAND ] + REFERENCE_COORD_PROPERTIES + ANALYSIS_REFERENCE_COORD_PROPERTIES for st in struc.struc_iter([receptor_st, solvent_st, membrane_st] + ligand_sts): struc.delete_atom_properties(st, FEP_ATOM_PROPNAMES) struc.delete_structure_properties(st, [ FEP_DG_CROSSLINK_CORRECTION, constants.FEP_STRUC_TAG, FEP_HASH_ID, FEP_ORIGINAL_TITLE ]) # build_geometry stage will read box from the solvent if st is not solvent_st: struc.delete_structure_properties(st, constants.SIM_BOX) for st in ligand_sts: st.property[ constants.FEP_STRUC_TAG] = constants.FEP_STRUC_TAG.VAL.LIGAND struc.set_atom_properties( st.atom, [constants.FEP_ABSOLUTE_BINDING_LIGAND, constants.GCMC_LIGAND], values=[1, 1]) st.property[FEP_HASH_ID] = util.str2hexid(st.title) st.property[FEP_ORIGINAL_TITLE] = st.title # Store the reference coordinates for the analysis only, # not for the restraints struc.set_ct_reference_coordinates( st, prop_names=ANALYSIS_REFERENCE_COORD_PROPERTIES) if receptor_st is None: raise ValueError("ERROR: Missing receptor structure.") receptor_st.property[ constants.FEP_STRUC_TAG] = constants.FEP_STRUC_TAG.VAL.RECEPTOR struc.set_ct_reference_coordinates( receptor_st, prop_names=ANALYSIS_REFERENCE_COORD_PROPERTIES) # Need to move the crystal waters to the end of the structure # to prevent the cross link restraint atom indicies from changing. receptor_st = reorder_water(receptor_st) if solvent_st is not None: solvent_st.property[ constants.FEP_STRUC_TAG] = constants.FEP_STRUC_TAG.VAL.SOLVENT if membrane_st is not None: membrane_st.property[ constants.FEP_STRUC_TAG] = constants.FEP_STRUC_TAG.VAL.MEMBRANE return list(struc.struc_iter(receptor_st, solvent_st, membrane_st)), ligand_sts
[docs]def prepare_md_structures(cts: List[Structure], ligand_hash_id: str) -> List[Structure]: """ Extracts the environment cts, the ligand ct that matches the given `ligand_hash_id` and update annotate with the `FEP_HASH_ID` and `FEP_ORIGINAL_TITLE` properties. """ filtered_cts = [] ligand_ct = None for ct in cts: if ct.property[ constants.FEP_STRUC_TAG] == constants.FEP_STRUC_TAG.VAL.LIGAND: # Temporary patch for DESMOND-10213 and DESMOND-10159 # FIXME: Remove when the underlying problems have been fixed if util.str2hexid(ct.title).endswith(ligand_hash_id): ligand_ct = ct filtered_cts.append(ct) else: filtered_cts.append(ct) ligand_ct.property[FEP_HASH_ID] = ligand_hash_id ligand_ct.property[FEP_ORIGINAL_TITLE] = ligand_ct.title return filtered_cts
[docs]def filter_fep_structures(sts: List[Structure], leg: str) -> List[Structure]: """ Extract the structures with the matching `leg`. """ return list( filter( lambda st: st.property.get(constants.ABSOLUTE_BINDING_LEGS) == leg, sts))
[docs]def make_dummy(ct: Structure): """ Mark structure as the dummy and add a dummy atom. This is needed for the solvent leg to run as a relative calculation. """ ct.property[constants.ABFEP_DUMMY_LIGAND] = 1 # Copy the coordinates of the first atom (if present) # so the dummy atom doesn't increase the box size. coords = ct.getXYZ()[0, :] if ct.atom_total else [0.0, 0.0, 0.0] new_atom = ct.addAtom('Na', *coords) new_atom.property[constants.FEP_MAPPING] = 0 new_atom.property[constants.REST_HOTREGION] = 0 new_atom.property[GCMC_LIGAND] = 0 new_atom.formal_charge = 0 new_atom.pdbres = 'DU ' return new_atom
[docs]def is_dummy_ligand(ct: Structure) -> bool: # DESMOND-11245: convert to just DUMMY_LIGAND return ct.property.get( constants.ABFEP_DUMMY_LIGAND) == 1 or ct.property.get( constants.DUMMY_LIGAND) == 1