Source code for atomate2.lobster.run
"""Functions to run LOBSTER."""
from __future__ import annotations
import logging
import shlex
import subprocess
from os.path import expandvars
from typing import TYPE_CHECKING, Any
from custodian import Custodian
from custodian.lobster.handlers import EnoughBandsValidator, LobsterFilesValidator
from custodian.lobster.jobs import LobsterJob
from jobflow.utils import ValueEnum
from atomate2 import SETTINGS
if TYPE_CHECKING:
from collections.abc import Sequence
from custodian.custodian import Validator
_DEFAULT_VALIDATORS = (LobsterFilesValidator(), EnoughBandsValidator())
_DEFAULT_HANDLERS = ()
logger = logging.getLogger(__name__)
[docs]
class JobType(ValueEnum):
"""
Type of Lobster job.
- ``DIRECT``: Run Lobster without custodian.
- ``NORMAL``: Normal custodian :obj:`.LobsterJob`.
"""
DIRECT = "direct"
NORMAL = "normal"
[docs]
def run_lobster(
job_type: JobType | str = JobType.NORMAL,
lobster_cmd: str = SETTINGS.LOBSTER_CMD,
max_errors: int = SETTINGS.LOBSTER_CUSTODIAN_MAX_ERRORS,
scratch_dir: str = SETTINGS.CUSTODIAN_SCRATCH_DIR,
validators: Sequence[Validator] = _DEFAULT_VALIDATORS,
lobster_job_kwargs: dict[str, Any] = None,
custodian_kwargs: dict[str, Any] = None,
) -> None:
"""
Run Lobster.
Supports running Lobster with or without custodian (see :obj:`JobType`).
Parameters
----------
job_type : str or .JobType
The job type.
lobster_cmd : str
Command to run lobster.
max_errors : int
Maximum number of errors.
scratch_dir : str or Path
Scratch directory.
validators : list of .Validator
The validators handlers used by custodian.
lobster_job_kwargs : dict
Keyword arguments that are passed to :obj:`.LosterJob`.
custodian_kwargs : dict
Keyword arguments that are passed to :obj:`.Custodian`.
"""
lobster_job_kwargs = lobster_job_kwargs or {}
custodian_kwargs = custodian_kwargs or {}
lobster_cmd = expandvars(lobster_cmd)
split_lobster_cmd = shlex.split(lobster_cmd)
if job_type == JobType.DIRECT:
logger.info(f"Running command: {lobster_cmd}")
return_code = subprocess.call(lobster_cmd, shell=True) # noqa: S602
logger.info(f"{lobster_cmd} finished running with returncode: {return_code}")
return
if job_type == JobType.NORMAL:
jobs = [LobsterJob(split_lobster_cmd, **lobster_job_kwargs)]
else:
raise ValueError(f"Unsupported {job_type=}")
handlers: list = []
c = Custodian(
handlers,
jobs,
validators=validators,
max_errors=max_errors,
scratch_dir=scratch_dir,
**custodian_kwargs,
)
logger.info("Running Lobster using custodian.")
c.run()