diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index c1c8d59b62..eda0638cbe 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" cache: pip cache-dependency-path: pyproject.toml @@ -36,7 +36,7 @@ jobs: shell: bash -l {0} # enables conda/mamba env activation by reading bash profile strategy: matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] steps: - name: Check out repo @@ -103,7 +103,7 @@ jobs: shell: bash -l {0} # enables conda/mamba env activation by reading bash profile strategy: matrix: - python-version: ["3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] steps: - name: Check out repo diff --git a/.github/workflows/update-precommit.yml b/.github/workflows/update-precommit.yml index 273be7171f..6959999283 100644 --- a/.github/workflows/update-precommit.yml +++ b/.github/workflows/update-precommit.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.10" - name: Install pre-commit run: pip install pre-commit diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 533a6d33db..203672442e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ default_language_version: exclude: ^(.github/|tests/test_data/abinit/) repos: - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.6.1 + rev: v0.6.7 hooks: - id: ruff args: [--fix] @@ -30,7 +30,7 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy files: ^src/ diff --git a/README.md b/README.md index 4f11ff119e..4b6997bb64 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![pypi version](https://img.shields.io/pypi/v/atomate2?color=blue)](https://pypi.org/project/atomate2) ![supported python versions](https://img.shields.io/pypi/pyversions/atomate2) [![Zenodo](https://img.shields.io/badge/DOI-10.5281/zenodo.10677081-blue?logo=Zenodo&logoColor=white)](https://zenodo.org/records/10677081) +[![This project supports Python 3.10+](https://img.shields.io/badge/Python-3.10+-blue.svg?logo=python&logoColor=white)](https://python.org/downloads) [Documentation][docs] | [PyPI][pypi] | [GitHub][github] @@ -85,7 +86,7 @@ atomate2 workflows can be run using the [FireWorks] software. See the ## Installation -Atomate2 is a Python 3.8+ library and can be installed using pip. Full installation +Atomate2 is a Python 3.10+ library and can be installed using pip. Full installation and configuration instructions are provided in the [installation tutorial][installation]. ## Tutorials diff --git a/docs/conf.py b/docs/conf.py index 3baba5e939..4ba88b9a01 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -169,7 +169,7 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "python": ("https://docs.python.org/3.8", None), + "python": ("https://docs.python.org/3.10", None), "matplotlib": ("https://matplotlib.org/stable/", None), "networkx": ("https://networkx.org/documentation/stable/", None), "jobflow": ("https://materialsproject.github.io/jobflow", None), diff --git a/docs/user/codes/openmm.md b/docs/user/codes/openmm.md index 59271e4db7..30a4ca651a 100644 --- a/docs/user/codes/openmm.md +++ b/docs/user/codes/openmm.md @@ -6,7 +6,7 @@ >>> conda activate atomate2 # installing atomate2 ->>> pip install git+https://github.com/orionarcher/atomate2.git +>>> pip install git+https://github.com/orionarcher/atomate2 # installing classical_md dependencies >>> conda install -c conda-forge --file .github/classical_md_requirements.txt @@ -18,7 +18,7 @@ you can clone the repository and install from source. ``` bash # installing atomate2 ->>> git clone https://github.com/orionarcher/atomate2.git +>>> git clone https://github.com/orionarcher/atomate2 >>> cd atomate2 >>> git branch openff >>> git checkout openff @@ -33,7 +33,7 @@ you intend to run on GPU, make sure that the tests are passing for CUDA. >>> python -m openmm.testInstallation ``` -# Understanding Atomate2 OpenMM +## Understanding Atomate2 OpenMM Atomate2 is really just a collection of jobflow workflows relevant to materials science. In all the workflows, we pass our system of interest @@ -55,7 +55,6 @@ The first job we need to create generates the `Interchange` object. To specify the system of interest, we use give it the SMILES strings, counts, and names (optional) of the molecules we want to include. - ```python from atomate2.openff.core import generate_interchange @@ -73,7 +72,6 @@ out the `create_mol_spec` function in the `atomate2.openff.utils` module. Under the hood, this is being called on each mol_spec dict. Meaning the code below is functionally identical to the code above. - ```python from atomate2.openff.utils import create_mol_spec @@ -90,7 +88,6 @@ object, which we can pass to the next stage of the simulation. NOTE: It's actually mandatory to include partial charges for PF6- here, the built in partial charge method fails. - ```python import numpy as np from pymatgen.core.structure import Molecule @@ -205,13 +202,13 @@ Awesome! At this point, we've run a workflow and could start analyzing our data. Before we get there though, let's go through some of the other simulation options available. -# Digging Deeper +## Digging Deeper Atomate2 OpenMM supports running a variety of workflows with different configurations. Below we dig in to some of the more advanced options. - ### Configuring the Simulation +
Learn more about the configuration of OpenMM simulations @@ -228,14 +225,13 @@ once and have it apply to all stages of the simulation. The value inheritance is as follows: 1) any explicitly set value, 2) the value from the previous maker, 3) the default value (as shown below). - ```python from atomate2.openmm.jobs.base import OPENMM_MAKER_DEFAULTS print(OPENMM_MAKER_DEFAULTS) ``` -``` +```py { "step_size": 0.001, "temperature": 298, @@ -339,7 +335,6 @@ Rather than use `jobflow.yaml`, you could also create the stores in Python and pass the stores to the `run_locally` function. This is a bit more code, so usually the prior method is preferred. - ```python from jobflow import run_locally, JobStore from maggma.stores import MongoStore, S3Store @@ -374,6 +369,7 @@ run_locally( ensure_success=True, ) ``` +
### Running on GPUs @@ -381,12 +377,10 @@ run_locally(
Learn to accelerate MD simulations with GPUs - Running on a GPU is nearly as simple as running on a CPU. The only difference is that you need to specify the `platform_properties` argument in the `EnergyMinimizationMaker` with the `DeviceIndex` of the GPU you want to use. - ```python production_maker = OpenMMFlowMaker( name="test_production", @@ -414,7 +408,6 @@ First you'll need to install mpi4py. Then you can modify and run the following script to distribute the work across the GPUs. - ```python # other imports @@ -457,15 +450,16 @@ for i in range(4): # this script will run four times, each with a different rank, thus distributing the work across the four GPUs. run_locally(flows[rank], ensure_success=True) ``` +
-# Analysis with Emmet +## Analysis with Emmet For now, you'll need to make sure you have a particular emmet branch installed. Later the builders will be integrated into `main`. ```bash -pip install git+https://github.com/orionarcher/emmet.git@md_builders +pip install git+https://github.com/orionarcher/emmet@md_builders ``` ### Analyzing Local Data @@ -498,6 +492,7 @@ u = create_universe( solute = create_solute(u, solute_name="Li", networking_solvents=["PF6"]) ``` + ### Setting up builders @@ -556,6 +551,7 @@ builder.connect() Here are some more convenient queries. Here are some more convenient queries we could use! + ```python # query jobs from a specific day april_16 = {"completed_at": {"$regex": "^2024-04-16"}} @@ -570,6 +566,7 @@ job_uuids = [ ] my_specific_jobs = {"uuid": {"$in": job_uuids}} ``` + @@ -611,6 +608,7 @@ solute = create_solute( fallback_radius=3, ) ``` + ### Automated analysis with builders diff --git a/docs/user/install.md b/docs/user/install.md index b1d6169dea..5649c27271 100644 --- a/docs/user/install.md +++ b/docs/user/install.md @@ -136,8 +136,8 @@ atomate2 ## Create a conda environment ```{note} -Make sure to create a Python 3.8+ environment as recent versions of atomate2 only -support Python 3.8 and higher. +Make sure to create a Python 3.10+ environment as recent versions of atomate2 only +support Python 3.10 and higher. ``` We highly recommend that you organize your installation of the atomate2 and the other diff --git a/pyproject.toml b/pyproject.toml index 7312ff6c47..fb4a48ff5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,11 +19,11 @@ classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.12", "Topic :: Other/Nonlisted Topic", "Topic :: Scientific/Engineering", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "PyYAML", "click", @@ -54,21 +54,19 @@ forcefields = [ "calorine<=2.2.1", "chgnet>=0.2.2", "mace-torch>=0.3.3", - "torchdata<=0.7.1", # TODO: remove when issue fixed "matgl>=1.1.3", - "quippy-ase>=0.9.14", + # quippy-ase support for py3.12 tracked in https://github.com/libAtoms/QUIP/issues/645 + "quippy-ase>=0.9.14; python_version < '3.12'", "sevenn>=0.9.3", + "torchdata<=0.7.1", # TODO: remove when issue fixed ] -ase = [ - "ase>=3.23.0", -] -ase-ext = [ - "tblite>=0.3.0", -] +ase = ["ase>=3.23.0"] +# tblite py3.12 support tracked in https://github.com/tblite/tblite/issues/198 +ase-ext = ["tblite>=0.3.0; python_version < '3.12'"] openmm = [ "mdanalysis>=2.7.0", - "openmm>=8.1.0", "openmm-mdanalysis-reporter>=0.1.0", + "openmm>=8.1.0", ] docs = [ "FireWorks==2.0.3", @@ -101,9 +99,12 @@ strict = [ "ijson==3.3.0", "jobflow==0.1.18", "lobsterpy==0.4.5", + "mdanalysis==2.7.0", "monty==2024.7.30", "mp-api==0.42.2", "numpy", + "openmm-mdanalysis-reporter==0.1.0", + "openmm==8.1.1", "phonopy==2.27.0", "pydantic-settings==2.5.2", "pydantic==2.9.2", @@ -111,21 +112,18 @@ strict = [ "pymatgen==2024.6.10", "python-ulid==2.7.0", "seekpath==2.1.0", - "tblite==0.3.0", + "tblite==0.3.0; python_version < '3.12'", "typing-extensions==4.12.2", - "mdanalysis==2.7.0", - "openmm==8.1.1", - "openmm-mdanalysis-reporter==0.1.0", ] strict-forcefields = [ "calorine==2.2.1", "chgnet==0.3.8", "mace-torch>=0.3.3", - "torchdata==0.7.1", # TODO: remove when issue fixed "matgl==1.1.3", - "quippy-ase==0.9.14", + "quippy-ase==0.9.14; python_version < '3.12'", "sevenn==0.9.3.post1", "torch==2.2.1", + "torchdata==0.7.1", # TODO: remove when issue fixed ] [project.scripts] @@ -179,7 +177,8 @@ exclude_lines = [ ] [tool.ruff] -target-version = "py39" +target-version = "py310" +output-format = "concise" [tool.ruff.lint] select = ["ALL"] diff --git a/src/atomate2/abinit/sets/base.py b/src/atomate2/abinit/sets/base.py index e2cf4825c2..00fb05afec 100644 --- a/src/atomate2/abinit/sets/base.py +++ b/src/atomate2/abinit/sets/base.py @@ -8,7 +8,7 @@ import os from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any import numpy as np from abipy.abio.inputs import AbinitInput, MultiDataset @@ -39,7 +39,7 @@ ) if TYPE_CHECKING: - from collections.abc import Iterable, Sequence + from collections.abc import Callable, Iterable, Sequence from pymatgen.core.structure import Structure @@ -394,7 +394,7 @@ def check_format_prev_dirs( """Check and format the prev_dirs (restart or dependency).""" if prev_dirs is None: return None - if isinstance(prev_dirs, (str, Path)): + if isinstance(prev_dirs, str | Path): return [str(prev_dirs)] return [str(prev_dir) for prev_dir in prev_dirs] diff --git a/src/atomate2/abinit/sets/core.py b/src/atomate2/abinit/sets/core.py index f75a5adc09..f8d1976cc6 100644 --- a/src/atomate2/abinit/sets/core.py +++ b/src/atomate2/abinit/sets/core.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING import numpy as np from abipy.abio.factories import ( @@ -18,6 +18,8 @@ from atomate2.abinit.sets.base import AbinitInputGenerator if TYPE_CHECKING: + from collections.abc import Callable + from abipy.abio.inputs import AbinitInput from pymatgen.core import Structure from pymatgen.io.abinit import PseudoTable diff --git a/src/atomate2/abinit/utils/history.py b/src/atomate2/abinit/utils/history.py index 87888b59c3..4356019f78 100644 --- a/src/atomate2/abinit/utils/history.py +++ b/src/atomate2/abinit/utils/history.py @@ -56,7 +56,7 @@ def log_initialization( self, job: Job | Flow, initialization_info: Any | None = None ) -> None: """Log initialization information about the job.""" - details = {"job_class": job.__class__.__name__} + details = {"job_class": type(job).__name__} if initialization_info: details["initialization_info"] = initialization_info self.append(JobEvent(JobEvent.INITIALIZED, details=details)) @@ -187,7 +187,7 @@ def get_events_by_types(self, types: list | AbinitEvent) -> list: types Single type or list of types. """ - types = types if isinstance(types, (list, tuple)) else [types] + types = types if isinstance(types, list | tuple) else [types] return [e for e in self if e.event_type in types] diff --git a/src/atomate2/aims/utils/bands.py b/src/atomate2/aims/utils/bands.py index ead5651a26..dd6fe17337 100644 --- a/src/atomate2/aims/utils/bands.py +++ b/src/atomate2/aims/utils/bands.py @@ -33,7 +33,7 @@ def prepare_band_input(structure: Structure, density: float = 20) -> list: points, labels = bp.get_kpoints(line_density=density, coords_are_cartesian=False) lines_and_labels: list[_SegmentDict] = [] current_segment: _SegmentDict | None = None - for label_, coords in zip(labels, points): + for label_, coords in zip(labels, points, strict=True): # rename the Gamma point label label = "G" if label_ in ("GAMMA", "\\Gamma", "Γ") else label_ if label: diff --git a/src/atomate2/ase/md.py b/src/atomate2/ase/md.py index dd11652a19..2d2963380a 100644 --- a/src/atomate2/ase/md.py +++ b/src/atomate2/ase/md.py @@ -320,7 +320,7 @@ def run_ase( ) _dyn_mod_path = DynamicsPresets[ - f"{self.ensemble.value}_{self.dynamics.replace('-','_')}" + f"{self.ensemble.value}_{self.dynamics.replace('-', '_')}" ].value.split(".") dynamics = getattr( import_module(".".join(_dyn_mod_path[:-1])), _dyn_mod_path[-1] diff --git a/src/atomate2/ase/utils.py b/src/atomate2/ase/utils.py index 39a2cfc095..1378371eb5 100644 --- a/src/atomate2/ase/utils.py +++ b/src/atomate2/ase/utils.py @@ -85,7 +85,7 @@ def __init__(self, atoms: Atoms, store_md_outputs: bool = False) -> None: self._store_md_outputs = store_md_outputs if store_md_outputs: - self._calc_kwargs.update({k: True for k in ("velocities", "temperature")}) + self._calc_kwargs |= dict(velocities=True, temperature=True) # `self.{velocities,temperatures}` always initialized, # but data is only stored / saved to trajectory for MD runs self.velocities: list[np.ndarray] = [] @@ -361,7 +361,7 @@ def relax( isinstance(atoms, Atoms) and all(not pbc for pbc in atoms.pbc) ) - if isinstance(atoms, (Structure, Molecule)): + if isinstance(atoms, Structure | Molecule): atoms = self.ase_adaptor.get_atoms(atoms) if self.fix_symmetry: atoms.set_constraint(FixSymmetry(atoms, symprec=self.symprec)) diff --git a/src/atomate2/cli/dev.py b/src/atomate2/cli/dev.py index de9e0cfa79..d879fdfd91 100644 --- a/src/atomate2/cli/dev.py +++ b/src/atomate2/cli/dev.py @@ -325,7 +325,7 @@ def abinit_test_data(test_name: str, test_data_dir: str | None, force: bool) -> maker_info = loadfn("maker.json") maker = maker_info["maker"] - maker_name = maker.__class__.__name__ + maker_name = type(maker).__name__ # take the module path and exclude the first two elements # (i.e. "atomate2" and "abinit") module_path = maker.__module__.split(".")[2:] @@ -413,6 +413,7 @@ def _fake_dirs( ("indata", "outdata", "tmpdata"), (indata_files, outdata_files, tmpdata_files), (indata_fake_files, outdata_fake_files, tmpdata_fake_files), + strict=True, ): _fake_dirdata( src_dir=src_dir, diff --git a/src/atomate2/common/flows/magnetism.py b/src/atomate2/common/flows/magnetism.py index 2f5a15bbd0..5a106192a0 100644 --- a/src/atomate2/common/flows/magnetism.py +++ b/src/atomate2/common/flows/magnetism.py @@ -119,15 +119,13 @@ def __post_init__(self) -> None: """ if self.relax_maker is None: warnings.warn( - ( - "No relax_maker provided, relaxations will be skipped. Please be" - " sure that this is intended!" - ), + "No relax_maker provided, relaxations will be skipped. Please be" + " sure that this is intended!", stacklevel=2, ) else: - static_base_maker_name = self.static_maker.__class__.__mro__[1].__name__ - relax_base_maker_name = self.relax_maker.__class__.__mro__[1].__name__ + static_base_maker_name = type(self.static_maker).__mro__[1].__name__ + relax_base_maker_name = type(self.relax_maker).__mro__[1].__name__ if relax_base_maker_name != static_base_maker_name: warnings.warn( "The provided static and relax makers do not use the " diff --git a/src/atomate2/common/jobs/anharmonicity.py b/src/atomate2/common/jobs/anharmonicity.py index aaf3ef74da..e44aa7f863 100644 --- a/src/atomate2/common/jobs/anharmonicity.py +++ b/src/atomate2/common/jobs/anharmonicity.py @@ -441,7 +441,7 @@ def get_sigma_a_per_mode( ).flatten() anharmonic_vals = [ dft_force - harmonic_force - for dft_force, harmonic_force in zip(dft_vals, harmonic_vals) + for dft_force, harmonic_force in zip(dft_vals, harmonic_vals, strict=True) ] sigma_a = np.std(anharmonic_vals) / np.std(dft_vals) mode_sigma_vals.append((mode, sigma_a)) @@ -544,7 +544,9 @@ def get_sigmas( anharmonic_forces = np.array( [ dft_force - harmonic_force - for dft_force, harmonic_force in zip(dft_forces, harmonic_forces) + for dft_force, harmonic_force in zip( + dft_forces, harmonic_forces, strict=True + ) ] ) diff --git a/src/atomate2/common/jobs/defect.py b/src/atomate2/common/jobs/defect.py index f1a50af1d1..083970fd94 100644 --- a/src/atomate2/common/jobs/defect.py +++ b/src/atomate2/common/jobs/defect.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING import numpy as np from jobflow import Flow, Response, job @@ -20,7 +20,7 @@ from atomate2.utils.path import strip_hostname if TYPE_CHECKING: - from collections.abc import Iterable + from collections.abc import Callable, Iterable from pathlib import Path from emmet.core.tasks import TaskDoc diff --git a/src/atomate2/common/jobs/elastic.py b/src/atomate2/common/jobs/elastic.py index cd25367cf6..3c3be827a3 100644 --- a/src/atomate2/common/jobs/elastic.py +++ b/src/atomate2/common/jobs/elastic.py @@ -78,7 +78,7 @@ def generate_elastic_deformations( strain_magnitudes = [strain_magnitudes] * len(strain_states) # type: ignore[assignment] strains = [] - for state, magnitudes in zip(strain_states, strain_magnitudes): + for state, magnitudes in zip(strain_states, strain_magnitudes, strict=True): strains.extend([Strain.from_voigt(m * np.array(state)) for m in magnitudes]) # remove zero strains diff --git a/src/atomate2/common/jobs/electrode.py b/src/atomate2/common/jobs/electrode.py index 4ef3a29a02..407632ae7a 100644 --- a/src/atomate2/common/jobs/electrode.py +++ b/src/atomate2/common/jobs/electrode.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Callable, NamedTuple +from typing import TYPE_CHECKING, NamedTuple from emmet.core.electrode import InsertionElectrodeDoc from emmet.core.mpid import MPID @@ -14,6 +14,7 @@ from ulid import ULID if TYPE_CHECKING: + from collections.abc import Callable from pathlib import Path from pymatgen.alchemy import ElementLike diff --git a/src/atomate2/common/jobs/magnetism.py b/src/atomate2/common/jobs/magnetism.py index 6243c35270..621c1df1a6 100644 --- a/src/atomate2/common/jobs/magnetism.py +++ b/src/atomate2/common/jobs/magnetism.py @@ -111,7 +111,7 @@ def run_ordering_calculations( """ jobs = [] num_orderings = len(orderings[0]) - for idx, (struct, origin) in enumerate(zip(*orderings)): + for idx, (struct, origin) in enumerate(zip(*orderings, strict=True)): name = f"{idx + 1}/{num_orderings} ({origin})" parent_structure = struct.copy() diff --git a/src/atomate2/common/jobs/qha.py b/src/atomate2/common/jobs/qha.py index 58cbbf641f..517654040f 100644 --- a/src/atomate2/common/jobs/qha.py +++ b/src/atomate2/common/jobs/qha.py @@ -101,7 +101,7 @@ def analyze_free_energy( heat_capacities.append([]) entropies.append([]) - for _, output in sorted(zip(volume, phonon_outputs)): + for _, output in sorted(zip(volume, phonon_outputs, strict=True)): # check if imaginary modes if (not output.has_imaginary_modes) or ignore_imaginary_modes: electronic_energies[itemp].append(output.total_dft_energy) diff --git a/src/atomate2/common/schemas/defects.py b/src/atomate2/common/schemas/defects.py index c91912d6bb..0c79a6ad56 100644 --- a/src/atomate2/common/schemas/defects.py +++ b/src/atomate2/common/schemas/defects.py @@ -1,9 +1,9 @@ """General schemas for defect workflow outputs.""" import logging -from collections.abc import Sequence +from collections.abc import Callable, Sequence from itertools import starmap -from typing import Any, Callable, Optional, Union +from typing import Any, Optional, Union import numpy as np from emmet.core.tasks import TaskDoc @@ -247,12 +247,14 @@ def get_cs_entry( entries1 = list( starmap( - get_cs_entry, zip(structures1, energies1, static_dirs1, static_uuids1) + get_cs_entry, + zip(structures1, energies1, static_dirs1, static_uuids1, strict=True), ) ) entries2 = list( starmap( - get_cs_entry, zip(structures2, energies2, static_dirs2, static_uuids2) + get_cs_entry, + zip(structures2, energies2, static_dirs2, static_uuids2, strict=True), ) ) @@ -393,7 +395,7 @@ def sort_pos_dist( d0 = dist(s1, s2) d_vs_s = [] - for q1, q2, s in zip(d1, d2, list_in): + for q1, q2, s in zip(d1, d2, list_in, strict=True): sign = +1 if q1 < q2 and q2 > d0: sign = -1 @@ -422,7 +424,7 @@ def get_dQ(ref: Structure, distorted: Structure) -> float: # noqa: N802 np.sum( [ x[0].distance(x[1]) ** 2 * x[0].specie.atomic_mass - for x in zip(ref, distorted) + for x in zip(ref, distorted, strict=True) ], ), ) diff --git a/src/atomate2/common/schemas/elastic.py b/src/atomate2/common/schemas/elastic.py index ddd06ca0db..7f0acabd2f 100644 --- a/src/atomate2/common/schemas/elastic.py +++ b/src/atomate2/common/schemas/elastic.py @@ -218,7 +218,9 @@ def from_stresses( if equilibrium_stress: eq_stress = -0.1 * Stress(equilibrium_stress) - pk_stresses = [s.piola_kirchoff_2(d) for s, d in zip(stresses, deformations)] + pk_stresses = [ + s.piola_kirchoff_2(d) for s, d in zip(stresses, deformations, strict=True) + ] if order is None: order = 2 if len(stresses) < 70 else 3 # TODO: Figure this out better diff --git a/src/atomate2/common/schemas/gruneisen.py b/src/atomate2/common/schemas/gruneisen.py index 3b2bdcfadb..b12f9c78a9 100644 --- a/src/atomate2/common/schemas/gruneisen.py +++ b/src/atomate2/common/schemas/gruneisen.py @@ -310,7 +310,7 @@ def get_gruneisen_weighted_bandstructure( ) for (dists_inx, dists), (_, freqs) in zip( - enumerate(data["distances"]), enumerate(data["frequency"]) + enumerate(data["distances"]), enumerate(data["frequency"]), strict=True ): for band_idx in range(gruneisen_band_symline_plotter.n_bands): ys = [freqs[band_idx][j] * u.factor for j in range(len(dists))] diff --git a/src/atomate2/common/schemas/qha.py b/src/atomate2/common/schemas/qha.py index 1efcdb5658..b0257d2142 100644 --- a/src/atomate2/common/schemas/qha.py +++ b/src/atomate2/common/schemas/qha.py @@ -42,7 +42,7 @@ class PhononQHADoc(StructureMetadata, extra="allow"): # type: ignore[call-arg] ) volume_temperature: Optional[list[float]] = Field( None, - description="Volumes in Angstrom^3 at temperatures." "Shape: (temperatures, )", + description="Volumes in Angstrom^3 at temperatures.Shape: (temperatures, )", ) gibbs_temperature: Optional[list[float]] = Field( None, @@ -51,7 +51,7 @@ class PhononQHADoc(StructureMetadata, extra="allow"): # type: ignore[call-arg] ) bulk_modulus_temperature: Optional[list[float]] = Field( None, - description="Bulk modulus in GPa at temperature." "Shape: (temperatures, )", + description="Bulk modulus in GPa at temperature.Shape: (temperatures, )", ) heat_capacity_p_numerical: Optional[list[float]] = Field( None, @@ -60,7 +60,7 @@ class PhononQHADoc(StructureMetadata, extra="allow"): # type: ignore[call-arg] ) gruneisen_temperature: Optional[list[float]] = Field( None, - description="Gruneisen parameters at temperatures." "Shape: (temperatures, )", + description="Gruneisen parameters at temperatures.Shape: (temperatures, )", ) pressure: Optional[float] = Field( None, description="Pressure in GPA at which Gibb's energy was computed" @@ -147,33 +147,33 @@ def from_phonon_runs( # create some plots here # add kwargs to change the names and file types qha.plot_helmholtz_volume().savefig( - f"{kwargs.get('helmholtz_volume_filename','helmholtz_volume')}" - f".{kwargs.get('plot_type','pdf')}" + f"{kwargs.get('helmholtz_volume_filename', 'helmholtz_volume')}" + f".{kwargs.get('plot_type', 'pdf')}" ) qha.plot_volume_temperature().savefig( - f"{kwargs.get('volume_temperature_plot','volume_temperature')}" - f".{kwargs.get('plot_type','pdf')}" + f"{kwargs.get('volume_temperature_plot', 'volume_temperature')}" + f".{kwargs.get('plot_type', 'pdf')}" ) qha.plot_thermal_expansion().savefig( - f"{kwargs.get('thermal_expansion_plot','thermal_expansion')}" - f".{kwargs.get('plot_type','pdf')}" + f"{kwargs.get('thermal_expansion_plot', 'thermal_expansion')}" + f".{kwargs.get('plot_type', 'pdf')}" ) qha.plot_gibbs_temperature().savefig( f"{kwargs.get('gibbs_temperature_plot', 'gibbs_temperature')}" - f".{kwargs.get('plot_type','pdf')}" + f".{kwargs.get('plot_type', 'pdf')}" ) qha.plot_bulk_modulus_temperature().savefig( f"{kwargs.get('bulk_modulus_plot', 'bulk_modulus_temperature')}" - f".{kwargs.get('plot_type','pdf')}" + f".{kwargs.get('plot_type', 'pdf')}" ) qha.plot_heat_capacity_P_numerical().savefig( f"{kwargs.get('heat_capacity_plot', 'heat_capacity_P_numerical')}" - f".{kwargs.get('plot_type','pdf')}" + f".{kwargs.get('plot_type', 'pdf')}" ) # qha.plot_heat_capacity_P_polyfit().savefig("heat_capacity_P_polyfit.eps") qha.plot_gruneisen_temperature().savefig( f"{kwargs.get('gruneisen_temperature_plot', 'gruneisen_temperature')}" - f".{kwargs.get('plot_type','pdf')}" + f".{kwargs.get('plot_type', 'pdf')}" ) qha.write_helmholtz_volume( diff --git a/src/atomate2/common/utils.py b/src/atomate2/common/utils.py index 4829e0b98c..9110a37f4b 100644 --- a/src/atomate2/common/utils.py +++ b/src/atomate2/common/utils.py @@ -34,7 +34,9 @@ def get_transformations( raise ValueError("Number of transformations and parameters must be the same.") transformation_objects = [] - for transformation, transformation_params in zip(transformations, params): + for transformation, transformation_params in zip( + transformations, params, strict=True + ): found = False for module in ( "advanced_transformations", diff --git a/src/atomate2/cp2k/jobs/base.py b/src/atomate2/cp2k/jobs/base.py index 6be8c4d600..40bfe1083b 100644 --- a/src/atomate2/cp2k/jobs/base.py +++ b/src/atomate2/cp2k/jobs/base.py @@ -4,7 +4,7 @@ from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from jobflow import Maker, Response, job from monty.serialization import dumpfn @@ -31,6 +31,8 @@ from atomate2.cp2k.sets.base import Cp2kInputGenerator if TYPE_CHECKING: + from collections.abc import Callable + from pymatgen.core import Structure diff --git a/src/atomate2/forcefields/jobs.py b/src/atomate2/forcefields/jobs.py index 4c085a41b2..f2307e3a50 100644 --- a/src/atomate2/forcefields/jobs.py +++ b/src/atomate2/forcefields/jobs.py @@ -19,8 +19,8 @@ from atomate2.forcefields.utils import ase_calculator, revert_default_dtype if TYPE_CHECKING: + from collections.abc import Callable from pathlib import Path - from typing import Callable from ase.calculators.calculator import Calculator from pymatgen.core.structure import Structure diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index 33b435ffbc..ae4352669e 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -373,10 +373,10 @@ def from_directory( struct = analyse.structure for _iplot, (ication, labels, cohps) in enumerate( - zip(seq_ineq_cations, seq_labels_cohps, seq_cohps) + zip(seq_ineq_cations, seq_labels_cohps, seq_cohps, strict=True) ): label_str = f"{struct[ication].specie!s}{ication + 1!s}: " - for label, cohp in zip(labels, cohps): + for label, cohp in zip(labels, cohps, strict=True): if label is not None: cba_cohp_plot_data[label_str + label] = cohp @@ -1176,7 +1176,7 @@ def _replace_inf_values(data: Union[dict[Any, Any], list[Any]]) -> None: """ if isinstance(data, dict): for key, value in data.items(): - if isinstance(value, (dict, list)): + if isinstance(value, dict | list): _replace_inf_values( value ) # Recursively process nested dictionaries and lists @@ -1184,7 +1184,7 @@ def _replace_inf_values(data: Union[dict[Any, Any], list[Any]]) -> None: data[key] = "-Infinity" # Replace -inf with a string representation elif isinstance(data, list): for index, item in enumerate(data): - if isinstance(item, (dict, list)): + if isinstance(item, dict | list): _replace_inf_values( item ) # Recursively process nested dictionaries and lists @@ -1280,6 +1280,7 @@ def _get_strong_bonds( bondlist["list_atom2"], bondlist["list_icohp"], bondlist["list_length"], + strict=True, ): bonds.append(f"{a.rstrip('0123456789')}-{b.rstrip('0123456789')}") icohp_all.append(sum(c.values())) diff --git a/src/atomate2/openff/core.py b/src/atomate2/openff/core.py index 9ef7790f3f..97fabd79b2 100644 --- a/src/atomate2/openff/core.py +++ b/src/atomate2/openff/core.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path -from typing import Callable +from typing import TYPE_CHECKING import openff.toolkit as tk from emmet.core.openff import ClassicalMDTaskDocument, MoleculeSpec @@ -16,6 +16,9 @@ from atomate2.openff.utils import create_mol_spec_list, merge_specs_by_name_and_smiles +if TYPE_CHECKING: + from collections.abc import Callable + def openff_job(method: Callable) -> job: """Decorate the ``make`` method of ClassicalMD job makers. diff --git a/src/atomate2/openff/utils.py b/src/atomate2/openff/utils.py index 92838a971f..78d81dfe67 100644 --- a/src/atomate2/openff/utils.py +++ b/src/atomate2/openff/utils.py @@ -190,7 +190,7 @@ def calculate_elyte_composition( salt_mws = {} for smile in salts: mol = tk.Molecule.from_smiles(smile, allow_undefined_stereo=True) - salt_mws[smile] = sum([masses[atom.atomic_number] for atom in mol.atoms]) + salt_mws[smile] = sum(masses[atom.atomic_number] for atom in mol.atoms) # Convert salt mole ratios to mass ratios salt_mass_ratio = { @@ -228,12 +228,13 @@ def counts_from_masses(species: dict[str, float], n_mol: int) -> dict[str, float mol_weights = [] for smile in species: mol = tk.Molecule.from_smiles(smile, allow_undefined_stereo=True) - mol_weights.append(sum([masses[atom.atomic_number] for atom in mol.atoms])) + mol_weights.append(sum(masses[atom.atomic_number] for atom in mol.atoms)) mol_ratio = np.array(list(species.values())) / np.array(mol_weights) mol_ratio /= sum(mol_ratio) return { - smile: int(np.round(ratio * n_mol)) for smile, ratio in zip(species, mol_ratio) + smile: int(np.round(ratio * n_mol)) + for smile, ratio in zip(species, mol_ratio, strict=True) } @@ -258,7 +259,7 @@ def counts_from_box_size( """ masses = {el.Z: el.atomic_mass for el in Element} - na = 6.02214076e23 + na = 6.02214076e23 # Avogadro's number volume = (side_length * 1e-7) ** 3 # Convert from nm3 to cm^3 total_mass = volume * density # grams @@ -266,7 +267,7 @@ def counts_from_box_size( mol_weights = [] for smile in species: mol = tk.Molecule.from_smiles(smile, allow_undefined_stereo=True) - mol_weights.append(sum([masses[atom.atomic_number] for atom in mol.atoms])) + mol_weights.append(sum(masses[atom.atomic_number] for atom in mol.atoms)) mean_mw = np.mean(mol_weights) n_mol = (total_mass / mean_mw) * na @@ -277,7 +278,7 @@ def counts_from_box_size( # Convert moles to number of molecules return { smile: int(np.round(ratio * n_mol)) - for smile, ratio in zip(species.keys(), mol_ratio) + for smile, ratio in zip(species.keys(), mol_ratio, strict=True) } diff --git a/src/atomate2/openmm/jobs/base.py b/src/atomate2/openmm/jobs/base.py index 01ae0bb136..9c190cbafc 100644 --- a/src/atomate2/openmm/jobs/base.py +++ b/src/atomate2/openmm/jobs/base.py @@ -9,7 +9,7 @@ from dataclasses import dataclass, field from datetime import datetime, timezone from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, NoReturn +from typing import TYPE_CHECKING, Any, NoReturn from emmet.core.openmm import ( Calculation, @@ -29,6 +29,8 @@ from atomate2.openmm.utils import increment_name, task_reports if TYPE_CHECKING: + from collections.abc import Callable + from openmm.app.simulation import Simulation @@ -308,7 +310,7 @@ def _add_reporters( if traj_file_type in ["h5md", "nc", "ncdf"]: writer_kwargs["velocities"] = report_velocities writer_kwargs["forces"] = False - elif report_velocities and traj_file_type not in ["trr"]: + elif report_velocities and traj_file_type != "trr": raise ValueError( f"File type {traj_file_type} does not support velocities as" f"of MDAnalysis 2.7.0. Select another file type" @@ -600,7 +602,7 @@ def _create_task_doc( ), completed_at=str(datetime.now(tz=timezone.utc)), task_name=job_name, - calc_type=self.__class__.__name__, + calc_type=type(self).__name__, ) prev_task = prev_task or OpenMMTaskDocument() diff --git a/src/atomate2/openmm/jobs/core.py b/src/atomate2/openmm/jobs/core.py index 821d1953eb..c841818215 100644 --- a/src/atomate2/openmm/jobs/core.py +++ b/src/atomate2/openmm/jobs/core.py @@ -196,7 +196,7 @@ def run_openmm(self, sim: Simulation) -> None: self.starting_temperature, self.temperature, self.temp_steps ) steps = create_list_summing_to(self.n_steps, self.temp_steps) - for temp, n_steps in zip(temps, steps): + for temp, n_steps in zip(temps, steps, strict=True): integrator.setTemperature(temp * kelvin) sim.step(n_steps) diff --git a/src/atomate2/openmm/jobs/generate.py b/src/atomate2/openmm/jobs/generate.py index b32ecc542a..a155af20f2 100644 --- a/src/atomate2/openmm/jobs/generate.py +++ b/src/atomate2/openmm/jobs/generate.py @@ -138,7 +138,7 @@ def assign_partial_charges(self, mol_or_method: tk.Molecule | str) -> None: openff_mol.assign_partial_charges(mol_or_method) mol_or_method = openff_mol self_mol = self.to_openff_molecule() - isomorphic, atom_map = get_atom_map(mol_or_method, self_mol) + _isomorphic, atom_map = get_atom_map(mol_or_method, self_mol) mol_charges = mol_or_method.partial_charges[list(atom_map.values())].magnitude self.partial_charges = mol_charges @@ -251,10 +251,10 @@ def generate_openmm_interchange( "The number of molecule specifications and XML files must match." ) - for mol_spec, xml_mol in zip(mol_specs, xml_mols): + for mol_spec, xml_mol in zip(mol_specs, xml_mols, strict=True): openff_mol = tk.Molecule.from_json(mol_spec.openff_mol) xml_openff_mol = xml_mol.to_openff_molecule() - is_isomorphic, atom_map = get_atom_map(openff_mol, xml_openff_mol) + is_isomorphic, _atom_map = get_atom_map(openff_mol, xml_openff_mol) if not is_isomorphic: raise ValueError( "The mol_specs and ff_xmls must index identical molecules." diff --git a/src/atomate2/qchem/jobs/base.py b/src/atomate2/qchem/jobs/base.py index 4de1d9499e..816771a0f1 100644 --- a/src/atomate2/qchem/jobs/base.py +++ b/src/atomate2/qchem/jobs/base.py @@ -5,7 +5,7 @@ import logging from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from emmet.core.qc_tasks import TaskDoc from jobflow import Maker, Response, job @@ -18,6 +18,8 @@ from atomate2.qchem.sets.base import QCInputGenerator if TYPE_CHECKING: + from collections.abc import Callable + from pymatgen.core.structure import Molecule logger = logging.getLogger(__name__) diff --git a/src/atomate2/utils/file_client.py b/src/atomate2/utils/file_client.py index 0e77aa4c13..47313204d8 100644 --- a/src/atomate2/utils/file_client.py +++ b/src/atomate2/utils/file_client.py @@ -11,13 +11,14 @@ from glob import glob from gzip import GzipFile from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any import paramiko from monty.io import zopen from paramiko import SFTPClient, SSHClient if TYPE_CHECKING: + from collections.abc import Callable from types import TracebackType diff --git a/src/atomate2/vasp/jobs/adsorption.py b/src/atomate2/vasp/jobs/adsorption.py index 328010453d..d1d253cdd0 100644 --- a/src/atomate2/vasp/jobs/adsorption.py +++ b/src/atomate2/vasp/jobs/adsorption.py @@ -402,13 +402,13 @@ def adsorption_calculations( configuration_numbers = [] job_dirs = [] - for i, _ad_structure in enumerate(adslab_structures): + for idx in range(len(adslab_structures)): ads_energy = ( - adslabs_data["static_energy"][i] - molecule_dft_energy - slab_dft_energy + adslabs_data["static_energy"][idx] - molecule_dft_energy - slab_dft_energy ) adsorption_energies.append(ads_energy) - configuration_numbers.append(i) - job_dirs.append(adslabs_data["dirs"][i]) + configuration_numbers.append(idx) + job_dirs.append(adslabs_data["dirs"][idx]) # Sort the data by adsorption energy sorted_indices = sorted( diff --git a/src/atomate2/vasp/jobs/base.py b/src/atomate2/vasp/jobs/base.py index c41b16a390..188ed20995 100644 --- a/src/atomate2/vasp/jobs/base.py +++ b/src/atomate2/vasp/jobs/base.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field from pathlib import Path from shutil import which -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from emmet.core.tasks import TaskDoc from jobflow import Maker, Response, job @@ -26,6 +26,8 @@ from atomate2.vasp.sets.base import VaspInputGenerator if TYPE_CHECKING: + from collections.abc import Callable + from pymatgen.core import Structure diff --git a/src/atomate2/vasp/jobs/elph.py b/src/atomate2/vasp/jobs/elph.py index 1cc05fbc4c..76cb9781a0 100644 --- a/src/atomate2/vasp/jobs/elph.py +++ b/src/atomate2/vasp/jobs/elph.py @@ -154,7 +154,7 @@ def run_elph_displacements( "uuids": [], "dirs": [], } - for temp, structure in zip(temperatures, structures): + for temp, structure in zip(temperatures, structures, strict=True): # create the job elph_job = vasp_maker.make(structure, prev_dir=prev_dir) elph_job.append_name(f" T={temp}") diff --git a/tests/common/schemas/test_qha.py b/tests/common/schemas/test_qha.py index 1552b55240..a3da77eecd 100644 --- a/tests/common/schemas/test_qha.py +++ b/tests/common/schemas/test_qha.py @@ -54,7 +54,7 @@ def test_analyze_free_energy(tmp_dir, test_dir): energies.append(float(e)) phonon_docs = [] - for index, energy, volume in zip(range(-5, 6), energies, volumes): + for index, energy, volume in zip(range(-5, 6), energies, volumes, strict=True): filename = f"{test_dir}/qha/thermal_properties.yaml-{index!s}" yaml = YAML() with open(filename) as f: @@ -131,7 +131,7 @@ def test_analyze_free_energy_small(tmp_dir, test_dir): energies.append(float(e)) phonon_docs = [] - for index, energy, volume in zip(range(-5, 6), energies, volumes): + for index, energy, volume in zip(range(-5, 6), energies, volumes, strict=True): filename = f"{test_dir}/qha/thermal_properties.yaml-{index!s}" yaml = YAML() with open(filename) as f: diff --git a/tests/forcefields/test_md.py b/tests/forcefields/test_md.py index f2c5c08cc9..2b11d0686e 100644 --- a/tests/forcefields/test_md.py +++ b/tests/forcefields/test_md.py @@ -1,5 +1,6 @@ """Tests for forcefield MD flows.""" +import sys from pathlib import Path import numpy as np @@ -53,6 +54,11 @@ def test_maker_initialization(): def test_ml_ff_md_maker( ff_name, si_structure, sr_ti_o3_structure, al2_au_structure, test_dir, clean_dir ): + if ff_name == "GAP" and sys.version_info >= (3, 12): + pytest.skip( + "GAP model not compatible with Python 3.12, waiting on https://github.com/libAtoms/QUIP/issues/645" + ) + n_steps = 5 ref_energies_per_atom = { diff --git a/tests/qchem/conftest.py b/tests/qchem/conftest.py index e8ced094f7..80dbdf49d7 100644 --- a/tests/qchem/conftest.py +++ b/tests/qchem/conftest.py @@ -3,7 +3,7 @@ import logging import shutil from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Final, Literal +from typing import TYPE_CHECKING, Any, Final, Literal import pytest from jobflow import CURRENT_JOB @@ -17,7 +17,7 @@ from atomate2.qchem.sets.base import QCInputGenerator if TYPE_CHECKING: - from collections.abc import Generator, Sequence + from collections.abc import Callable, Generator, Sequence logger = logging.getLogger("atomate2") diff --git a/tests/qchem/flows/test_core.py b/tests/qchem/flows/test_core.py index 2fadffcb00..0b05657dc4 100644 --- a/tests/qchem/flows/test_core.py +++ b/tests/qchem/flows/test_core.py @@ -46,7 +46,7 @@ def test_frequency_opt_flattening_maker( mock_qchem, clean_dir, qchem_test_dir, h2o_molecule ): ref_paths = { - k: Path(qchem_test_dir) / "ffopt" / f"{k.lower().replace(' ','_')}" + k: Path(qchem_test_dir) / "ffopt" / f"{k.lower().replace(' ', '_')}" for k in ("Geometry Optimization", "Frequency Analysis 1") } diff --git a/tests/vasp/conftest.py b/tests/vasp/conftest.py index 096f769aa8..eb4137eea5 100644 --- a/tests/vasp/conftest.py +++ b/tests/vasp/conftest.py @@ -3,7 +3,7 @@ import logging import shutil from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Final, Literal +from typing import TYPE_CHECKING, Any, Final, Literal import pytest from jobflow import CURRENT_JOB @@ -20,7 +20,7 @@ from atomate2.vasp.sets.base import VaspInputGenerator if TYPE_CHECKING: - from collections.abc import Generator, Sequence + from collections.abc import Callable, Generator, Sequence logger = logging.getLogger("atomate2") diff --git a/tests/vasp/flows/test_eos.py b/tests/vasp/flows/test_eos.py index 2b879b6d65..c0f4d79d46 100644 --- a/tests/vasp/flows/test_eos.py +++ b/tests/vasp/flows/test_eos.py @@ -180,5 +180,5 @@ def test_mp_eos_maker( assert all( approx(v) == data[k] for k, v in ref_eos_fit[job_type][key].items() ) - elif isinstance(data, (float, int)): + elif isinstance(data, float | int): assert approx(ref_eos_fit[job_type][key]) == data diff --git a/tests/vasp/schemas/test_defect.py b/tests/vasp/schemas/test_defect.py index 61350dd398..cedad2e0c0 100644 --- a/tests/vasp/schemas/test_defect.py +++ b/tests/vasp/schemas/test_defect.py @@ -31,11 +31,11 @@ def is_strict_minimum(min_index, arr): inputs1 = [ (task.output.structure, task.output.energy, sdir) - for task, sdir in zip(static_tasks1, static_dirs1) + for task, sdir in zip(static_tasks1, static_dirs1, strict=True) ] inputs2 = [ (task.output.structure, task.output.energy, sdir) - for task, sdir in zip(static_tasks2, static_dirs2) + for task, sdir in zip(static_tasks2, static_dirs2, strict=True) ] input_dict = defaultdict(list) diff --git a/tests/vasp/sets/test_matpes.py b/tests/vasp/sets/test_matpes.py index 06ee5fa0eb..f29b31c870 100644 --- a/tests/vasp/sets/test_matpes.py +++ b/tests/vasp/sets/test_matpes.py @@ -42,7 +42,7 @@ def test_matpes_sets(set_generator: VaspInputGenerator) -> None: "vdw", } assert matpes_set.potcar_functional == "PBE_64" - assert isinstance(matpes_set.inherit_incar, (list, tuple)) + assert isinstance(matpes_set.inherit_incar, list | tuple) assert set(matpes_set.inherit_incar) == set(MatPESStaticSet.inherit_incar) assert matpes_set.auto_ismear is False assert matpes_set.auto_kspacing is False