Source code for schrodinger.application.desmond.starter.ui.fep_plus

"""
FEP+ command line UI
Version 0.1

Copyright Schrodinger, LLC. All rights reserved.
"""
import argparse
import sys
import warnings
from typing import List
from typing import Optional

from schrodinger import structure
from schrodinger.application.desmond import constants
from schrodinger.application.desmond.constants import FEP_TYPES
from schrodinger.application.desmond.constants import FepLegTypes
from schrodinger.application.desmond.constants import UiMode
from schrodinger.application.desmond.starter.ui.cmdline import get_sim_time_message
from schrodinger.application.desmond.util import ensure_file_exists
from schrodinger.infra import mm

from . import cmdline
from .cmdline import Option

USAGE = """
* Run a new job:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <pv-or-fmp-file>

* Restart a previously interrupted job:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> -RESTART -checkpoint <multisim-checkpoint-file>

* Extend production simulations for certain edges:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> -extend <edge-file> -checkpoint <multisim-checkpoint-file>
  An example for the format of an edge-file:
     36da5ad:397128e
     33dd5ad:347118e
     33fe5ad:3171f8e
  Each line specifies an edge with the two node's IDs. Each node ID is a hex
  number of at least 7 digits. The two IDs are separated by a ':' (or '-' or
  '_').

* Prepare input files for multisim. Do NOT run job:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <pv-or-fmp-file> -prepare

* Run a protein residue mutation job:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <mae-file> -protein <mutation-file> -solvent_asl <SOLVENT-ASL>
  <mutation-file> follows the same format as used by $SCHRODINGER/run residue_scanning_backend.py -muts_file

* Run a protein stability job:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <mae-file> -protein <mutation-file>

* Add mutations to a complete protein fep job:
    $SCHRODINGER/fep_plus  <out-fmp-file> -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> -protein <mutation-file> -expand_protein <mae-file>

* Run a metalloprotein FEP job:
    $SCHRODINGER/fep_plus -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <pv-or-fmp-file> -mp <property-name>
"""

_DEFAULT_FEP_TIME = 5000.0


[docs]class FepPlusArgs(cmdline.FepArgs): PROGRAM_NAME = "FEP+" SUPPORTED_FEP_TYPES = [ FEP_TYPES.SMALL_MOLECULE, FEP_TYPES.METALLOPROTEIN, FEP_TYPES.COVALENT_LIGAND, FEP_TYPES.PROTEIN_SELECTIVITY, FEP_TYPES.PROTEIN_STABILITY, FEP_TYPES.LIGAND_SELECTIVITY ]
[docs] def __init__(self, opt: argparse.Namespace, argv=None): """ :param opt: Command line options with corresponding values. """ self.argv = argv or [] # Original list of arguments provided by user. # NOTE: argv does not have jobcontrol's parameters (JOBNAME, HOST, ...) super().__init__(opt)
[docs] def copy_parser_attributes(self, opt: argparse.Namespace): super().copy_parser_attributes(opt) # time_complex and time_solvent are allowed to be 0. self.time_complex = self.time if opt.time_complex is None else opt.time_complex self.time_solvent = self.time if opt.time_solvent is None else opt.time_solvent self.time_vacuum = self.time if opt.time_vacuum is None else opt.time_vacuum self.h_mass = not opt.no_h_mass if self.skip_legs: self.skip_legs = [s.lower() for s in self.skip_legs] if self.ats: # Automated torsional scaling requires use of modify dihedral self.modify_dihe = True
[docs] def validate(self): super().validate() self._validate_sim_times( [self.time_vacuum, self.time_complex, self.time_solvent]) ensure_file_exists(self.expand_protein) if self.mp: for mp_prop in self.mp: if not mp_prop.startswith(('i_fep_mp', 'i_user_mp')): sys.exit("ERROR: The metalloprotein property name must be " "of the pattern 'i_fep_mp*' or 'i_user_mp*'.") if self.mode == UiMode.NEW: if self.inp_file.endswith('.fmp'): if self.atom_mapping: warnings.warn('When fmp file is given as input, ' \ '"-atom_mapping" option will be ignored') from schrodinger.application.scisol.packages.fep import graph g = graph.Graph.deserialize(self.inp_file) if self.vacuum and (g.fep_type != constants.FEP_TYPES.SMALL_MOLECULE): sys.exit( "ERROR: For a vacuum job, the input FEP type must be " "small molecule") if self.mp: if g.fep_type != constants.FEP_TYPES.METALLOPROTEIN: sys.exit("ERROR: For a metalloprotein job, the input " "FEP type must be metalloprotein") if not g.receptor_struc: sys.exit( "ERROR: Graph must contain receptor in metalloprotein " "workflow") membrane_st, solvent_st = g.membrane_struc, g.solvent_struc else: from schrodinger.application.scisol.packages.fep.fepmae import \ filter_receptors_and_ligands # the receptor/ligands returned by filter_receptors_and_ligands # here wont be valid but the membrane and solvent will be _, solvent_st, membrane_st, _ = filter_receptors_and_ligands( structure.StructureReader(self.inp_file)) if self.mp: from schrodinger.application.scisol.packages.fep import utils err = utils.validate_mp_props(self.inp_file, self.mp) if err: sys.exit(err) if membrane_st: if not solvent_st: sys.exit("ERROR: Input system contains membrane but no " "solvent structure.") self.membrane = True
[docs] def get_time_for_leg(self, leg_type: str) -> Optional[float]: if leg_type == FepLegTypes.VACUUM: return self.time_vacuum elif leg_type == FepLegTypes.COMPLEX: return self.time_complex elif leg_type == FepLegTypes.SOLVENT: return self.time_solvent
[docs]def ui(argv: List[str]) -> FepPlusArgs: """ Parse the arguments and return an object containing the values. :param argv: List of command line arguments :return: Parsed FEP+ options """ options = get_fep_plus_options() argv1 = argv[1:] # Skip program's name. args = cmdline.parse_options(USAGE, options, argv1) return FepPlusArgs(args, argv)
[docs]def get_fep_plus_parser(add_help: bool = True) -> argparse.ArgumentParser: """ Return a parser configured with fep_plus command line options :param add_help: Whether to add help flag to the parser :return: fep_plus command line parser """ options = get_fep_plus_options() parser = cmdline.get_parser(USAGE, options, add_help=add_help) return parser
[docs]def get_fep_plus_options() -> List[cmdline.Option]: """ Return a list of options for fep_plus :return: list of fep_plus options """ options = cmdline.get_common_options() options.extend(cmdline.get_common_fep_options()) options.extend([ Option("inp_file", None, "A fmp or a pv structure file", {"nargs": "?"}), Option( "-ensemble", constants.Ensembles.MUVT, "Specify the ensemble class. Default: %(default)s.", {"metavar": "{muVT|NPT|NVT}"}, ), Option("-time", _DEFAULT_FEP_TIME, get_sim_time_message(default_time=_DEFAULT_FEP_TIME), { "type": float, "metavar": "<sim-time>" }), Option( "-time-complex", None, get_sim_time_message(default_time=_DEFAULT_FEP_TIME, leg_type="complex", default_arg="-time"), { "type": float, "metavar": "<sim-time>" }), Option( "-time-solvent", None, get_sim_time_message(default_time=_DEFAULT_FEP_TIME, leg_type="solvent", default_arg="-time"), { "type": float, "metavar": "<sim-time>" }), Option( "-time-vacuum", None, get_sim_time_message(default_time=_DEFAULT_FEP_TIME, leg_type="vacuum", default_arg="-time"), { "type": float, "metavar": "<sim-time>" }), Option( "-protein", "", "Generate and run protein residue mutation if a mutation_file is given here and a solvent_asl is also provided; " "Generate and run protein stability when a mutation_file is given here and no solvent_asl is provided", { "metavar": "<mutation>", }, ), Option( "-mp", None, "Generate and run metalloprotein workflow by " "specifying the atom property used to indicate the atoms involved " "in the zero order bond. Pass in option multiple times to specify " "multiple mp properties.", { "metavar": "<property_name>", "action": "append", 'default': [], }), Option( ["-solvent-asl", "-solvent_asl"], None, "Specify ASL to put in solvent leg for protein residue mutation", { "dest": "solvent_asl", }, ), Option( "-vacuum", False, "Include vacuum simulations. Only supported for small molecule FEP.", ), Option( "-extend", "", "Extend production simulations of specified edges.", { "metavar": "<edge-file>", }, ), Option( ["-atom-mapping", "-atom_mapping"], "", "Atom mapping specification for leadoptmap.py. For small molecule FEP, specify " "SMARTS string to customize core assignment; for protein residue mutation FEP, " "'sidechain' is the only argument allowing the side chain atoms to be mapped as " "well while by default the side chains are not mapped. " "This option will be ignored if fmp file is provided as input."), Option( ["-modify-dihe", "-modify_dihe"], False, "Modify retained dihedral angle interactions for customized core."), Option("-h_mass", True, argparse.SUPPRESS), Option(["-no-h-mass", "-no_h_mass"], False, "Turn off hydrogen mass repartitioning (on by default)."), Option( "-membrane", False, "Indicates the model system is a membrane protein system, such as " "the GPCR. If membrane/water components are not provided, the POPC " "bilayer will be added and equilibrated. The protein coordinates " "should be OPM-compatible. If membrane/water components are " "provided, this option is enabled automatically."), Option( "-align_core_only", False, argparse.SUPPRESS # "Do not adjust the non-core atoms when aligning the core atoms." ), Option( "-minimize_volume", False, argparse.SUPPRESS # "Turn on volume minimization during the system_builder or build_geometry stages." ), Option("-core_hopping", False, argparse.SUPPRESS), Option(["-charged-lambda-windows", "-charged_lambda_windows"], 24, "Number of lambda windows for the charge protocol. Default: 24"), Option( ["-core-hopping-lambda-windows", "-core_hopping_lambda_windows"], 16, "Number of lambda windows for the core-hopping protocol. Default: 16" ), Option( "-ats", False, argparse.SUPPRESS, ), # original help: "Automated torsional scaling" Option(["-residue-structure", "-residue_structure"], None, "Noncanonical amino acids for protein mutation.", {"metavar": "<mae-file>"}), Option(["-expand-protein", "-expand_protein"], None, "Pass the structure file for protein fep " "to re-run with additional mutations."), Option( "-water", "SPC", "Specify the water model used in the simulations. Valid values: %(choices)s. Default: %(default)s", { "dest": "water_model", "choices": ['SPC', 'TIP3P', 'TIP4P', 'TIP4PEW', 'TIP4PD', 'TIP5P'], "type": str.upper, }, ), Option( ["-custom-charge-mode", "-custom_charge_mode"], constants.CUSTOM_CHARGE_MODE.ASSIGN, "Set the custom charge calculation mode when using the " f"{mm.OPLS_NAME_F16} forcefield." "Default is to 'assign' custom charges based on the input geometries." "Set to 'clear' to clear custom charges without assigning them." "Set to 'keep' to keep existing custom charge parameters."), Option( "-skip-leg", None, "Specify the legs to skip (complex/solvent/vacuum). Pass in " "multiple times to skip multiple legs", { "metavar": "<leg_name>", 'dest': 'skip_legs', 'action': 'append', 'default': [], }), ]) return options