Skip to content

Commit

Permalink
overhaul psi4 detection parsing (#418)
Browse files Browse the repository at this point in the history
* overhaul psi4 detection parsing

* update channels

* fix bugs and check returncode

* account for pre-ddd

* rule out null case
  • Loading branch information
loriab authored Aug 14, 2023
1 parent eb3366f commit 6db2494
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 16 deletions.
2 changes: 1 addition & 1 deletion qcengine/procedures/optking.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def found(self, raise_error: bool = False) -> bool:
"optking",
return_bool=True,
raise_error=raise_error,
raise_msg="Please install via `conda install optking -c psi4/label/dev`.",
raise_msg="Please install via `conda install optking -c conda-forge`.",
)

def build_input_model(self, data: Union[Dict[str, Any], "OptimizationInput"]) -> "OptimizationInput":
Expand Down
4 changes: 2 additions & 2 deletions qcengine/programs/adcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ def found(raise_error: bool = False) -> bool:
"adcc",
return_bool=True,
raise_error=raise_error,
raise_msg="Please install via `conda install adcc -c adcc`.",
raise_msg="Please install via `conda install adcc -c conda-forge`.",
)
found_psi4 = which_import(
"psi4",
return_bool=True,
raise_error=raise_error,
raise_msg="Please install psi4 for adcc harness via `conda install psi4 -c psi4`.",
raise_msg="Please install psi4 for adcc harness via `conda install psi4 -c conda-forge/label/libint_dev -c conda-forge`.",
)
return found_adcc and found_psi4

Expand Down
43 changes: 30 additions & 13 deletions qcengine/programs/psi4.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,31 +54,46 @@ def found(raise_error: bool = False) -> bool:
"""
psithon = which("psi4", return_bool=True)
psiapi = which_import("psi4", return_bool=True)
error_msg = ""
error_which = which

if psithon and not psiapi:
with popen([which("psi4"), "--module"]) as exc:
exc["proc"].wait(timeout=30)
if "module does not exist" in exc["stderr"]:
pass # --module argument only in Psi4 DDD branch
psiapi = True # --module argument only in Psi4 DDD branch (or >=1.6) so grandfathered in
else:
sys.path.append(exc["stdout"].split()[-1])
so, se = exc["stdout"], exc["stderr"]
error_msg = f" In particular, psi4 command found but unable to load psi4 module into sys.path. stdout: {so}, stderr: {se}"
error_which = which_import
if (so) and (not se) and (exc["proc"].returncode == 0):
psimod = Path(so.rstrip()) # stdout is string & Path is tolerant, so safe op
if psimod.exists():
sys.path.append(str(psimod))
psiapi = which_import("psi4", return_bool=True)

if psiapi and not psithon:
psiimport = str(Path(which_import("psi4")).parent.parent)
env = os.environ.copy()
env["PYTHONPATH"] = psiimport
with popen(["python", "-c", "import psi4; print(psi4.executable[:-5])"], popen_kwargs={"env": env}) as exc:
with popen(["python", "-c", "import psi4; print(psi4.executable)"]) as exc:
exc["proc"].wait(timeout=30)
os.environ["PATH"] += os.pathsep + exc["stdout"].split()[-1]

if psithon or psiapi:
so, se = exc["stdout"], exc["stderr"]
error_msg = f" In particular, psi4 module found but unable to load psi4 command into PATH. stdout: {so}, stderr: {se}"
# yes, everthing up to here could be got from `import psi4; psiexe = psi4.executable`. but, we try not to
# load programs/modules in the `def found` fns.
if (so) and (not se) and (exc["proc"].returncode == 0):
psiexe = Path(so.rstrip()) # stdout is string & Path is tolerant, so safe op
if psiexe.exists():
os.environ["PATH"] += os.pathsep + str(psiexe.parent)
psithon = which("psi4", return_bool=True)

if psithon and psiapi:
return True

return which(
return error_which(
"psi4",
return_bool=True,
raise_error=raise_error,
raise_msg="Please install via `conda install psi4 -c psi4`. Check it's in your PATH with `which psi4`.",
raise_msg="Please install via `conda install psi4 -c conda-forge/label/libint_dev -c conda-forge`. Check it's in your PATH with `which psi4`."
+ error_msg,
)

def get_version(self) -> str:
Expand All @@ -88,7 +103,9 @@ def get_version(self) -> str:
if which_prog not in self.version_cache:
with popen([which_prog, "--version"]) as exc:
exc["proc"].wait(timeout=30)
self.version_cache[which_prog] = safe_version(exc["stdout"].split()[-1])
if (exc["proc"].returncode != 0) or exc["stderr"]:
raise TypeError(f"Error {exc['proc'].returncode} retrieving Psi4 version: " + exc["stderr"])
self.version_cache[which_prog] = safe_version(exc["stdout"].rstrip())

candidate_version = self.version_cache[which_prog]

Expand Down Expand Up @@ -205,7 +222,7 @@ def compute(self, input_model: "AtomicInput", config: "TaskConfig") -> "AtomicRe
psi4.core.set_num_threads(config.ncores, quiet=True)
psi4.set_memory(f"{config.memory}GiB", quiet=True)
# psi4.core.IOManager.shared_object().set_default_path(str(tmpdir))
if pversion < parse_version("1.6a2"): # adjust to where DDD merged
if pversion < parse_version("1.6"): # adjust to where DDD merged
# slightly dangerous in that if `qcng.compute({..., psiapi=True}, "psi4")` called *from psi4
# session*, session could unexpectedly get its own files cleaned away.
output_data = psi4.schema_wrapper.run_qcschema(input_model).dict()
Expand Down

0 comments on commit 6db2494

Please sign in to comment.