def _validate_specification(self) -> None: """ Validate the specification this is called before running an optimisation to catch errors before run time. """ from openff.toolkit.typing.engines.smirnoff import get_available_force_fields openff_forcefields = [ ff.split(".offxml")[0].lower() for ff in get_available_force_fields() ] # set up some models ani_methods = {"ani1x", "ani1ccx", "ani2x"} xtb_methods = { "gfn0-xtb", "gfn0xtb", "gfn1-xtb", "gfn1xtb", "gfn2-xtb", "gfn2xtb", "gfn-ff", "gfnff", } rdkit_methods = {"uff", "mmff94", "mmff94s"} gaff_forcefields = { "gaff-1.4", "gaff-1.8", "gaff-1.81", "gaff-2.1", "gaff-2.11", } settings = { "openmm": {"antechamber": gaff_forcefields, "smirnoff": openff_forcefields}, "torchani": {None: ani_methods}, "xtb": {None: xtb_methods}, "rdkit": {None: rdkit_methods}, } # now check these settings # TODO do we raise an error or just change at run time with a warning? if self.program.lower() != "psi4" and self.optimiser == "optking": raise SpecificationError( f"The optimiser optking currently only supports psi4 as the engine." ) # we do not validate QM as there are so many options if self.program.lower() in settings: program_settings = settings[self.program.lower()] allowed_methods = program_settings.get(self.basis, None) if allowed_methods is None: raise SpecificationError( f"The Basis {self.basis} is not supported for the program {self.program} please chose from {program_settings.keys()}" ) # now check the method method = self.method.split(".offxml")[0].lower() if method not in allowed_methods: raise SpecificationError( f"The method {method} is not available for the program {self.program} with basis {self.basis}, please chose from {allowed_methods}" )
def validate_optimiser(cls, optimiser: str) -> str: """ Make sure the chosen optimiser is available. """ procedures = qcng.list_available_procedures() if optimiser.lower() not in procedures: raise SpecificationError( f"The optimiser {optimiser} is not available, available optimisers are {procedures}" ) return optimiser.lower()
def _check_solvent(cls, solvent: str) -> str: """ Make sure that a valid solvent from the list of supported values is passed. """ solvent_formula = cls._solvents.get(solvent.lower(), solvent.upper()) if solvent_formula not in cls._solvents.values(): raise SpecificationError( f"The solvent {solvent} is not supported please chose from the following solvents or formulas {cls._solvents.items()}" ) return solvent_formula
def validate_program(self): """ Validate the choice of program against those supported by QCEngine and QUBEKit. """ programs = qcng.list_available_programs() programs.discard("dftd3") if self.program.lower() not in programs: raise SpecificationError( f"The program {self.program} is not available, available programs are {programs}" )
def _validate_specification(self, qc_spec: "QCOptions") -> None: """ Validate the specification this is called before running an optimisation to catch errors before run time. """ # now check these settings # TODO do we raise an error or just change at run time with a warning? if qc_spec.program.lower() != "psi4" and self.optimiser.lower() == "optking": raise SpecificationError( "The optimiser optking currently only supports psi4 as the engine." )
def _check_forcefield(cls, force_field: str) -> str: """ Make sure the supplied force field is valid. """ openff_forcefields = [ ff.lower() for ff in get_available_force_fields() ] if force_field in openff_forcefields: return force_field.lower() else: raise SpecificationError( f"The force field {force_field} was not found by the openff-toolkit please chosse from {openff_forcefields}." )
def is_available(cls) -> bool: """ The MBIS option is only available via new psi4 so make sure it is installed. """ # check installed psi4 = which_import( "psi4", return_bool=True, raise_error=True, raise_msg="Please install via `conda install psi4 -c psi4`.", ) # now check the version meets the minimum requirement which_psi4 = which("psi4") with popen([which_psi4, "--version"]) as exc: exc["proc"].wait(timeout=30) version = parse_version(safe_version(exc["stdout"].split()[-1])) if version <= parse_version("1.4a1"): raise SpecificationError( f"The version of psi4 installed is {version} and needs to be 1.4 or newer please update it to continue." ) return psi4
def validate_specification(self) -> None: """ Validate the specification this should be called before using the spec to find errors. """ # make sure the program is valid first then the basis method combination self.validate_program() openff_forcefields = [ ff.split(".offxml")[0].lower() for ff in get_available_force_fields() ] # set up some models ani_methods = {"ani1x", "ani1ccx", "ani2x"} xtb_methods = { "gfn0-xtb", "gfn0xtb", "gfn1-xtb", "gfn1xtb", "gfn2-xtb", "gfn2xtb", "gfn-ff", "gfnff", } rdkit_methods = {"uff", "mmff94", "mmff94s"} gaff_forcefields = { "gaff-1.4", "gaff-1.8", "gaff-1.81", "gaff-2.1", "gaff-2.11", } settings = { "openmm": { "antechamber": gaff_forcefields, "smirnoff": openff_forcefields }, "torchani": { None: ani_methods }, "xtb": { None: xtb_methods }, "rdkit": { None: rdkit_methods }, } # now check these settings # TODO do we raise an error or just change at run time with a warning? # we do not validate QM as there are so many options if self.program.lower() in settings: program_settings = settings[self.program.lower()] allowed_methods = program_settings.get(self.basis, None) if allowed_methods is None: raise SpecificationError( f"The Basis {self.basis} is not supported for the program {self.program} please chose from {program_settings.keys()}" ) # now check the method method = self.method.split(".offxml")[0].lower() if method not in allowed_methods: raise SpecificationError( f"The method {method} is not available for the program {self.program} with basis {self.basis}, please chose from {allowed_methods}" ) if self.td_settings is not None: if self.program.lower() not in ["gaussian"]: raise SpecificationError( f"The program {self.program.lower()} does not support time-dependent calculations." )