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

"""
FEP+ solubility command line UI

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

from schrodinger.application.desmond.constants import CUSTOM_CHARGE_MODE
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 import stage
from schrodinger.application.desmond.starter.ui.cmdline import get_sim_time_message
from schrodinger.application.desmond import util
from schrodinger.infra import mm
from schrodinger.structure import StructureReader

from . import cmdline
from .cmdline import Option

_ERROR_CHARGED_LIGAND = 'ERROR: Charged ligands are not supported in solubility calculations. Please check the %d charged ligand(s): %s.'
_ERROR_ZERO_EXTEND_TIME = "ERROR: Time of at least one leg must be non-zero. Reset one of -hydration_time or -sublimation_time or reset -solvation_time for -solvation-only workflow."
_ERROR_NOT_ENOUGH_MOLECULES = ("ERROR: -dsb_n_molecules can not be less than "
                               f"{stage.DisorderedSystemBuilder.N_MOLECULES}.")
_ERROR_INCORRECT_COMPOSITION = "ERROR: -solvent-composition must be in the format X:Y:Z where X, Y, Z are integers specifying "
" the number of each solvent molecule matching the order in the input -solvent-template-structure file."
_ERROR_MISSING_TEMPLATE = "ERROR: -solvent-template-structure must be specified for the -solvation-only workflow."
_ERROR_MISSING_COMPOSITION = "ERROR: -solvent-composition must specified for the -solvation-only workflow."
_WARNING_SOLVATION_EXTEND_FLAGS = "WARNING: solvent options should not be specified for extending or restarting the -solvation-only workflow and will be ignored."

_MIN_ATOMS = 20
_WARNING_TOO_SMALL = 'WARNING: One or more of the input structures is smaller than 20 atoms.\n' + \
                     'If the simulation exits with an error "The simulation box is too small."\n' + \
                     'please run with -prepare and modify %s_md.msj to include\n' + \
                     'disordered_system_builder { \n molecules = 128 \n forcefield = %s \n } \n'


[docs]class FepSolubilityArgs(cmdline.FepArgs): PROGRAM_NAME = "FEP Solubility" SUPPORTED_FEP_TYPES = [FEP_TYPES.SOLUBILITY]
[docs] def copy_parser_attributes(self, opt: argparse.Namespace): super().copy_parser_attributes(opt) self.time = opt.time self.hydration_fep_sim_time = opt.hydration_time self.sublimation_fep_sim_time = opt.sublimation_time self.solvation_fep_sim_time = opt.solvation_time self.h_mass = opt.h_mass self.custom_charge_mode = opt.custom_charge_mode self.dsb_n_molecules = opt.dsb_n_molecules
[docs] def validate(self): """ Validate the parameters for missing files. :raise SystemExit: For invalid parameters. """ super().validate() from schrodinger.application.scisol.packages.fep import graph from schrodinger.application.scisol.packages.fep import utils as \ fep_utils self._validate_sim_times([ self.time, self.hydration_fep_sim_time, self.sublimation_fep_sim_time, self.solvation_fep_sim_time ]) # Check the hydration/sublimation or solvation simulation times are specified if self.mode == UiMode.EXTEND: if self.solvation_only: if not self.solvation_fep_sim_time: sys.exit(_ERROR_ZERO_EXTEND_TIME) elif self.hydration_only: if not self.hydration_fep_sim_time: sys.exit(_ERROR_ZERO_EXTEND_TIME) else: if not (self.hydration_fep_sim_time or self.sublimation_fep_sim_time): sys.exit(_ERROR_ZERO_EXTEND_TIME) if self.mode == UiMode.NEW: if self.inp_file.endswith("fmp"): g = graph.Graph.deserialize(self.inp_file) cts = [node.struc for node in fep_utils.get_ligand_nodes(g)] else: cts = list(StructureReader(self.inp_file)) charged_titles = set() for ct in cts: if ct.atom_total < _MIN_ATOMS: jobname = self.JOBNAME if self.JOBNAME else '*' print(_WARNING_TOO_SMALL % (jobname, self.forcefield)) if ct.formal_charge != 0: charged_titles.add(ct.title) if charged_titles: sys.exit(_ERROR_CHARGED_LIGAND % (len(charged_titles), ','.join(charged_titles))) self.check_duplicate_titles(cts) # check the number of molecules for disordered system builder if self.dsb_n_molecules < stage.DisorderedSystemBuilder.N_MOLECULES: sys.exit(_ERROR_NOT_ENOUGH_MOLECULES) if self.solvation_only: if self.mode == UiMode.NEW: if self.solvent_template_structure: util.ensure_file_exists(self.solvent_template_structure) if not self.solvent_composition: sys.exit(_ERROR_MISSING_COMPOSITION) try: # 1 is for the solute self.solvent_composition = [ int(v) for v in self.solvent_composition.split(':') ] + [1] except ValueError: sys.exit(_ERROR_INCORRECT_COMPOSITION) else: sys.exit(_ERROR_MISSING_TEMPLATE) elif self.solvent_template_structure: print(_WARNING_SOLVATION_EXTEND_FLAGS)
[docs] def get_time_for_leg(self, leg_type: str) -> Optional[float]: if leg_type == FepLegTypes.HYDRATION: return self.hydration_fep_sim_time elif leg_type == FepLegTypes.SUBLIMATION: return self.sublimation_fep_sim_time elif leg_type == FepLegTypes.SOLVATION: return self.solvation_fep_sim_time
[docs]def ui(argv: List[str]) -> FepSolubilityArgs: """ Parse the arguments and return an object containing the values. :param argv: List of command line arguments :return: Parsed command line options """ usage = """ * Run a new job: $SCHRODINGER/fep_solubility -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <ligands.mae> * Restart a previously interrupted job: $SCHRODINGER/fep_solubility -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> -RESTART -checkpoint <multisim-checkpoint-file> * Prepare input files for multisim. Do NOT run job: $SCHRODINGER/fep_solubility -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> <ligands.mae> -prepare * Extend production simulations for certain ligands: $SCHRODINGER/fep_solubility -HOST <main-host> -SUBHOST <subhost> -JOBNAME <jobname> -extend <ligand-file> -checkpoint <multisim-checkpoint-file> -hydration-time 5000.0 -sublimation-time 10000.0 An example for the format of an edge-file: ligand1_title ligand2_title ligand3_title """ options = cmdline.get_common_options() options.extend(cmdline.get_common_fep_options()) options.extend([ # name default help [dest] Option("inp_file", None, "A Maestro structure file", {"nargs": "?"}), Option( "-time", 5000.0, "Specify the production-simulation time (in ps) for the Molecular Dynamics stage." " Default: 5000.0. Min value: 500.0", {"metavar": "<sim-time>"}), Option(["-hydration-time", "-hydration_time"], 5000.0, get_sim_time_message(default_time=5000.0, leg_type="hydration"), {"metavar": "<sim-time>"}), Option(["-solvation-time", "-solvation_time"], 10000.0, get_sim_time_message(default_time=10000.0, leg_type="solvation"), {"metavar": "<sim-time>"}), Option(["-sublimation-time", "-sublimation_time"], 10000.0, get_sim_time_message(default_time=10000.0, leg_type="sublimation"), {"metavar": "<sim-time>"}), Option(["-hydration-only", "-hydration_only"], False, "Only run hydration fep."), Option("-solvation-only", False, "Only run solvation fep."), Option( "-solvent-template-structure", None, "Template structure for solvent(s). " "Only used in the solvation only workflow."), Option( "-solvent-composition", "", "Composition of the solvent in the format " "X:Y:Z where X, Y, Z are integers specifying " "the number of each solvent molecule matching the order in the " "input -solvent-template-structure file. If there is only one " "structure, specify a single integer X. " "Only used in the solvation only workflow."), Option("-crystal-structure", False, "Treat input structure as crystal structure (off by default)."), Option(["-h-mass", "-h_mass"], False, "Turn on hydrogen mass repartitioning (off by default)."), Option( "-no_h_mass", True, "Turn off hydrogen mass repartitioning (off by default). " "NOTE: This option is deprecated and ignored."), Option( ["-custom-charge-mode", "-custom_charge_mode"], 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( "-extend", "", "Extend production simulations of specified ligands.", { "metavar": "<ligand-file>", }, ), Option( ["-dsb-n-molecules", "-dsb_n_molecules"], stage.DisorderedSystemBuilder.N_MOLECULES, argparse.SUPPRESS, ), ]) cmdline.suppress_options(options, {"-salt"}) args = cmdline.parse_options(usage, options, argv[1:]) return FepSolubilityArgs(args)