"""
Conformer structure reader, which extracts blocks of conformers from the given
input based on perception options.
Copyright Schrodinger LLC, All Rights Reserved.
"""
from schrodinger import structure
from schrodinger.application.phase.packages import phase_screen_utils
from schrodinger.application.phase.packages import phase_utils
from schrodinger.application.phase.packages import shape_ligprep
from schrodinger.infra import mm
from schrodinger.infra import phase
CONF_SAMPLE_METHODS = {
phase.CONF_SAMPLE_COARSE_NAME: phase.CONF_SAMPLE_COARSE,
phase.CONF_SAMPLE_FINE_NAME: phase.CONF_SAMPLE_FINE
}
CONF_FORCE_FIELDS = {
mm.OPLS_NAME_F14: phase.CONF_FF_OPLS_2005,
mm.OPLS_NAME_F16: phase.CONF_FF_OPLS3
}
[docs]def add_file_options(parser, create_group=True):
"""
Adds file screening options to the provided parser and returns the
argument group object that holds those options.
:param parser: Argument parser object
:type parser: argparser.ArgumentParser
:param create_group: Create a new argument group and put file
screening options into that group.
:type create_group: bool
:return: Argument group object
:rtype: argparse._ArgumentGroup
"""
if create_group:
file_options = parser.add_argument_group(title="File Screening Options")
else:
file_options = parser
conf_treatment = file_options.add_mutually_exclusive_group(required=False)
conf_treatment.add_argument(
"-distinct",
action="store_true",
help="Treat each structure as a distinct molecule (i.e., one conformer "
"only). By default, consecutive structures with identical titles "
"and connectivity are treated as conformers of a single molecule.")
conf_treatment.add_argument(
"-connect",
action="store_true",
help=
"Consider connectivities only (not titles) when perceiving conformers.")
conf_treatment.add_argument(
"-stereo",
action="store_true",
help="Consider stereochemistry when perceiving conformers. Consecutive "
"structures with the same connectivity will be treated as conformers "
"of a single molecule if and only if they have the same "
"stereochemistry. Titles are ignored.")
file_options.add_argument(
"-title",
metavar="<propname>",
help="Use an alternate property (of string or integer type) "
"as the source of titles for conformer perception.")
return file_options
[docs]def add_confgen_options(parser,
refine,
default_conf_sample_name=phase.CONF_SAMPLE_COARSE_NAME):
"""
Adds conformer generation options to the provided parser and returns the
argument group object that holds those options.
:param parser: Argument parser object
:type parser: argparser.ArgumentParser
:param refine: Flag to add -refine as mutually exclusive option to -flex
:type refine: bool
:param default_conf_sample_name: Name of the conformer sampling method
to be used by default (for help string only).
:type default_conf_sample_name: str
:return: Argument group object
:rtype: argparse._ArgumentGroup
"""
confgen_options = parser.add_argument_group(
title="Conformer Generation Options")
if refine:
flex_refine = confgen_options.add_mutually_exclusive_group(
required=False)
flex_refine.add_argument(
"-flex",
action="store_true",
help="Generate conformers on-the-fly for each input structure. Not "
"valid in combination with -distinct, -connect, -stereo or -refine."
)
flex_refine.add_argument(
"-refine",
action="store_true",
help=
"Generate conformers on-the-fly for the highest scoring match and "
"search for additional matches. May be used in combination with "
"-distinct, -connect or -stereo, but not with -flex. Use of "
"-append has no effect.")
else:
confgen_options.add_argument(
"-flex",
action="store_true",
help="Generate conformers on-the-fly for each input structure. Not "
"valid in combination with -distinct, -connect or -stereo or "
"when input is a Phase Database.")
add_standard_confgen_options(confgen_options, refine,
default_conf_sample_name)
[docs]def add_standard_confgen_options(
argument_group,
refine,
default_conf_sample_name=phase.CONF_SAMPLE_COARSE_NAME):
"""
Adds standard conformer generation options to the provided argument group.
:param argument_group: Argument group object
:type argument_group: argparser._ArgumentGroup
:param refine: Set to True if argument_group supports -refine flag
:type refine: bool
:param default_conf_sample_name: Name of the conformer sampling method
to be used by default (for help string only).
:type default_conf_sample_name: str
"""
argument_group.add_argument(
"-sample",
choices=[phase.CONF_SAMPLE_COARSE_NAME, phase.CONF_SAMPLE_FINE_NAME],
help="Conformational sampling method (default: %s)." %
default_conf_sample_name)
argument_group.add_argument(
"-max",
type=int,
metavar="<numconfs>",
choices=[phase_utils.RestrictedRange(0, None, False)],
help="Maximum number of conformers to generate (default: %d)." %
phase.PHASE_DEFAULT_MAX_CONFS)
argument_group.add_argument(
"-force_field",
choices=[mm.OPLS_NAME_F14, mm.OPLS_NAME_F16],
help="Use a force field to minimize conformers. Increases conformer "
"generation time by approximately a factor of 50. The default is no "
"force field minimization.")
argument_group.add_argument(
"-nddo",
action="store_true",
help="Minimize conformers using NDDO CM1A-BCC charge model. Adds "
"significant time to minimization. Valid only with -force_field "
"%s. The default is off." % mm.OPLS_NAME_F16)
argument_group.add_argument(
"-ewin",
type=float,
metavar="<deltaE>",
choices=[phase_utils.RestrictedRange(0.0, None, False)],
help="Conformer energy window in kJ/mol (default: %.1f)." %
phase.PHASE_DEFAULT_ENERGY_WINDOW)
argument_group.add_argument(
"-append",
action="store_true",
help="Append new conformers to existing conformer(s). The default is "
"to discard existing conformers.")
[docs]def get_confgen_options(args):
"""
Creates conformer generation options from the supplied parser.
:param args: Command line arguments.
:type args: argparse.Namespace
:returned: Conformer generation options
:rtype: PhpConfOptions
"""
confgen_options = phase.PhpConfOptions()
if args.sample is not None:
confgen_options.setSamplingMethod(CONF_SAMPLE_METHODS[args.sample])
if args.max is not None:
confgen_options.setMaxConfs(args.max)
if args.force_field is not None:
confgen_options.setForceField(CONF_FORCE_FIELDS[args.force_field])
if args.nddo:
confgen_options.setForceField(phase.CONF_FF_OPLS3_NDDO)
if args.ewin is not None:
confgen_options.setEnergyWindow(args.ewin)
confgen_options.setAppendConfs(args.append)
return confgen_options
[docs]def validate_confgen_conflicts(args):
"""
Checks options for conflicts between file options and confgen options not
detected by ArgumentParser.parse_args and returns an informative error
message if a conflict is found.
:param args: argparser.Namespace with command line options
:type args: argparser.Namespace
:return: tuple of validity and error message if a conflict is found
:rtype: bool, str
"""
# Catch exception if -refine was not added to the confgen options
try:
flex_or_refine = args.flex or args.refine
flex_or_refine_str = "-flex or -refine"
except AttributeError:
flex_or_refine = args.flex
flex_or_refine_str = "-flex"
if args.flex:
conf_treatment_args = {
"-distinct": args.distinct,
"-connect": args.connect,
"-stereo": args.stereo
}
for key in conf_treatment_args:
if conf_treatment_args[key]:
mesg = "-flex is not allowed with %s" % key
return False, mesg
if not flex_or_refine:
confgen_args = {
"-sample": args.sample,
"-max": args.max,
"-force_field": args.force_field,
"-ewin": args.ewin
}
for key in confgen_args:
if confgen_args[key] is not None:
mesg = "%s is allowed only with %s" % (key, flex_or_refine_str)
return False, mesg
if args.append:
# Technically used only with -flex, so issue a -flex error message.
return False, "-append is allowed only with -flex"
nddo_ok, mesg = validate_confgen_nddo(args)
if not nddo_ok:
return False, mesg
return True, ""
[docs]def validate_confgen_nddo(args):
"""
Checks for illegal use of -nddo flag.
:param args: argparser.Namespace with command line arguments
:type args: argparser.Namespace
:return: tuple of validity and non-empty error message if not valid
:rtype: bool, str
"""
if args.nddo:
expected_ff = mm.OPLS_NAME_F16
if args.force_field != expected_ff:
mesg = "-nddo is allowed only with -force_field %s" % expected_ff
return False, mesg
return True, ""
[docs]def validate_title_option(args):
"""
Checks for illegal values of -title flag.
:param args: argparser.Namespace with command line arguments
:type args: argparser.Namespace
:return: tuple of validity and non-empty error message if not valid
:rtype: bool, str
"""
if args.title:
if not args.title.startswith(('i_', 's_')):
return (False, "-title must refer to a string or integer property "
"(property name starts with s_ or i_)")
return True, ""