diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 00e51db9..3bc6315e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,14 +7,14 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.5 + rev: v0.4.3 hooks: - id: ruff args: [--fix, --ignore, D, --unsafe-fixes] - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-yaml exclude: pymatgen/analysis/vesta_cutoffs.yaml @@ -22,7 +22,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.9.0 + rev: v1.10.0 hooks: - id: mypy additional_dependencies: [types-requests] diff --git a/custodian/ansible/interpreter.py b/custodian/ansible/interpreter.py index 3c827b6f..261b5ee5 100644 --- a/custodian/ansible/interpreter.py +++ b/custodian/ansible/interpreter.py @@ -41,6 +41,8 @@ def __init__(self, actions=None, strict=True, directory="./") -> None: mode, unsupported actions are simply ignored without any errors raised. In strict mode, if an unsupported action is supplied, a ValueError is raised. Defaults to True. + directory (str): The directory containing the files to be modified. + Defaults to "./". """ self.supported_actions = {} actions = actions if actions is not None else [DictActions] diff --git a/custodian/cp2k/handlers.py b/custodian/cp2k/handlers.py index 12b20561..c7402889 100644 --- a/custodian/cp2k/handlers.py +++ b/custodian/cp2k/handlers.py @@ -18,6 +18,7 @@ import os import re import time +from typing import ClassVar import numpy as np from monty.os.path import zpath @@ -56,7 +57,11 @@ class StdErrHandler(ErrorHandler): is_monitor = True raises_runtime_error = False - error_msgs = {"seg_fault": ["SIGSEGV"], "out_of_memory": ["insufficient virtual memory"], "abort": ["SIGABRT"]} + error_msgs: ClassVar = { + "seg_fault": ["SIGSEGV"], + "out_of_memory": ["insufficient virtual memory"], + "abort": ["SIGABRT"], + } def __init__(self, std_err="std_err.txt") -> None: """Initialize the handler with the output file to check. diff --git a/custodian/cp2k/interpreter.py b/custodian/cp2k/interpreter.py index a76187a4..7baa1ffd 100644 --- a/custodian/cp2k/interpreter.py +++ b/custodian/cp2k/interpreter.py @@ -35,7 +35,8 @@ def __init__(self, filename="cp2k.inp", actions=None, strict=True, ci=None, dire supplied, a ValueError is raised. Defaults to True. ci (Cp2kInput): A Cp2kInput object from the current directory. Initialized automatically if not passed (but passing it will - avoid having to reparse the directory). + avoid having to re-parse the directory). + directory (str): The directory containing the Cp2kInput set. Defaults to "./". """ self.directory = directory self.ci = ci or Cp2kInput.from_file(os.path.join(self.directory, filename)) diff --git a/custodian/cp2k/jobs.py b/custodian/cp2k/jobs.py index d7606fa8..a1f7330a 100644 --- a/custodian/cp2k/jobs.py +++ b/custodian/cp2k/jobs.py @@ -77,7 +77,7 @@ def __init__( self.final = final self.backup = backup self.suffix = suffix - self.settings_override = settings_override if settings_override else [] + self.settings_override = settings_override or [] self.restart = restart def setup(self, directory="./") -> None: diff --git a/custodian/custodian.py b/custodian/custodian.py index da4e70ee..0441d342 100644 --- a/custodian/custodian.py +++ b/custodian/custodian.py @@ -212,12 +212,12 @@ def __init__( def _load_checkpoint(directory): restart = 0 run_log = [] - chkpts = glob(os.path.join(directory, "custodian.chk.*.tar.gz")) - if chkpts: - chkpt = sorted(chkpts, key=lambda c: int(c.split(".")[-3]))[0] - restart = int(chkpt.split(".")[-3]) - logger.info(f"Loading from checkpoint file {chkpt}...") - with tarfile.open(chkpt) as file: + chk_pts = glob(os.path.join(directory, "custodian.chk.*.tar.gz")) + if chk_pts: + chk_pt = min(chk_pts, key=lambda c: int(c.split(".")[-3])) + restart = int(chk_pt.split(".")[-3]) + logger.info(f"Loading from checkpoint file {chk_pt}...") + with tarfile.open(chk_pt) as file: def is_within_directory(directory, target): abs_directory = os.path.abspath(directory) @@ -381,7 +381,7 @@ def run(self): try: # skip jobs until the restart - for job_n, job in islice(enumerate(self.jobs, 1), self.restart, None): + for job_n, job in islice(enumerate(self.jobs, start=1), self.restart, None): self._run_job(job_n, job) # We do a dump of the run log after each job. dumpfn(self.run_log, os.path.join(self.directory, Custodian.LOG_FILE), cls=MontyEncoder, indent=4) diff --git a/custodian/feff/interpreter.py b/custodian/feff/interpreter.py index 8c20c2f6..df44cdbd 100644 --- a/custodian/feff/interpreter.py +++ b/custodian/feff/interpreter.py @@ -25,8 +25,8 @@ def __init__(self, actions=None, strict=True, feffinp=None, directory="./") -> N supplied, a ValueError is raised. Defaults to True. feffinp (FEFFInput): A FeffInput object from the current directory. Initialized automatically if not passed (but passing it will - avoid having to reparse the directory). - directory (str): Directory to run in + avoid having to re-parse the directory). + directory (str): The directory containing the FeffInput set. Defaults to "./". """ self.directory = directory self.feffinp = feffinp or FEFFDictSet.from_directory(self.directory) diff --git a/custodian/gaussian/__init__.py b/custodian/gaussian/__init__.py index a4fa85c4..cd7a1386 100644 --- a/custodian/gaussian/__init__.py +++ b/custodian/gaussian/__init__.py @@ -1,6 +1,4 @@ -""" -This package implements various Gaussian Jobs and Error Handlers. -""" +"""This package implements various Gaussian Jobs and Error Handlers.""" __author__ = "Rasha Atwi" __version__ = "0.1" diff --git a/custodian/gaussian/handlers.py b/custodian/gaussian/handlers.py index 0983d213..70b2d546 100644 --- a/custodian/gaussian/handlers.py +++ b/custodian/gaussian/handlers.py @@ -9,7 +9,7 @@ import os import re import shutil -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, ClassVar import numpy as np from monty.io import zopen @@ -47,7 +47,7 @@ class GaussianErrorHandler(ErrorHandler): """ # definition of job errors as they appear in Gaussian output file - error_defs = { + error_defs: ClassVar = { "Optimization stopped": "opt_steps", "Convergence failure": "scf_convergence", "FormBX had a problem": "linear_bend", @@ -71,7 +71,7 @@ class GaussianErrorHandler(ErrorHandler): recom_mem_patt = re.compile( r"Use %mem=([0-9]+)MW to provide the minimum amount of memory required to complete this step." ) - conv_critera = { + conv_criteria: ClassVar = { "max_force": re.compile(r"\s+(Maximum Force)\s+(-?\d+.?\d*|.*)\s+(-?\d+.?\d*)"), "rms_force": re.compile(r"\s+(RMS {5}Force)\s+(-?\d+.?\d*|.*)\s+(-?\d+.?\d*)"), "max_disp": re.compile(r"\s+(Maximum Displacement)\s+(-?\d+.?\d*|.*)\s+(-?\d+.?\d*)"), @@ -79,7 +79,7 @@ class GaussianErrorHandler(ErrorHandler): } grid_patt = re.compile(r"(-?\d{5})") - GRID_NAMES = [ + GRID_NAMES = ( "finegrid", "fine", "superfinegrid", @@ -90,8 +90,8 @@ class GaussianErrorHandler(ErrorHandler): "sg1", "pass0grid", "pass0", - ] - MEM_UNITS = ["kb", "mb", "gb", "tb", "kw", "mw", "gw", "tw"] + ) + MEM_UNITS = ("kb", "mb", "gb", "tb", "kw", "mw", "gw", "tw") activate_better_guess = False @@ -504,7 +504,7 @@ def check(self, directory: str = "./") -> bool: self.recom_mem = GaussianErrorHandler.convert_mem(float(mem), "mw") if self.check_convergence and "opt" in self.gin.route_parameters: - for k, v in GaussianErrorHandler.conv_critera.items(): + for k, v in GaussianErrorHandler.conv_criteria.items(): m = v.search(line) if m: if k not in self.conv_data["values"]: diff --git a/custodian/utils.py b/custodian/utils.py index de3fd01c..fe366487 100644 --- a/custodian/utils.py +++ b/custodian/utils.py @@ -5,6 +5,7 @@ import os import tarfile from glob import glob +from typing import ClassVar def backup(filenames, prefix="error", directory="./") -> None: @@ -60,7 +61,7 @@ class tracked_lru_cache: Allows Custodian to clear the cache after all the checks have been performed. """ - cached_functions: set = set() + cached_functions: ClassVar = set() def __init__(self, func) -> None: """ diff --git a/custodian/vasp/handlers.py b/custodian/vasp/handlers.py index 94e34cd7..20709132 100644 --- a/custodian/vasp/handlers.py +++ b/custodian/vasp/handlers.py @@ -16,6 +16,7 @@ import warnings from collections import Counter from math import prod +from typing import ClassVar import numpy as np from monty.dev import deprecated @@ -66,7 +67,7 @@ class VaspErrorHandler(ErrorHandler): is_monitor = True - error_msgs = { + error_msgs: ClassVar = { "tet": [ "Tetrahedron method fails", "tetrahedron method fails", @@ -135,20 +136,17 @@ def __init__( is being redirected. The error messages that are checked are present in the stdout. Defaults to "vasp.out", which is the default redirect used by :class:`custodian.vasp.jobs.VaspJob`. - errors_subset_to_detect (list): A subset of errors to catch. The + errors_subset_to_catch (list): A subset of errors to catch. The default is None, which means all supported errors are detected. Use this to catch only a subset of supported errors. E.g., ["eddrmm", "zheev"] will only catch the eddrmm and zheev errors, and not others. If you wish to only exclude one or - two of the errors, you can create this list by the following - lines: + two of the errors, you can create this list by the following lines: - ``` - subset = list(VaspErrorHandler().error_msgs) - subset.remove("eddrmm") + subset = list(VaspErrorHandler().error_msgs) + subset.remove("eddrmm") + handler = VaspErrorHandler(errors_subset_to_catch=subset) - handler = VaspErrorHandler(errors_subset_to_catch=subset) - ``` vtst_fixes (bool): Whether to consider VTST optimizers. Defaults to False for compatibility purposes, but if you have VTST, you would likely benefit from setting this to True. @@ -691,7 +689,7 @@ class LrfCommutatorHandler(ErrorHandler): is_monitor = True - error_msgs = {"lrf_comm": ["LRF_COMMUTATOR internal error"]} + error_msgs: ClassVar = {"lrf_comm": ["LRF_COMMUTATOR internal error"]} def __init__(self, output_filename: str = "std_err.txt") -> None: """Initialize the handler with the output file to check. @@ -744,7 +742,7 @@ class StdErrHandler(ErrorHandler): is_monitor = True - error_msgs = { + error_msgs: ClassVar = { "kpoints_trans": ["internal error in GENERATE_KPOINTS_TRANS: number of G-vector changed in star"], "out_of_memory": ["Allocation would exceed memory limit"], } @@ -804,7 +802,7 @@ class AliasingErrorHandler(ErrorHandler): is_monitor = True - error_msgs = { + error_msgs: ClassVar = { "aliasing": ["WARNING: small aliasing (wrap around) errors must be expected"], "aliasing_incar": ["Your FFT grids (NGX,NGY,NGZ) are not sufficient for an accurate"], } diff --git a/custodian/vasp/interpreter.py b/custodian/vasp/interpreter.py index 56fbe970..80795d27 100644 --- a/custodian/vasp/interpreter.py +++ b/custodian/vasp/interpreter.py @@ -24,7 +24,8 @@ def __init__(self, actions=None, strict=True, vi=None, directory="./") -> None: supplied, a ValueError is raised. Defaults to True. vi (VaspInput): A VaspInput object from the current directory. Initialized automatically if not passed (but passing it will - avoid having to reparse the directory). + avoid having to re-parse the directory). + directory (str): The directory containing the VaspInput set. """ self.vi = vi or VaspInput.from_directory(directory) self.directory = directory diff --git a/custodian/vasp/jobs.py b/custodian/vasp/jobs.py index 2297a9b7..ba7b7258 100644 --- a/custodian/vasp/jobs.py +++ b/custodian/vasp/jobs.py @@ -315,6 +315,7 @@ def double_relaxation_run( wall-time handler which will write a read-only STOPCAR to prevent VASP from deleting it once it finishes. Defaults to False. + directory (str): Directory where the job was run. Defaults to './'. Returns: List of two jobs corresponding to an AFLOW style run. @@ -382,12 +383,8 @@ def metagga_opt_run( metaGGA = incar.get("METAGGA", "SCAN") # Pre optimize WAVECAR and structure using regular GGA - pre_opt_settings = [ - { - "dict": "INCAR", - "action": {"_set": {"METAGGA": None, "LWAVE": True, "NSW": 0}}, - } - ] + new_settings = {"METAGGA": None, "LWAVE": True, "NSW": 0} + pre_opt_settings = [{"dict": "INCAR", "action": {"_set": new_settings}}] jobs = [ VaspJob( vasp_cmd, @@ -460,6 +457,7 @@ def full_opt_run( half_kpts_first_relax (bool): Whether to halve the kpoint grid for the first relaxation. Speeds up difficult convergence considerably. Defaults to False. + directory (str): Directory where the job was run. Defaults to './'. **vasp_job_kwargs: Passthrough kwargs to VaspJob. See :class:`custodian.vasp.jobs.VaspJob`. @@ -558,6 +556,7 @@ def constrained_opt_run( which is more robust but can be a bit slow. The code does fall back on the bisection when bfgs gives a nonsensical result, e.g., negative lattice params. + directory (str): Directory where the job was run. Defaults to './'. **vasp_job_kwargs: Passthrough kwargs to VaspJob. See :class:`custodian.vasp.jobs.VaspJob`. @@ -807,8 +806,7 @@ def __init__( self.settings_override = settings_override def setup(self, directory="./") -> None: - """ - Performs initial setup for VaspNEBJob, including overriding any settings + """Performs initial setup for VaspNEBJob, including overriding any settings and backing up. """ neb_dirs, neb_sub = self._get_neb_dirs(directory) @@ -825,7 +823,6 @@ def setup(self, directory="./") -> None: if self.half_kpts and os.path.isfile(os.path.join(directory, "KPOINTS")): kpts = Kpoints.from_file(os.path.join(directory, "KPOINTS")) kpts.kpts = np.maximum(np.array(kpts.kpts) / 2, 1) - kpts.kpts = kpts.kpts.astype(int).tolist() if tuple(kpts.kpts[0]) == (1, 1, 1): kpt_dic = kpts.as_dict() kpt_dic["generation_style"] = "Gamma" diff --git a/docs/_themes/flask_theme_support.py b/docs/_themes/flask_theme_support.py index 76b16b2c..785f75bf 100644 --- a/docs/_themes/flask_theme_support.py +++ b/docs/_themes/flask_theme_support.py @@ -1,5 +1,7 @@ """flasky extensions. flasky pygments style based on tango style.""" +from typing import ClassVar + from pygments.style import Style from pygments.token import ( Comment, @@ -23,7 +25,7 @@ class FlaskyStyle(Style): background_color = "#f8f8f8" default_style = "" - styles = { + styles: ClassVar = { # No corresponding class for the following: # Text: "", # class: '' Whitespace: "underline #f8f8f8", # class: 'w' diff --git a/tasks.py b/tasks.py index 1d4b1fc6..f2cf6aba 100644 --- a/tasks.py +++ b/tasks.py @@ -13,7 +13,7 @@ from custodian import __version__ as CURRENT_VER -NEW_VER = datetime.datetime.today().strftime("%Y.%-m.%-d") +NEW_VER = datetime.datetime.now(tz=datetime.timezone.utc).strftime("%Y.%-m.%-d") @task diff --git a/tests/gaussian/test_jobs.py b/tests/gaussian/test_jobs.py index b3137ae3..8642c78c 100644 --- a/tests/gaussian/test_jobs.py +++ b/tests/gaussian/test_jobs.py @@ -67,8 +67,8 @@ def test_better_guess(self): self.output_file, self.stderr_file, self.backup, - True, - self.directory, + cart_coords=True, + directory=self.directory, ) jobs = list(job_gen) assert len(jobs) == 1, "One job should be generated under normal conditions."