diff --git a/HISTORY.rst b/HISTORY.rst index 3e35e8f..bbe0dbb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,10 @@ ======= History ======= +2024.3.17 -- Updated the installer + * Updated the installer to use the new version of the SEAMM installer. + * Finalizes installing either with Conda or Docker + 2024.3.4 -- Allowing short names for method and DFT functionals * Added short names for the methods (Hamiltonians) and DFT functionals. * Catch errors in Psi4 calculating properties for e.g. CISD(T) method diff --git a/psi4_step/data/psi4.ini b/psi4_step/data/psi4.ini index 66efe52..c659dee 100644 --- a/psi4_step/data/psi4.ini +++ b/psi4_step/data/psi4.ini @@ -1,35 +1,71 @@ # Configuration options for how to run Psi4 -[local] -# The code to use. This may maybe more than just the name of the code, and variables in braces -# {} will be expanded. For example: +[docker] +# The code to use. This may maybe more than just the name of the code, and variables in +# braces {} will be expanded. For example: # code = mpiexec -np {NTASKS} lmp_mpi # would expand {NTASKS} to the number of tasks and run the command + code = psi4 -n {NTASKS} +# The name and location of the Docker container to use, optionally with the version + +container = ghcr.io/molssi-seamm/seamm-mopac:{version} + +# In addition, you can specify the platform to use. This is useful on e.g. Macs with +# app silicon (M1, M3...) where the default platform is linux/arm64 but some containers +# are only available for linux/amd64. + +platform = linux/amd64 + +[local] # The type of local installation to use. Options are: -# conda: Use a conda environment +# conda: Use a conda environment # modules: Use the modules system -# local: Use a local installation +# local: Use a local installation +# docker: Use a Docker container # By default SEAMM installs Psi4 using conda. + installation = conda -# The Conda environment to use. This is the name or full path of the environment to use. +# The command line to use, which should start with the executable followed by any options. +# Variables in braces {} will be expanded. For example: +# +# code = mpiexec -np {NTASKS} lmp_mpi +# +# would expand {NTASKS} to the number of tasks and run the command. +# For a 'local' installation, the command line should include the full path to the +# executable or it should be in the path. + +code = psi4 -n {NTASKS} + +######################### conda section ############################ +# The full path to the conda executable: + +# conda = + +# The Conda environment to use. This is either the name or full path. + conda-environment = seamm-psi4 -# The modules to load to run Psi4. This is a list of strings, each of which -# is a module to load. For example, to load the modules psi4 and openmpi, -# you would use: +######################### modules section ############################ +# The modules to load to run Psi4, as a list of strings. +# For example, to load the modules psi4 and openmpi, you would use: # modules = psi4 openmpi + # modules = -[docker] -# The code to use. This may maybe more than just the name of the code, and variables in braces -# {} will be expanded. For example: -# code = mpiexec -np {NTASKS} lmp_mpi -# would expand {NTASKS} to the number of tasks and run the command -code = psi4 -n {NTASKS} +######################### local section ############################ +# The full path to the Psi4 executable should be in the 'code' option. -# The name and location of the Docker container to use, optionally with the version -container = ghcr.io/molssi-seamm/seamm-psi4:{version} -platform = linux/amd64 +######################### docker section ############################ +# The name and location of the Docker container to use, optionally with the version. +# {version} will be expanded to the version of the plug-in. + +# container = ghcr.io/molssi-seamm/seamm-mopac:{version} + +# In addition, you can specify the platform to use. This is useful on e.g. Macs with +# app silicon (M1, M3...) where the default platform is linux/arm64 but some containers +# are only available for linux/amd64. + +# platform = linux/amd64 diff --git a/psi4_step/installer.py b/psi4_step/installer.py index ad36a38..147b27b 100644 --- a/psi4_step/installer.py +++ b/psi4_step/installer.py @@ -57,42 +57,185 @@ def __init__(self, logger=logger): logger.debug("Initializing the Psi4 installer object.") self.section = "psi4-step" - self.path_name = "psi4-path" self.executables = ["psi4"] self.resource_path = Path(pkg_resources.resource_filename(__name__, "data/")) + + # The environment.yaml file for Conda installations. + logger.debug(f"data directory: {self.resource_path}") + self.environment_file = self.resource_path / "seamm-psi4.yml" + + def check(self): + """Check the status of the Psi4 installation.""" + print("Checking the Psi4 installation.") + # What Conda environment is the default? - data = self.configuration.get_values(self.section) + path = self.configuration.path.parent / "psi4.ini" + if not path.exists(): + text = (self.resource_path / "psi4.ini").read_text() + path.write_text(text) + print(f" The psi4.ini file did not exist. Created {path}") + + self.exe_config.path = path + + # Get the current values + data = self.exe_config.get_values("local") + if "conda-environment" in data and data["conda-environment"] != "": self.environment = data["conda-environment"] else: self.environment = "seamm-psi4" - # The environment.yaml file for Conda installations. - path = Path(pkg_resources.resource_filename(__name__, "data/")) - logger.debug(f"data directory: {path}") - self.environment_file = path / "seamm-psi4.yml" + super().check() + + def install(self): + """Install Psi4 in a conda environment.""" + print("Installing Psi4.") + + # What Conda environment is the default? + path = self.configuration.path.parent / "psi4.ini" + if not path.exists(): + text = (self.resource_path / "psi4.ini").read_text() + path.write_text(text) + print(f" The psi4.ini file did not exist. Created {path}") + + self.exe_config.path = path + + # Get the current values + data = self.exe_config.get_values("local") + + if "conda-environment" in data and data["conda-environment"] != "": + self.environment = data["conda-environment"] + else: + self.environment = "seamm-psi4" + + super().install() + + def show(self): + """Show the status of the Psi4 installation.""" + print("Showing the Psi4 installation.") + + # What Conda environment is the default? + path = self.configuration.path.parent / "psi4.ini" + if not path.exists(): + text = (self.resource_path / "psi4.ini").read_text() + path.write_text(text) + print(f" The psi4.ini file does not exist at {path}") + print(" The 'check' command will create it if Psi4 is installed.") + print(" Otherwise 'install' will install Psi4.") + return + + self.exe_config.path = path - def exe_version(self, path): + if not self.exe_config.section_exists("local"): + print( + " Psi4 is not configured: there is no 'local' section in " + f" {path}." + ) + return + + # Get the current values + data = self.exe_config.get_values("local") + + if "conda-environment" in data and data["conda-environment"] != "": + self.environment = data["conda-environment"] + else: + self.environment = "seamm-psi4" + + super().show() + + def uninstall(self): + """Uninstall the Psi4 installation.""" + print("Uninstall the Psi4 installation.") + + # What Conda environment is the default? + path = self.configuration.path.parent / "psi4.ini" + if not path.exists(): + text = (self.resource_path / "psi4.ini").read_text() + path.write_text(text) + print( + f"""" The psi4.ini file does not exist at {path} + Perhaps Psi4 is not installed, but if it is the 'check' command may locate it + and create the ini file, after which 'uninstall' will remove it.""" + ) + return + + self.exe_config.path = path + + if not self.exe_config.section_exists("local"): + print( + f"""" The psi4.ini file at {path} does not have local section. + Perhaps Psi4 not installed, but if it is the 'check' command may locate it + and update the ini file, after which 'uninstall' will remove it.""" + ) + return + + # Get the current values + data = self.exe_config.get_values("local") + + if "conda-environment" in data and data["conda-environment"] != "": + self.environment = data["conda-environment"] + else: + self.environment = "seamm-psi4" + + super().uninstall() + + def update(self): + """Updates the Psi4 installation.""" + print("Updating the Psi4 installation.") + + # What Conda environment is the default? + path = self.configuration.path.parent / "psi4.ini" + if not path.exists(): + text = (self.resource_path / "psi4.ini").read_text() + path.write_text(text) + print(f" The psi4.ini file did not exist. Created {path}") + + self.exe_config.path = path + + # Get the current values + data = self.exe_config.get_values("local") + + if "conda-environment" in data and data["conda-environment"] != "": + self.environment = data["conda-environment"] + else: + self.environment = "seamm-psi4" + + super().update() + + def exe_version(self, config): """Get the version of the Psi4 executable. Parameters ---------- - path : pathlib.Path - Path to the executable. + config : dict + Dictionary of configuration parameters from psi4.ini Returns ------- - str - The version reported by the executable, or 'unknown'. + str, str + "Psi4" and the version reported by the executable, or 'unknown'. """ + environment = config["conda-environment"] + conda = config["conda"] + if environment[0] == "~": + environment = str(Path(environment).expanduser()) + command = f"'{conda}' run --live-stream -p '{environment}' psi4 --version" + elif Path(environment).is_absolute(): + command = f"'{conda}' run --live-stream -p '{environment}' psi4 --version" + else: + command = f"'{conda}' run --live-stream -n '{environment}' psi4 --version" + + logger.debug(f" Running {command}") try: result = subprocess.run( - [str(path), "--version"], + command, stdin=subprocess.DEVNULL, capture_output=True, text=True, + shell=True, ) - except Exception: + except Exception as e: + logger.debug(f" Failed to run {command}: {e}") version = "unknown" else: version = "unknown" @@ -112,4 +255,4 @@ def exe_version(self, path): except Exception: pass - return version + return "Psi4", version