Source code for schrodinger.application.jaguar.autots_input

"""
Functions and classes for defining the input to a AutoTS workflow.
"""
# Contributors: Mark A. Watson, Leif D. Jacobson, Daniel S. Levine

import copy
import os

import schrodinger.application.jaguar.autots_constants as autots_constants
import schrodinger.application.jaguar.autots_input_constants as constants
from schrodinger.application.jaguar import autots_keywords
from schrodinger.application.jaguar import autots_validation as rv
from schrodinger.application.jaguar import utils as jag_utils  # noqa: F401, mocked in autots_input_test
from schrodinger.application.jaguar import workflow_validation as wv  # noqa: F401
from schrodinger.application.jaguar.autots_bonding import AutoTSStructureReader
from schrodinger.application.jaguar.input import JaguarInput  # noqa: F401, mocked in autots_input_test
from schrodinger.application.jaguar.workflow_input import WorkflowInput
from schrodinger.structure import StructureReader
from schrodinger.structutils.analyze import generate_smiles
from schrodinger.utils import fileutils


[docs]def read_mae_files(fhlist, fileonly, clean_st, reset_bonding=True): """ Process a list of mae files and return extant file paths or associated Structure instances if requested. :type fhlist: list of strings :param fhlist: list of filehandles :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True, cleanup structure file by removing dummy atoms, etc. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :return: Structures or file paths. """ # filter off non-strings fname_strings = [x for x in fhlist if isinstance(x, str)] # if file doesn't exist use a basename # this is a fallback for when running # remotely under jobcontrol and the path is bad fname_basename = [x if os.path.exists(x) \ else os.path.basename(x) for x in fname_strings] # filter out files that can't be found fnames = filter(os.path.exists, fname_basename) structs = [] for fh in fnames: if fileonly: # Append path to .mae file structs.append(fh) else: # Append all structures in this filehandle # if clean_st run through AutoTSStructureReader if clean_st: reader = AutoTSStructureReader(fh, reset_bonding=reset_bonding) else: reader = StructureReader(fh) for st in reader: structs.append(st) return structs
#------------------------------------------------------------------------------
[docs]class AutoTSInput(WorkflowInput): """ A class to completely specify a AutoTS calculation. Example usage:: input = AutoTSInput() # Set user-defined values input.setValue('integerKW', 3) input.setValue('floatKW', '7.0') input.setValue('stringKW', 'foo') # Print all keyword/value pairs. for keyword in input: print "keyword '%s' has value %s" % (keyword, input[keyword]) # Handling the case of trying to set an unsupported keyword try: input['mykeyword'] = 'value' except WorkflowKeywordException as e: print e.allowed_keywords """ # List of keys for all possible input files associated with a given # AutoTS workflow input_file_keys = [ constants.REACTANT, constants.PRODUCT, constants.REACTANT_COMPLEX, constants.PRODUCT_COMPLEX, constants.REFERENCE_REACTANT_COMPLEX, constants.REFERENCE_PRODUCT_COMPLEX, constants.TEMPLATE_DATABASE_FILE, constants.FULL_PATH_FILE, constants.SPECTATORS ] workflow_name = 'AutoTS'
[docs] def __init__(self, inputfile=None, keywords=None, jaguar_keywords=None, jobname=None, add_autots_jaguar_defaults=False): """ Create a AutoTSInput instance. If a keyword is specified in both 'inputfile' and 'keywords', then the values in 'keywords' will be set preferrentially. This also applies to 'jaguar_keywords'. :type inputfile: str :param inputfile: Path to a AutoTS input file :type keywords: dict :param keywords: AutoTS keyword/value pairs :type jaguar_keywords: dict :param jaguar_keywords: Jaguar &gen section keyword/value pairs :type jobname: string :param jobname: Name of job, if it is not None it will be set to the basename of the input file name. :type add_autots_jaguar_defaults: boolean :param add_autots_jaguar_defaults: if True add some custom Jaguar defaults """ self._add_autots_jaguar_defaults = add_autots_jaguar_defaults super().__init__(inputfile=inputfile, keywords=keywords, jaguar_keywords=jaguar_keywords, jobname=jobname) self.setConstants() if inputfile or keywords or jaguar_keywords: self.validate()
@property def reaction_smarts(self): """ Reaction smarts identifying input reaction """ reactants = [generate_smiles(r) for r in self.getReactants()] products = [generate_smiles(p) for p in self.getProducts()] return ".".join(reactants) + ">>" + ".".join(products)
[docs] def generate_keywords(self): """ Initialize dictionary of all possible AutoTS keywords """ if not autots_keywords.AUTOTS_KEYWORDS: autots_keywords.generate_all_keywords() return copy.deepcopy(autots_keywords.AUTOTS_KEYWORDS)
[docs] def setJaguarValues(self, keywords): """ Set multiple Jaguar &gen section keywords. :type keywords: dict of string/anytype pairs :param keywords: Jaguar &gen section keyword/value pairs """ super().setJaguarValues(keywords) input_jaguar_keywords = list(keywords) if self._add_autots_jaguar_defaults: self._addAutoTSJaguarDefaults(input_jaguar_keywords)
def _addAutoTSJaguarDefaults(self, keywords_list): """ Add a few defaults for Jaguar that are specific to AutoTS. :type keywords_list: list :param keywords_list: List of jaguar keywords that have already been set. No specialized non-defaults will be set for any of these keywords. """ # set some extra defaults unless the user has specified otherwise if 'isymm' not in keywords_list: self._jaguarinput.setValue('isymm', 0) if 'maxit' not in keywords_list: self._jaguarinput.setValue('maxit', 200) if 'nogas' not in keywords_list and 'isolv' in keywords_list: self._jaguarinput.setValue('nogas', 2) is_single_diffuse = self._jaguarinput.getValue('basis').count('+') == 1 if is_single_diffuse and 'iusediffuse' not in keywords_list: self._jaguarinput.setValue('iusediffuse', 1)
[docs] def updateJaguarUserKeywords(self, keywords): """ Update the Jaguar &gen section keywords of the user-defined settings only if previous settings exist. :type keywords: dict of string/anytype pairs :param keywords: Jaguar &gen section keyword/value pairs """ # This dict is empty unless a .in file has been read or AutoTSInput # was instantiated with a non-empty jaguar_keywords argument. if self._jaguar_user_keys: self._jaguar_user_keys.update(keywords)
[docs] def getReactants(self, fileonly=False, clean_st=False, reset_bonding=True): """ Return list of reactants. If no file(s) found, return empty list. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :type return: list :return: reactant Structures or file paths. """ fhlist = self.getValue(constants.REACTANT) reactants = read_mae_files(fhlist, fileonly, clean_st, reset_bonding) return reactants
[docs] def getProducts(self, fileonly=False, clean_st=False, reset_bonding=True): """ Return list of products. If no file(s) found, return empty list. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :type return: list :return: product Structures or file paths. """ fhlist = self.getValue(constants.PRODUCT) products = read_mae_files(fhlist, fileonly, clean_st, reset_bonding) return products
[docs] def getReactantComplex(self, fileonly=False, clean_st=False, reset_bonding=True): """ Return Reactant Complex. If no file found, return NoneType. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :return: Reactant Complex Structures or file path. """ fh = self.getValue(constants.REACTANT_COMPLEX) rcomplex = read_mae_files([fh], fileonly, clean_st, reset_bonding) if rcomplex: return rcomplex[0] else: return None
[docs] def getProductComplex(self, fileonly=False, clean_st=False, reset_bonding=True): """ Return Product Complex. If no file found, return NoneType. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :return: Product Complex Structures or file path. """ fh = self.getValue(constants.PRODUCT_COMPLEX) pcomplex = read_mae_files([fh], fileonly, clean_st, reset_bonding) if pcomplex: return pcomplex[0] else: return None
[docs] def getReferenceReactantComplex(self, fileonly=False, clean_st=False, reset_bonding=True): """ Return Reference Reactant Complex. If no file found, return NoneType. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :return: Reactant Complex Structures or file path. """ fh = self.getValue(constants.REFERENCE_REACTANT_COMPLEX) rcomplex = read_mae_files([fh], fileonly, clean_st, reset_bonding) if rcomplex: return rcomplex[0] else: return None
[docs] def getReferenceProductComplex(self, fileonly=False, clean_st=False, reset_bonding=True): """ Return Reference Product Complex. If no file found, return NoneType. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :type reset_bonding: bool :param reset_bonding: if True and clean_st is True, redefine bonding using mmjag. :return: Product Complex Structures or file path. """ fh = self.getValue(constants.REFERENCE_PRODUCT_COMPLEX) pcomplex = read_mae_files([fh], fileonly, clean_st, reset_bonding) if pcomplex: return pcomplex[0] else: return None
[docs] def getSpectators(self, fileonly=False, clean_st=False): """ Return spectator. If no file found, return NoneType. :type fileonly: bool :param fileonly: if True, return only file paths, else Structures. :type clean_st: bool :param clean_st: if True redefine bonding using mmlewis. :return: Reactant Complex Structures or file path. """ fh = self.getValue(constants.SPECTATORS) specs = read_mae_files(fh, fileonly, clean_st) if specs: return specs else: return None
[docs] def validate(self): """ Perform a self-consistency check of all currently set keywords. :raise WorkflowKeywordConflictError if conflicting values found :raise WorkflowConservationError if matter not conserved """ for k, kwd in self._keywords.items(): # The keys to self._keywords are keyword names so should be kept # in sync else confusion could arise. if k != kwd.name: msg = "AutoTSInput class is corrupted: %s != %s" % (k, kwd.name) raise RuntimeError(msg) # Do internal AutoTSKeyword validation kwd.validate() # Do some ad hoc self-consistency checks for conflicting keywords rv.check_conflicts(kwd, self._keywords) # validate structures rv.validate_structures(self) strs = self.getReactants() + self.getProducts() self.validate_jaguar_keywords(strs) rv.validate_constraints(self)
[docs] def validate_jaguar_keywords(self, strs): """ Perform a check to ensure that Jaguar keywords are not set that AutoTS cannot handle. """ jaguar_keys = self.getJaguarNonDefault() rv.validate_jag_keys(jaguar_keys) super().validate_jaguar_keywords(strs)
[docs] def setConstants(self): """ Set any constants defined by keywords """ autots_constants.FRACTION_DIFFERENT = self.getValue( "bond_stretch_fraction")
[docs] def read(self, inputfile): """ Read an existing AutoTS input file. Any keywords specified in the input file will override existing values in this AutoTSInput instance. Jaguar &gen section keywords are defined like:: &JaguarKeywords key=val key=val ... & Constraints can be defined with:: &Constraints st_title atom_index1 atom_index2... value & :type inputfile: str :param inputfile: Path to a AutoTS input file """ super().read(inputfile) if self._add_autots_jaguar_defaults: jaguar_keys = self.getJaguarNonDefault().keys() self._addAutoTSJaguarDefaults(jaguar_keys)
[docs] def set_default_template_file(self): """ If the default template file is not set and the keyword use_default_templates is True and that file exists, set the template file to the default one. """ if not self.getValue(constants.TEMPLATE_DATABASE_FILE ) and self.getValue("use_default_templates"): template_file = users_default_template_filename() if os.path.exists(template_file): self.setValue(constants.TEMPLATE_DATABASE_FILE, template_file)
[docs]def users_default_template_filename(): """ Returns the name of the users template file that *should* be found in .schrodinger/autots_templates. The file may or may not exist. """ return os.path.join(fileutils.get_directory_path(fileutils.USERDATA), "autots_templates", "autots_templates.mae")