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