Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated for new install process, supporting Conda and Docker #29

Merged
merged 1 commit into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
@@ -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
Expand Down
72 changes: 54 additions & 18 deletions psi4_step/data/psi4.ini
Original file line number Diff line number Diff line change
@@ -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
171 changes: 157 additions & 14 deletions psi4_step/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -112,4 +255,4 @@ def exe_version(self, path):
except Exception:
pass

return version
return "Psi4", version
Loading