Source code for autojob.harvest.harvesters.gaussian
"""Gaussian calculation harvesting utilities.
This module defines the :func:`.harvest_gaussian_results` function which
can be used to harvest Gaussian results from a directory.
Example:
from pathlib import Path
from autojob.harvest.harvesters.gaussian import harvest_gaussian_results
outputs = harvest_gaussian_results(Path.cwd())
"""
import logging
from pathlib import Path
from typing import Any
import ase.io
import cclib.io.ccio
from cclib.parser.data import ccData
logger = logging.getLogger(__name__)
FILES_TO_CARRYOVER = ["Gaussian.chk"]
GAUSSIAN_LOG = "Gaussian.log"
[docs]
def harvest_gaussian_results(
src: str | Path,
) -> dict[str, Any]:
"""Harvest Gaussian calculation results from a directory.
Note that all quantities other than the final energy are reported in atomic
units (Hartree/Bohr).
Args:
src: The directory containing the Gaussian output files.
Returns:
A dictionary containing Gaussian calculation outputs.
"""
logger.debug(
"Loading calculation outputs for Gaussian calculation in directory: %s",
src,
)
log_file = Path(src, GAUSSIAN_LOG)
results: dict[str, Any] = {"forces": None, "energy": None}
if not log_file.exists():
logger.warning(
"Gaussian output file %s does not exist in directory: %s",
GAUSSIAN_LOG,
src,
)
elif cc_data := cclib.io.ccio.ccread(str(log_file)):
cc_data.listify()
data = cc_data.getattributes()
if energies := data.get("scfenergies"):
results["energy"] = energies[-1] if energies else None
if forces := data.get("grads"):
results["forces"] = forces[-1] if forces else None
results["converged"] = bool(
# optimization statuses are defined in bit value notation
getattr(cc_data, "optstatus", False)
and (cc_data.optstatus[-1] & ccData.OPT_DONE)
)
atoms = ase.io.read(Path(src, GAUSSIAN_LOG))
results["calculator_results"] = {**data, "atoms": atoms}
logger.debug(
"Successfully loaded Gaussian calculation outputs "
"from directory: %s",
src,
)
else:
logger.warning(
"Unable to parse Gaussian outputs from directory: %s", src
)
return results