Source code for schrodinger.application.jaguar.solvation_input
"""
Functions and classes for defining the input to a Solvation workflow.
"""
# Contributors: Mark A. Watson, Leif D. Jacobson, Daniel S. Levine
import copy
import os
from schrodinger.application.jaguar import solvation_keywords
from schrodinger.application.jaguar import solvation_validation as sv
from schrodinger.application.jaguar.workflow_input import WorkflowInput
from schrodinger.structure import StructureReader
[docs]class SolvationInput(WorkflowInput):
    """
    A class to completely specify a Solvation calculation.
    Example usage::
        input = SolvationInput()
        # 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
    # Solvation workflow
    input_file_keys = [solvation_keywords.INPUT_MOLECULE]
    workflow_name = 'Solvation'
[docs]    def __init__(self,
                 inputfile=None,
                 keywords=None,
                 jaguar_keywords=None,
                 jobname=None,
                 add_solvation_jaguar_defaults=False):
        """
        Create a SolvationInput 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 Solvation input file
        :type  keywords: dict
        :param keywords: Solvation 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_solvation_jaguar_defaults: boolean
        :param add_solvation_jaguar_defaults: if True add some custom Jaguar defaults
        """
        self._add_solvation_jaguar_defaults = add_solvation_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 Solvation keywords
        """
        if not solvation_keywords.SOLVATION_KEYWORDS:
            solvation_keywords.generate_all_keywords()
        return copy.deepcopy(solvation_keywords.SOLVATION_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_solvation_jaguar_defaults:
            self._addSolvationJaguarDefaults(input_jaguar_keywords)
    def _addSolvationJaguarDefaults(self, keywords_list):
        """
        Add a few defaults for Jaguar that are specific to Solvation.
        :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:
            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 getInputMolecule(self):
        """
        Return list of input molecules.
        If no file(s) found, return None.
        :rtype: list of Structures (or None)
        :return: reactant Structures
        """
        fh = self.getValue(solvation_keywords.INPUT_MOLECULE)
        # 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 = fh if os.path.exists(fh) else os.path.basename(fh)
        # filter out files that can't be found
        if os.path.exists(fname_basename):
            inputs = list(StructureReader(fname_basename))
        else:
            inputs = None
        return inputs
[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 = "SolvationInput class is corrupted: %s != %s" % (k,
                                                                       kwd.name)
                raise RuntimeError(msg)
            # Do internal SolvationKeyword validation
            kwd.validate()
            # Do some ad hoc self-consistency checks for conflicting keywords
            sv.check_conflicts(kwd, self._keywords)
        # validate structures
        sv.validate_structures(self)
        structs = self.getInputMolecule()
        self.validate_jaguar_keywords(structs)
[docs]    def read(self, inputfile):
        """
        Read an existing Solvation input file.
        Any keywords specified in the input file will override
        existing values in this SolvationInput 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 Solvation input file
        """
        super().read(inputfile)
        if self._add_solvation_jaguar_defaults:
            jaguar_keys = self.getJaguarNonDefault().keys()
            self._addSolvationJaguarDefaults(jaguar_keys)