Source code for schrodinger.application.jaguar.hydrokinetic_input

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

import copy
import os
import time

import schrodinger.application.jaguar.hydrokinetic_input_constants as constants
from schrodinger.application.jaguar import hydrokinetic_validation as dv
from schrodinger.application.jaguar import utils as jag_utils
from schrodinger.application.jaguar import hydrokinetic_keywords
from schrodinger.application.jaguar.autots_input import AutoTSInput, read_mae_files
from schrodinger.application.jaguar.exceptions import JaguarUserFacingException
from schrodinger.application.jaguar.workflow_input import WorkflowInput
from schrodinger.utils import fileutils

# List of keys for all possible input files associated with a given
# Hydrokinetic workflow

INPUT_FILE_KEYS = [
    constants.INPUT_MOLECULE,
]
HYDROKINETIC_TEMPLATE_FILE = 'hydrokinetic_template.in'


[docs]class HydrokineticInput(WorkflowInput): """ A class to completely specify a Hydrokinetic calculation. Example usage: input = HydrokineticInput() input.setValue('integerKW', 3) input.setValue('floatKW', '7.0') input.setValue('stringKW', 'foo') for keyword in input: print "keyword '%s' has value %s" % (keyword, input[keyword]) try: input['mykeyword'] = 'value' except WorkflowKeywordException as e: print e.allowed_keywords """
[docs] def __init__(self, inputfile=None, keywords=None, jaguar_keywords=None, jobname=None, add_hydrokinetic_jaguar_defaults=False): """ Create a HydrokineticInput 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 Hydrokinetic input file :type keywords: dict :param keywords: Hydrokinetic 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_hydrokinetic_jaguar_defaults: boolean :param add_hydrokinetic_jaguar_defaults: if True add some custom Jaguar defaults """ self._add_hydrokinetic_jaguar_defaults = add_hydrokinetic_jaguar_defaults super().__init__(inputfile=inputfile, keywords=keywords, jaguar_keywords=jaguar_keywords, jobname=jobname) if inputfile or keywords or jaguar_keywords: self.validate()
[docs] def generate_keywords(self): """ Initialize dictionary of all possible Hydrokinetic keywords """ if not hydrokinetic_keywords.HYDROKINETIC_KEYWORDS: hydrokinetic_keywords.generate_all_keywords() return copy.deepcopy(hydrokinetic_keywords.HYDROKINETIC_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_hydrokinetic_jaguar_defaults: self._addHydrokineticJaguarDefaults(input_jaguar_keywords)
def _addHydrokineticJaguarDefaults(self, keywords_list): """ Add a few defaults for Jaguar that are specific to Hydrokinetic. :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) if 'mulken' not in keywords_list: self._jaguarinput.setValue('mulken', 1) if 'props_each_step' not in keywords_list: self._jaguarinput.setValue('props_each_step', 1) 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 getInputMolecule(self, fileonly=False, clean_st=False): """ Return list of input molecules. 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 return: list :return: reactant Structures or file paths. """ fh = self.getValue(constants.INPUT_MOLECULE) reactants = read_mae_files([fh], fileonly, clean_st, reset_bonding=False) if reactants: return reactants[0] else: raise JaguarUserFacingException( f'Failed to specify a valid {constants.INPUT_MOLECULE}.')
[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 :raise JaguarUnsupportedBasisSet: if basis is not supported """ 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 = "HydrokineticInput class is corrupted: %s != %s" % ( k, kwd.name) raise RuntimeError(msg) # Do internal HydrokineticKeyword validation kwd.validate() # Do some ad hoc self-consistency checks for conflicting keywords dv.check_conflicts(kwd, self._keywords) # validate structures dv.validate_structures(self) strs = [self.getInputMolecule()] self.validate_jaguar_keywords(strs)
[docs] def validate_jaguar_keywords(self, strs): """ Perform a check to ensure that Jaguar keywords are not set that Hydrokinetic cannot handle. """ jaguar_keys = self.getJaguarNonDefault() super().validate_jaguar_keywords(strs)
[docs] def setJobname(self, jobname): """ Set the attribute jobname. :type jobname: string :param jobname: input name of job. If jobname is None we try to use self._inputfile. If that is also None we assign a unique name. """ if jobname is not None: self.jobname = jobname elif self._inputfile is not None: name, ext = os.path.splitext(self._inputfile) self.jobname = os.path.basename(name) else: # assign randomly based on the script Hydrokinetic and a time stamp stamp = str(time.time()) self.jobname = jag_utils.get_jobname("Hydrokinetic", stamp)
[docs] def read(self, inputfile): """ Read an existing Hydrokinetic input file. Any keywords specified in the input file will override existing values in this HydrokineticInput 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 Hydrokinetic input file """ super().read(inputfile) if self._add_hydrokinetic_jaguar_defaults: jaguar_keys = self.getJaguarNonDefault().keys() self._addHydrokineticJaguarDefaults(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 remove_input_file_paths(self): """ Remove full paths from file specifications. A new input file is no longer written, if that is desired the user must call the save method. """ # Update internal state of this class for key in INPUT_FILE_KEYS: filepath = self.getValue(key) if isinstance(filepath, list): basename = [os.path.basename(x) for x in filepath] else: basename = os.path.basename(filepath) self.setValue(key, basename)
[docs] def update_input_file_paths(self): """ Update full paths for file specifications to reflect the CWD. This is useful if running this job in a subdirectory or on a remote host. """ def _prepend_cwd(cwd, filename): if os.path.exists(filename): return os.path.join(cwd, filename) else: raise IOError("File does not exist: %s" % filename) # remove old paths self.remove_input_file_paths() cwd = os.getcwd() # Update internal state of this class for key in INPUT_FILE_KEYS: filepath = self.getValue(key) if filepath: #only act on non-empty lists/strings if isinstance(filepath, list): basename = [_prepend_cwd(cwd, x) for x in filepath] else: basename = _prepend_cwd(cwd, filepath) self.setValue(key, basename)
[docs] def get_input_files(self): """ Return set of expected input files. """ infiles = super().get_input_files() # Auxiliary files for key in INPUT_FILE_KEYS: filepath = self.getValue(key) if isinstance(filepath, list): infiles.update(filepath) elif filepath: infiles.add(filepath) return infiles
[docs]def users_default_template_filename(): """ Returns the name of the users template file that *should* be found in .schrodinger/hydrokinetic_templates. The file may or may not exist. """ return os.path.join(fileutils.get_directory_path(fileutils.USERDATA), "hydrokinetic_templates", "hydrokinetic_templates.mae")
[docs]def create_autots_template_file(hinp, autots_kwds, fname=HYDROKINETIC_TEMPLATE_FILE): """ Create AutoTS template file for use in Hydrokinetic from HydrokineticInput. :type hinp: HydrokineticInput :param hinp: HydrokineticInput instance containing keywords for AutoTS :type autots_kwds: dict :param autots_kwds: AutoTS workflow keywords to use :type fname: str :param fname: filename to use for AutoTS template file """ jaguar_keywords = hinp.getJaguarNonDefault() autots_template = AutoTSInput() autots_template.setValues(autots_kwds) autots_template.setJaguarValues(jaguar_keywords) autots_template.save(fname) return fname