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 schrodinger import structure
from schrodinger.application.desmond import constants
from schrodinger.application.desmond.util import ensure_file_exists
from schrodinger.application.desmond.util import fatal
from schrodinger.infra import mm
from schrodinger.utils import fileutils

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>
"""


[docs]class FepPlusArgs(cmdline.BaseArgs):
[docs] def __init__(self, opt: argparse.Namespace, argv=[]): # noqa: M511 """ :param opt: Command line options with corresponding values. :param inp_file: Input filename """ self.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 = opt.time_vacuum or self.time self.h_mass = not opt.no_h_mass if self.skip_legs: self.skip_legs = [s.lower() for s in self.skip_legs] self.argv = argv # Original list of arguments provided by user. # NOTE: argv does not have jobcontrol's parameters (JOBNAME, HOST, ...) if self.ats: # Automated torsional scaling requires use of modify dihedral self.modify_dihe = True super(FepPlusArgs, self).__init__(opt)
[docs] def set_restart(self): """ Set the RESTART flag if only the checkpoint is given. """ if self.inp_file is None and self.checkpoint and not self.extend: self.RESTART = True
[docs] def validate(self): if not (self.DEBUG or self.extend) and (self.time_complex < 500 or self.time_solvent < 500 or self.time_vacuum < 500): fatal('ERROR: Simulation time must be >= 500 ps.') if not (self.inp_file or self.checkpoint) and not self.extend: # Do we need either an input file or a checkpoint file # for restarting too? fatal('ERROR: Must provide <pv-or-fmp-file> for running a new job.') ensure_file_exists(self.inp_file) ensure_file_exists(self.extend) 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.inp_file: if (not self.inp_file.endswith('.fmp') and fileutils.is_poseviewer_file(self.inp_file) and not fileutils.is_maestro_file(self.inp_file)): sys.exit("ERROR: Input must be an PV, FMP, or Maestro format.") 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 g.fep_type == constants.FEP_TYPES.ABSOLUTE_BINDING: sys.exit("ERROR: Cannot use an Absolute Binding fmp as " "input to FEP+") 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 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 self.check_ppj() super().validate()
[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([ 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", 5000.0, "Specify the production-simulation time (in ps). For extension, this option " "specifies the additional simulation time (in ps). Default: 5000.0. " "Min value: 500.0."), Option("-time-complex", None, argparse.SUPPRESS, {"type": float}), Option("-time-solvent", None, argparse.SUPPRESS, {"type": float}), Option("-time-vacuum", None, argparse.SUPPRESS, {"type": float}), 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", [], "Generate and run metalloprotein workflow.", { "metavar": "<property_name>", 'nargs': '?' }), 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", [], "Specify the legs to skip (complex/solvent/vacuum). Pass in " "multiple times to skip multiple legs", { "metavar": "<property_name>", 'nargs': '?', 'dest': 'skip_legs', }), ]) return options