From cef8cdd1aca14bc11ca6fd965d3301fe5d2d3b84 Mon Sep 17 00:00:00 2001 From: BryantLi-BLI Date: Sat, 28 Mar 2026 01:17:14 +0000 Subject: [PATCH] Auto-update pre-commit hooks --- .pre-commit-config.yaml | 12 +- docs/user/codes/openmm.md | 2 - docs/user/codes/vasp.md | 9 +- src/atomate2/abinit/schemas/calculation.py | 18 +-- src/atomate2/abinit/schemas/task.py | 48 +++--- src/atomate2/aims/schemas/calculation.py | 28 ++-- src/atomate2/aims/schemas/task.py | 50 +++---- src/atomate2/cli/dev.py | 4 +- src/atomate2/common/jobs/mpmorph.py | 5 +- src/atomate2/common/schemas/anharmonicity.py | 12 +- src/atomate2/common/schemas/cclib.py | 28 ++-- src/atomate2/common/schemas/defects.py | 58 ++++---- src/atomate2/common/schemas/elastic.py | 73 +++++----- src/atomate2/common/schemas/gruneisen.py | 26 ++-- src/atomate2/common/schemas/magnetism.py | 92 ++++++------ src/atomate2/common/schemas/phonons.py | 72 +++++---- src/atomate2/common/schemas/qha.py | 38 ++--- src/atomate2/cp2k/schemas/calculation.py | 28 ++-- src/atomate2/cp2k/schemas/task.py | 54 ++++--- src/atomate2/forcefields/schemas.py | 18 +-- src/atomate2/forcefields/utils.py | 3 +- src/atomate2/lobster/schemas.py | 145 +++++++++---------- src/atomate2/settings.py | 18 +-- src/atomate2/utils/datetime.py | 2 +- src/atomate2/vasp/schemas/defect.py | 6 +- tests/abinit/conftest.py | 6 +- 26 files changed, 414 insertions(+), 441 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 44b0ec977e..52360fdf75 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,14 +3,14 @@ default_language_version: exclude: ^(.github/|tests/test_data/abinit/) repos: - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.9.3 + rev: v0.15.8 hooks: - id: ruff args: [--fix] exclude: tutorials/grueneisen_workflow.ipynb - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-yaml - id: fix-encoding-pragma @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/blacken-docs - rev: 1.19.1 + rev: 1.20.0 hooks: - id: blacken-docs additional_dependencies: [black] @@ -31,7 +31,7 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 + rev: v1.19.1 hooks: - id: mypy files: ^src/ @@ -39,14 +39,14 @@ repos: - tokenize-rt==4.1.0 - types-paramiko - repo: https://github.com/codespell-project/codespell - rev: v2.4.0 + rev: v2.4.2 hooks: - id: codespell stages: [pre-commit, commit-msg] args: [--ignore-words-list, 'titel,statics,ba,nd,te,atomate'] types_or: [python, rst, markdown] - repo: https://github.com/kynan/nbstripout - rev: 0.8.1 + rev: 0.9.1 hooks: - id: nbstripout args: diff --git a/docs/user/codes/openmm.md b/docs/user/codes/openmm.md index 26a89f6817..dd20f70928 100644 --- a/docs/user/codes/openmm.md +++ b/docs/user/codes/openmm.md @@ -85,7 +85,6 @@ for PF6- here, the built in partial charge method fails. import numpy as np from pymatgen.core.structure import Molecule - pf6 = Molecule( ["P", "F", "F", "F", "F", "F", "F"], [ @@ -142,7 +141,6 @@ from atomate2.openmm.jobs.core import ( ) from jobflow import Flow, run_locally - production_maker = OpenMMFlowMaker( name="production_flow", makers=[ diff --git a/docs/user/codes/vasp.md b/docs/user/codes/vasp.md index 43d84ffe62..f8d2f26032 100644 --- a/docs/user/codes/vasp.md +++ b/docs/user/codes/vasp.md @@ -300,7 +300,6 @@ A Grüneisen workflow for VASP can be started as follows: from atomate2.vasp.flows.gruneisen import GruneisenMaker from pymatgen.core.structure import Structure - structure = Structure( lattice=[[0, 2.13, 2.13], [2.13, 0, 2.13], [2.13, 2.13, 0]], species=["Mg", "O"], @@ -324,7 +323,6 @@ The following script allows you to start the default workflow for VASP with some from atomate2.vasp.flows.qha import QhaMaker from pymatgen.core.structure import Structure - structure = Structure( lattice=[[0, 2.13, 2.13], [2.13, 0, 2.13], [2.13, 2.13, 0]], species=["Mg", "O"], @@ -349,7 +347,6 @@ You can start the workflow as follows: from atomate2.vasp.flows.eos import EosMaker from pymatgen.core.structure import Structure - structure = Structure( lattice=[[0, 2.13, 2.13], [2.13, 0, 2.13], [2.13, 2.13, 0]], species=["Mg", "O"], @@ -651,13 +648,11 @@ gamma_only_static_maker = StaticMaker(input_set_generator=custom_gamma_only_set) For those who are more familiar with manual *k*-point generation, you can use a VASP-style KPOINTS file or string to set the *k*-points as well: ```py -kpoints = Kpoints.from_str( - """Uniform density Monkhorst-Pack mesh +kpoints = Kpoints.from_str("""Uniform density Monkhorst-Pack mesh 0 Monkhorst-pack 5 5 5 -""" -) +""") custom_static_set = StaticSetGenerator(user_kpoints_settings=kpoints) ``` diff --git a/src/atomate2/abinit/schemas/calculation.py b/src/atomate2/abinit/schemas/calculation.py index 15b8cff682..9704196299 100644 --- a/src/atomate2/abinit/schemas/calculation.py +++ b/src/atomate2/abinit/schemas/calculation.py @@ -6,7 +6,7 @@ import os from datetime import datetime, timezone from pathlib import Path -from typing import Optional, Union +from typing import Union from abipy.electrons.gsr import GsrFile from abipy.flowtk import events @@ -82,23 +82,23 @@ class CalculationOutput(BaseModel): None, description="The Fermi level from the calculation in eV" ) - forces: Optional[list[Vector3D]] = Field( + forces: list[Vector3D] | None = Field( None, description="Forces acting on each atom" ) - stress: Optional[Matrix3D] = Field(None, description="The stress on the cell") - is_metal: Optional[bool] = Field(None, description="Whether the system is metallic") - bandgap: Optional[float] = Field( + stress: Matrix3D | None = Field(None, description="The stress on the cell") + is_metal: bool | None = Field(None, description="Whether the system is metallic") + bandgap: float | None = Field( None, description="The band gap from the calculation in eV" ) - direct_bandgap: Optional[float] = Field( + direct_bandgap: float | None = Field( None, description="The direct band gap from the calculation in eV" ) - cbm: Optional[float] = Field( + cbm: float | None = Field( None, description="The conduction band minimum, or LUMO for molecules, in eV " "(if system is not metallic)", ) - vbm: Optional[float] = Field( + vbm: float | None = Field( None, description="The valence band maximum, or HOMO for molecules, in eV " "(if system is not metallic)", @@ -194,7 +194,7 @@ class Calculation(BaseModel): event_report: events.EventReport = Field( None, description="Event report of this abinit job." ) - output_file_paths: Optional[dict[str, str]] = Field( + output_file_paths: dict[str, str] | None = Field( None, description="Paths (relative to dir_name) of the Abinit output files " "associated with this calculation", diff --git a/src/atomate2/abinit/schemas/task.py b/src/atomate2/abinit/schemas/task.py index 07a66d3fa8..3974f4722f 100644 --- a/src/atomate2/abinit/schemas/task.py +++ b/src/atomate2/abinit/schemas/task.py @@ -5,7 +5,7 @@ import logging from collections.abc import Sequence from pathlib import Path -from typing import Any, Optional, Union +from typing import Any, Union from abipy.abio.inputs import AbinitInput from abipy.flowtk import events @@ -89,7 +89,7 @@ class OutputDoc(BaseModel): """ structure: Union[Structure] = Field(None, description="The output structure object") - trajectory: Optional[Sequence[Union[Structure]]] = Field( + trajectory: Sequence[Union[Structure]] | None = Field( None, description="The trajectory of output structures" ) energy: float = Field( @@ -98,15 +98,15 @@ class OutputDoc(BaseModel): energy_per_atom: float = Field( None, description="The final DFT energy per atom for the last calculation" ) - bandgap: Optional[float] = Field( + bandgap: float | None = Field( None, description="The DFT bandgap for the last calculation" ) - cbm: Optional[float] = Field(None, description="CBM for this calculation") - vbm: Optional[float] = Field(None, description="VBM for this calculation") - forces: Optional[list[Vector3D]] = Field( + cbm: float | None = Field(None, description="CBM for this calculation") + vbm: float | None = Field(None, description="VBM for this calculation") + forces: list[Vector3D] | None = Field( None, description="Forces on atoms from the last calculation" ) - stress: Optional[Matrix3D] = Field( + stress: Matrix3D | None = Field( None, description="Stress on the unit cell from the last calculation" ) @@ -179,49 +179,47 @@ class AbinitTaskDoc(StructureMetadata): Additional json loaded from the calculation directory """ - dir_name: Optional[str] = Field( - None, description="The directory for this Abinit task" - ) - last_updated: Optional[str] = Field( + dir_name: str | None = Field(None, description="The directory for this Abinit task") + last_updated: str | None = Field( default_factory=datetime_str, description="Timestamp for when this task document was last updated", ) - completed_at: Optional[str] = Field( + completed_at: str | None = Field( None, description="Timestamp for when this task was completed" ) - input: Optional[InputDoc] = Field( + input: InputDoc | None = Field( None, description="The input to the first calculation" ) - output: Optional[OutputDoc] = Field( + output: OutputDoc | None = Field( None, description="The output of the final calculation" ) structure: Union[Structure] = Field( None, description="Final output atoms from the task" ) - state: Optional[TaskState] = Field(None, description="State of this task") - event_report: Optional[events.EventReport] = Field( + state: TaskState | None = Field(None, description="State of this task") + event_report: events.EventReport | None = Field( None, description="Event report of this abinit job." ) - included_objects: Optional[list[AbinitObject]] = Field( + included_objects: list[AbinitObject] | None = Field( None, description="List of Abinit objects included with this task document" ) - abinit_objects: Optional[dict[AbinitObject, Any]] = Field( + abinit_objects: dict[AbinitObject, Any] | None = Field( None, description="Abinit objects associated with this task" ) - task_label: Optional[str] = Field(None, description="A description of the task") - tags: Optional[list[str]] = Field( + task_label: str | None = Field(None, description="A description of the task") + tags: list[str] | None = Field( None, description="Metadata tags for this task document" ) - author: Optional[str] = Field( + author: str | None = Field( None, description="Author extracted from transformations" ) - icsd_id: Optional[str] = Field( + icsd_id: str | None = Field( None, description="International crystal structure database id of the structure" ) - calcs_reversed: Optional[list[Calculation]] = Field( + calcs_reversed: list[Calculation] | None = Field( None, description="The inputs and outputs for all Abinit runs in this task." ) - transformations: Optional[dict[str, Any]] = Field( + transformations: dict[str, Any] | None = Field( None, description="Information on the structural transformations, parsed from a " "transformations.json file", @@ -231,7 +229,7 @@ class AbinitTaskDoc(StructureMetadata): description="Information on the custodian settings used to run this " "calculation, parsed from a custodian.json file", ) - additional_json: Optional[dict[str, Any]] = Field( + additional_json: dict[str, Any] | None = Field( None, description="Additional json loaded from the calculation directory" ) diff --git a/src/atomate2/aims/schemas/calculation.py b/src/atomate2/aims/schemas/calculation.py index 92903032c6..81f2e9d459 100644 --- a/src/atomate2/aims/schemas/calculation.py +++ b/src/atomate2/aims/schemas/calculation.py @@ -7,7 +7,7 @@ from collections.abc import Sequence from datetime import datetime, timezone from pathlib import Path -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any import numpy as np from ase.spectrum.band_structure import BandStructure @@ -98,24 +98,24 @@ class CalculationOutput(BaseModel): None, description="The final structure from the calculation" ) - efermi: Optional[float] = Field( + efermi: float | None = Field( None, description="The Fermi level from the calculation in eV" ) - forces: Optional[list[Vector3D]] = Field( + forces: list[Vector3D] | None = Field( None, description="Forces acting on each atom" ) - all_forces: Optional[list[list[Vector3D]]] = Field( + all_forces: list[list[Vector3D]] | None = Field( None, description="Forces acting on each atom for each structure in the output file", ) - stress: Optional[Matrix3D] = Field(None, description="The stress on the cell") - stresses: Optional[list[Matrix3D]] = Field( + stress: Matrix3D | None = Field(None, description="The stress on the cell") + stresses: list[Matrix3D] | None = Field( None, description="The atomic virial stresses" ) - is_metal: Optional[bool] = Field(None, description="Whether the system is metallic") - bandgap: Optional[float] = Field( + is_metal: bool | None = Field(None, description="Whether the system is metallic") + bandgap: float | None = Field( None, description="The band gap from the calculation in eV" ) cbm: float = Field( @@ -123,7 +123,7 @@ class CalculationOutput(BaseModel): description="The conduction band minimum, or LUMO for molecules, in eV " "(if system is not metallic)", ) - vbm: Optional[float] = Field( + vbm: float | None = Field( None, description="The valence band maximum, or HOMO for molecules, in eV " "(if system is not metallic)", @@ -264,7 +264,7 @@ def from_aims_files( parse_dos: str | bool = False, parse_bandstructure: str | bool = False, store_trajectory: bool = False, - store_volumetric_data: Optional[Sequence[str]] = STORE_VOLUMETRIC_DATA, + store_volumetric_data: Sequence[str] | None = STORE_VOLUMETRIC_DATA, ) -> tuple[Self, dict[AimsObject, dict]]: """Create an FHI-aims calculation document from a directory and file paths. @@ -393,7 +393,7 @@ def _get_output_file_paths(volumetric_files: list[str]) -> dict[AimsObject, str] def _get_volumetric_data( dir_name: Path, output_file_paths: dict[AimsObject, str], - store_volumetric_data: Optional[Sequence[str]], + store_volumetric_data: Sequence[str] | None, ) -> dict[AimsObject, VolumetricData]: """ Load volumetric data files from a directory. @@ -430,7 +430,7 @@ def _get_volumetric_data( return volumetric_data -def _parse_dos(parse_dos: str | bool, aims_output: AimsOutput) -> Optional[Dos]: +def _parse_dos(parse_dos: str | bool, aims_output: AimsOutput) -> Dos | None: """Parse DOS outputs from FHI-aims calculation. Parameters @@ -458,7 +458,7 @@ def _parse_dos(parse_dos: str | bool, aims_output: AimsOutput) -> Optional[Dos]: def _parse_bandstructure( parse_bandstructure: str | bool, aims_output: AimsOutput -) -> Optional[BandStructure]: +) -> BandStructure | None: """ Get the band structure. @@ -478,7 +478,7 @@ def _parse_bandstructure( return None -def _parse_trajectory(aims_output: AimsOutput) -> Optional[Trajectory]: +def _parse_trajectory(aims_output: AimsOutput) -> Trajectory | None: """Grab a Trajectory object given an FHI-aims output object. Parameters diff --git a/src/atomate2/aims/schemas/task.py b/src/atomate2/aims/schemas/task.py index 8743492df5..d16ab28493 100644 --- a/src/atomate2/aims/schemas/task.py +++ b/src/atomate2/aims/schemas/task.py @@ -6,7 +6,7 @@ import logging from collections.abc import Sequence from pathlib import Path -from typing import Any, Optional, Union +from typing import Any, Union import numpy as np from emmet.core.math import Matrix3D, Vector3D @@ -41,12 +41,12 @@ class AnalysisDoc(BaseModel): Errors from the FHI-aims output """ - delta_volume: Optional[float] = Field(None, description="Absolute change in volume") - delta_volume_as_percent: Optional[float] = Field( + delta_volume: float | None = Field(None, description="Absolute change in volume") + delta_volume_as_percent: float | None = Field( None, description="Percentage change in volume" ) - max_force: Optional[float] = Field(None, description="Maximum force on the atoms") - errors: Optional[list[str]] = Field( + max_force: float | None = Field(None, description="Maximum force on the atoms") + errors: list[str] | None = Field( None, description="Errors from the FHI-aims output" ) @@ -104,7 +104,7 @@ class InputDoc(BaseModel): xc: str = Field( None, description="Exchange-correlation functional used if not the default" ) - magnetic_moments: Optional[list[float]] = Field( + magnetic_moments: list[float] | None = Field( None, description="Magnetic moments for each atom" ) @@ -174,18 +174,18 @@ class OutputDoc(BaseModel): energy_per_atom: float = Field( None, description="The final DFT energy per atom for the last calculation" ) - bandgap: Optional[float] = Field( + bandgap: float | None = Field( None, description="The DFT bandgap for the last calculation" ) - cbm: Optional[float] = Field(None, description="CBM for this calculation") - vbm: Optional[float] = Field(None, description="VBM for this calculation") - forces: Optional[list[Vector3D]] = Field( + cbm: float | None = Field(None, description="CBM for this calculation") + vbm: float | None = Field(None, description="VBM for this calculation") + forces: list[Vector3D] | None = Field( None, description="Forces on atoms from the last calculation" ) - stress: Optional[Matrix3D] = Field( + stress: Matrix3D | None = Field( None, description="Stress on the unit cell from the last calculation" ) - all_forces: Optional[list[list[Vector3D]]] = Field( + all_forces: list[list[Vector3D]] | None = Field( None, description="Forces on atoms from all calculations." ) @@ -258,7 +258,7 @@ class ConvergenceSummary(BaseModel): None, description="The last value of the input setting to study convergence against", ) - asked_epsilon: Optional[float] = Field( + asked_epsilon: float | None = Field( None, description="The difference in the values for the convergence criteria that was" " asked for", @@ -405,7 +405,7 @@ class AimsTaskDoc(BaseTaskDocument, StructureMetadata, MoleculeMetadata): completed_at: str = Field( None, description="Timestamp for when this task was completed" ) - input: Optional[InputDoc] = Field( + input: InputDoc | None = Field( None, description="The input to the first calculation" ) output: OutputDoc = Field(None, description="The output of the final calculation") @@ -413,32 +413,32 @@ class AimsTaskDoc(BaseTaskDocument, StructureMetadata, MoleculeMetadata): None, description="Final output atoms from the task" ) state: TaskState = Field(None, description="State of this task") - included_objects: Optional[list[AimsObject]] = Field( + included_objects: list[AimsObject] | None = Field( None, description="List of FHI-aims objects included with this task document" ) - aims_objects: Optional[dict[AimsObject, Any]] = Field( + aims_objects: dict[AimsObject, Any] | None = Field( None, description="FHI-aims objects associated with this task" ) - entry: Optional[ComputedEntry] = Field( + entry: ComputedEntry | None = Field( None, description="The ComputedEntry from the task doc" ) analysis: AnalysisDoc = Field( None, description="Summary of structural relaxation and forces" ) task_label: str = Field(None, description="A description of the task") - tags: Optional[list[str]] = Field( + tags: list[str] | None = Field( None, description="Metadata tags for this task document" ) - author: Optional[str] = Field( + author: str | None = Field( None, description="Author extracted from transformations" ) - icsd_id: Optional[str] = Field( + icsd_id: str | None = Field( None, description="International crystal structure database id of the structure" ) - calcs_reversed: Optional[list[Calculation]] = Field( + calcs_reversed: list[Calculation] | None = Field( None, description="The inputs and outputs for all FHI-aims runs in this task." ) - transformations: Optional[dict[str, Any]] = Field( + transformations: dict[str, Any] | None = Field( None, description="Information on the structural transformations, parsed from a " "transformations.json file", @@ -448,7 +448,7 @@ class AimsTaskDoc(BaseTaskDocument, StructureMetadata, MoleculeMetadata): description="Information on the custodian settings used to run this " "calculation, parsed from a custodian.json file", ) - additional_json: Optional[dict[str, Any]] = Field( + additional_json: dict[str, Any] | None = Field( None, description="Additional json loaded from the calculation directory" ) @@ -534,7 +534,7 @@ def from_directory( @staticmethod def get_entry( - calc_docs: list[Calculation], job_id: Optional[str] = None + calc_docs: list[Calculation], job_id: str | None = None ) -> ComputedEntry: """Get a computed entry from a list of FHI-aims calculation documents. @@ -652,7 +652,7 @@ def _get_task_files( return task_files -def _get_max_force(calc_doc: Calculation) -> Optional[float]: +def _get_max_force(calc_doc: Calculation) -> float | None: """Get max force acting on atoms from a calculation document. Parameters diff --git a/src/atomate2/cli/dev.py b/src/atomate2/cli/dev.py index d613706773..aa171c8329 100644 --- a/src/atomate2/cli/dev.py +++ b/src/atomate2/cli/dev.py @@ -603,13 +603,13 @@ def save_abinit_maker(maker: Maker) -> None: author_mail = None if git: name = subprocess.run( - "git config user.name".split(), + ["git", "config", "user.name"], capture_output=True, encoding="utf-8", check=True, ) mail = subprocess.run( - "git config user.email".split(), + ["git", "config", "user.email"], capture_output=True, encoding="utf-8", check=True, diff --git a/src/atomate2/common/jobs/mpmorph.py b/src/atomate2/common/jobs/mpmorph.py index 4e87182b8a..64987b16e7 100644 --- a/src/atomate2/common/jobs/mpmorph.py +++ b/src/atomate2/common/jobs/mpmorph.py @@ -47,8 +47,7 @@ def _get_average_volumes_file( stream_data = requests.get(_DEFAULT_AVG_VOL_URL, stream=True, timeout=timeout) with open(str(_DEFAULT_AVG_VOL_FILE), "wb") as file: - for chunk in stream_data.iter_content(chunk_size=chunk_size): - file.write(chunk) + file.writelines(stream_data.iter_content(chunk_size=chunk_size)) return pd.read_json(_DEFAULT_AVG_VOL_FILE, orient="split") @@ -261,7 +260,7 @@ def get_entry_from_dict(chem_env: str) -> dict | None: for ielt in range(2, len(composition)): for combo in combinations(composition, ielt): chem_env_key = _get_chem_env_key_from_composition( - Composition({spec: 1 for spec in combo}), + Composition(dict.fromkeys(combo, 1)), ignore_oxi_states=ignore_oxi_states, ) diff --git a/src/atomate2/common/schemas/anharmonicity.py b/src/atomate2/common/schemas/anharmonicity.py index 5a8e41f622..d655410840 100644 --- a/src/atomate2/common/schemas/anharmonicity.py +++ b/src/atomate2/common/schemas/anharmonicity.py @@ -1,7 +1,7 @@ """Schemas for anharmonicity quantification.""" import logging -from typing import Any, Optional +from typing import Any from emmet.core.math import Matrix3D from emmet.core.structure import StructureMetadata @@ -16,13 +16,13 @@ class AnharmonicityDoc(StructureMetadata): """Collection to store data from anharmonicity workflow.""" - phonon_doc: Optional[PhononBSDOSDoc] = Field( + phonon_doc: PhononBSDOSDoc | None = Field( None, description="Collection of data from phonon part of the workflow" ) supercell_matrix: Matrix3D = Field("Matrix describing the supercell") - structure: Optional[Structure] = Field( + structure: Structure | None = Field( None, description="Structure of Materials Project." ) @@ -30,11 +30,11 @@ class AnharmonicityDoc(StructureMetadata): "matrix describing relationship to primitive cell" ) - sigma_dict: Optional[dict[str, Any]] = Field( + sigma_dict: dict[str, Any] | None = Field( None, description="Dictionary with all computed sigma^A forms" ) - parameters_dict: Optional[dict] = Field( + parameters_dict: dict | None = Field( None, description="Parameters used for anharmonicity quantification" ) @@ -46,7 +46,7 @@ def from_phonon_doc_sigma( one_shot: bool, temp: float, n_samples: int, - seed: Optional[int], + seed: int | None, ) -> "AnharmonicityDoc": """ Generate the collection of data for the anharmonicity workflow. diff --git a/src/atomate2/common/schemas/cclib.py b/src/atomate2/common/schemas/cclib.py index d6d681df4d..dc3bb87252 100644 --- a/src/atomate2/common/schemas/cclib.py +++ b/src/atomate2/common/schemas/cclib.py @@ -3,7 +3,7 @@ import logging import os from pathlib import Path -from typing import Any, Optional, Union +from typing import Any, Union from emmet.core.structure import MoleculeMetadata from monty.dev import requires @@ -34,26 +34,26 @@ class TaskDocument(MoleculeMetadata, extra="allow"): # type: ignore[call-arg] For the list of supported packages, see https://cclib.github.io """ - molecule: Optional[Molecule] = Field( + molecule: Molecule | None = Field( None, description="Final output molecule from the task" ) - energy: Optional[float] = Field(None, description="Final total energy") - dir_name: Optional[str] = Field( + energy: float | None = Field(None, description="Final total energy") + dir_name: str | None = Field( None, description="Directory where the output is parsed" ) - logfile: Optional[str] = Field( + logfile: str | None = Field( None, description="Path to the log file used in the post-processing analysis" ) - attributes: Optional[dict] = Field( + attributes: dict | None = Field( None, description="Computed properties and calculation outputs" ) - metadata: Optional[dict] = Field( + metadata: dict | None = Field( None, description="Calculation metadata, including input parameters and runtime " "statistics", ) - task_label: Optional[str] = Field(None, description="A description of the task") - tags: Optional[list[str]] = Field( + task_label: str | None = Field(None, description="A description of the task") + tags: list[str] | None = Field( None, description="Optional tags for this task document" ) last_updated: str = Field( @@ -71,9 +71,9 @@ def from_logfile( dir_name: Union[str, Path], logfile_extensions: Union[str, list[str]], store_trajectory: bool = False, - additional_fields: Optional[dict[str, Any]] = None, - analysis: Optional[Union[str, list[str]]] = None, - proatom_dir: Optional[Union[Path, str]] = None, + additional_fields: dict[str, Any] | None = None, + analysis: Union[str, list[str]] | None = None, + proatom_dir: Union[Path, str] | None = None, ) -> Self: """Create a TaskDocument from a log file. @@ -259,7 +259,7 @@ def cclib_calculate( method: str, cube_file: Union[Path, str], proatom_dir: Union[Path, str], -) -> Optional[dict[str, Any]]: +) -> dict[str, Any] | None: """ Run a cclib population analysis. @@ -353,7 +353,7 @@ def cclib_calculate( def _get_homos_lumos( mo_energies: list[list[float]], homo_indices: list[int] -) -> tuple[list[float], Optional[list[float]], Optional[list[float]]]: +) -> tuple[list[float], list[float] | None, list[float] | None]: """ Calculate the HOMO, LUMO, and HOMO-LUMO gap energies in eV. diff --git a/src/atomate2/common/schemas/defects.py b/src/atomate2/common/schemas/defects.py index 0c79a6ad56..a718b1a86d 100644 --- a/src/atomate2/common/schemas/defects.py +++ b/src/atomate2/common/schemas/defects.py @@ -3,7 +3,7 @@ import logging from collections.abc import Callable, Sequence from itertools import starmap -from typing import Any, Optional, Union +from typing import Any, Union import numpy as np from emmet.core.tasks import TaskDoc @@ -25,52 +25,52 @@ class FormationEnergyDiagramDocument(BaseModel): will not necessarily have all the entries in the phase diagram computed. """ - bulk_entry: Optional[ComputedStructureEntry] = Field( + bulk_entry: ComputedStructureEntry | None = Field( None, description="The ComputedEntry representing the bulk structure.", ) - defect_entries: Optional[list[DefectEntry]] = Field( + defect_entries: list[DefectEntry] | None = Field( None, description="The defect entries for the formation energy diagram.", ) - pd_entries: Optional[list[ComputedEntry]] = Field( + pd_entries: list[ComputedEntry] | None = Field( None, description="The entries used to construct the phase diagram.", ) - vbm: Optional[float] = Field( + vbm: float | None = Field( None, description="The VBM of the pristine supercell calculation.", ) - band_gap: Optional[float] = Field( + band_gap: float | None = Field( None, description="The band gap of the pristine supercell calculation.", ) - inc_inf_values: Optional[bool] = Field( + inc_inf_values: bool | None = Field( None, description="Whether or not to include infinite values in the diagram.", ) - defect: Optional[Defect] = Field( + defect: Defect | None = Field( None, description="The defect for which the diagram is being calculated.", ) - bulk_sc_dir: Optional[str] = Field( + bulk_sc_dir: str | None = Field( None, description="The directory name of the pristine supercell calculation.", ) - defect_sc_dirs: Optional[dict[int, str]] = Field( + defect_sc_dirs: dict[int, str] | None = Field( None, description="The directory names of the charged defect calculations.", ) - dielectric: Optional[Union[float, list[list[float]]]] = Field( + dielectric: Union[float, list[list[float]]] | None = Field( None, description="The dielectric constant or tensor, can be used to compute " "finite-size corrections.", @@ -102,7 +102,7 @@ def from_formation_energy_diagram( def as_formation_energy_diagram( self, - pd_entries: Optional[list[ComputedEntry]] = None, + pd_entries: list[ComputedEntry] | None = None, ) -> FormationEnergyDiagram: """Create a `FormationEnergyDiagram` object from the document. @@ -128,65 +128,65 @@ def as_formation_energy_diagram( class CCDDocument(BaseModel): """Configuration-coordinate definition of configuration-coordinate diagram.""" - q1: Optional[int] = Field(None, description="Charge state 1.") - q2: Optional[int] = Field(None, description="Charge state 2.") - structure1: Optional[Structure] = Field( + q1: int | None = Field(None, description="Charge state 1.") + q2: int | None = Field(None, description="Charge state 2.") + structure1: Structure | None = Field( None, description="The structure of defect (supercell) in charge state (q2).", ) - structure2: Optional[Structure] = Field( + structure2: Structure | None = Field( None, description="The structure of defect (supercell) in charge state (q2).", ) - distortions1: Optional[list[float]] = Field( + distortions1: list[float] | None = Field( None, description="The distortions of the defect (supercell) in charge state (q1).", ) - distortions2: Optional[list[float]] = Field( + distortions2: list[float] | None = Field( None, description="The distortions of the defect (supercell) in charge state (q2).", ) - energies1: Optional[list[float]] = Field( + energies1: list[float] | None = Field( None, description="The energies of the defect (supercell) in charge state (q1).", ) - energies2: Optional[list[float]] = Field( + energies2: list[float] | None = Field( None, description="The energies of the defect (supercell) in charge state (q2).", ) - static_dirs1: Optional[list[str]] = Field( + static_dirs1: list[str] | None = Field( None, description="Directories of distorted calculations for the defect (supercell) " "in charge state (q1).", ) - static_dirs2: Optional[list[str]] = Field( + static_dirs2: list[str] | None = Field( None, description="Directories of distorted calculations for the defect (supercell) " "in charge state (q2).", ) - static_uuids1: Optional[list[str]] = Field( + static_uuids1: list[str] | None = Field( None, description="UUIDs of distorted calculations for the defect (supercell) in " "charge state (q1).", ) - static_uuids2: Optional[list[str]] = Field( + static_uuids2: list[str] | None = Field( None, description="UUIDs of distorted calculations for the defect (supercell) in " "charge state (q2).", ) - relaxed_index1: Optional[int] = Field( + relaxed_index1: int | None = Field( None, description="The index of the static calculation in that corresponds to the " "relaxed charge state (q1).", ) - relaxed_index2: Optional[int] = Field( + relaxed_index2: int | None = Field( None, description="The index of the static calculation in that corresponds to the " "relaxed charge state (q2).", @@ -265,8 +265,8 @@ def from_entries( cls, entries1: list[ComputedStructureEntry], entries2: list[ComputedStructureEntry], - relaxed_uuid1: Optional[str] = None, - relaxed_uuid2: Optional[str] = None, + relaxed_uuid1: str | None = None, + relaxed_uuid2: str | None = None, ) -> Self: """Create a CCDTaskDocument from a list of distorted calculations. @@ -343,7 +343,7 @@ def get_taskdocs(self) -> tuple[list[TaskDoc], list[TaskDoc]]: """Get the distorted task documents.""" def remove_host_name(dir_name: str) -> str: - return dir_name.split(":")[-1] + return dir_name.rsplit(":", maxsplit=1)[-1] static1_task_docs = [ TaskDoc.from_directory(remove_host_name(dir_name)) diff --git a/src/atomate2/common/schemas/elastic.py b/src/atomate2/common/schemas/elastic.py index 1675a922fc..7a527f8731 100644 --- a/src/atomate2/common/schemas/elastic.py +++ b/src/atomate2/common/schemas/elastic.py @@ -2,7 +2,6 @@ from copy import deepcopy from enum import Enum -from typing import Optional import numpy as np from emmet.core.math import Matrix3D, MatrixVoigt @@ -26,61 +25,61 @@ class DerivedProperties(BaseModel): """Properties derived from an elastic tensor.""" - k_voigt: Optional[float] = Field( + k_voigt: float | None = Field( None, description="Voigt average of the bulk modulus." ) - k_reuss: Optional[float] = Field( + k_reuss: float | None = Field( None, description="Reuss average of the bulk modulus." ) - k_vrh: Optional[float] = Field( + k_vrh: float | None = Field( None, description="Voigt-Reuss-Hill average of the bulk modulus." ) - g_voigt: Optional[float] = Field( + g_voigt: float | None = Field( None, description="Voigt average of the shear modulus." ) - g_reuss: Optional[float] = Field( + g_reuss: float | None = Field( None, description="Reuss average of the shear modulus." ) - g_vrh: Optional[float] = Field( + g_vrh: float | None = Field( None, description="Voigt-Reuss-Hill average of the shear modulus." ) - universal_anisotropy: Optional[float] = Field( + universal_anisotropy: float | None = Field( None, description="Universal elastic anisotropy." ) - homogeneous_poisson: Optional[float] = Field( + homogeneous_poisson: float | None = Field( None, description="Homogeneous poisson ratio." ) - y_mod: Optional[float] = Field( + y_mod: float | None = Field( None, description="Young's modulus (SI units) from the Voight-Reuss-Hill averages of " "the bulk and shear moduli.", ) - trans_v: Optional[float] = Field( + trans_v: float | None = Field( None, description="Transverse sound velocity (SI units) obtained from the " "Voigt-Reuss-Hill average bulk modulus.", ) - long_v: Optional[float] = Field( + long_v: float | None = Field( None, description="Longitudinal sound velocity (SI units) obtained from the " "Voigt-Reuss-Hill average bulk modulus.", ) - snyder_ac: Optional[float] = Field( + snyder_ac: float | None = Field( None, description="Synder's acoustic sound velocity (SI units)." ) - snyder_opt: Optional[float] = Field( + snyder_opt: float | None = Field( None, description="Synder's optical sound velocity (SI units)." ) - snyder_total: Optional[float] = Field( + snyder_total: float | None = Field( None, description="Synder's total sound velocity (SI units)." ) - clark_thermalcond: Optional[float] = Field( + clark_thermalcond: float | None = Field( None, description="Clarke's thermal conductivity (SI units)." ) - cahill_thermalcond: Optional[float] = Field( + cahill_thermalcond: float | None = Field( None, description="Cahill's thermal conductivity (SI units)." ) - debye_temperature: Optional[float] = Field( + debye_temperature: float | None = Field( None, description="Debye temperature from longitudinal and transverse sound " "velocities (SI units).", @@ -90,25 +89,25 @@ class DerivedProperties(BaseModel): class FittingData(BaseModel): """Data used to fit elastic tensors.""" - cauchy_stresses: Optional[list[Matrix3D]] = Field( + cauchy_stresses: list[Matrix3D] | None = Field( None, description="The Cauchy stresses used to fit the elastic tensor." ) - strains: Optional[list[Matrix3D]] = Field( + strains: list[Matrix3D] | None = Field( None, description="The strains used to fit the elastic tensor." ) - pk_stresses: Optional[list[Matrix3D]] = Field( + pk_stresses: list[Matrix3D] | None = Field( None, description="The Piola-Kirchoff stresses used to fit the elastic tensor." ) - deformations: Optional[list[Matrix3D]] = Field( + deformations: list[Matrix3D] | None = Field( None, description="The deformations corresponding to each strain state." ) - uuids: Optional[list[str]] = Field( + uuids: list[str] | None = Field( None, description="The uuids of the deformation jobs." ) - job_dirs: Optional[list[Optional[str]]] = Field( + job_dirs: list[str | None] | None = Field( None, description="The directories where the deformation jobs were run." ) - failed_uuids: Optional[list[str]] = Field( + failed_uuids: list[str] | None = Field( None, description="The uuids of perturbations that were not completed" ) @@ -116,8 +115,8 @@ class FittingData(BaseModel): class ElasticTensorDocument(BaseModel): """Raw and standardized elastic tensors.""" - raw: Optional[MatrixVoigt] = Field(None, description="Raw elastic tensor.") - ieee_format: Optional[MatrixVoigt] = Field( + raw: MatrixVoigt | None = Field(None, description="Raw elastic tensor.") + ieee_format: MatrixVoigt | None = Field( None, description="Elastic tensor in IEEE format." ) @@ -131,28 +130,28 @@ class ElasticWarnings(Enum): class ElasticDocument(StructureMetadata): """Document containing elastic tensor information and related properties.""" - structure: Optional[Structure] = Field( + structure: Structure | None = Field( None, description="The structure for which the elastic data is calculated." ) - elastic_tensor: Optional[ElasticTensorDocument] = Field( + elastic_tensor: ElasticTensorDocument | None = Field( None, description="Fitted elastic tensor." ) - eq_stress: Optional[Matrix3D] = Field( + eq_stress: Matrix3D | None = Field( None, description="The equilibrium stress of the structure." ) - derived_properties: Optional[DerivedProperties] = Field( + derived_properties: DerivedProperties | None = Field( None, description="Properties derived from the elastic tensor." ) - fitting_data: Optional[FittingData] = Field( + fitting_data: FittingData | None = Field( None, description="Data used to fit the elastic tensor." ) - fitting_method: Optional[str] = Field( + fitting_method: str | None = Field( None, description="Method used to fit the elastic tensor." ) - order: Optional[int] = Field( + order: int | None = Field( None, description="Order of the expansion of the elastic tensor." ) - warnings: Optional[list[str]] = Field(None, description="Warnings.") + warnings: list[str] | None = Field(None, description="Warnings.") @classmethod def from_stresses( @@ -163,8 +162,8 @@ def from_stresses( uuids: list[str], job_dirs: list[str], fitting_method: str = SETTINGS.ELASTIC_FITTING_METHOD, - order: Optional[int] = None, - equilibrium_stress: Optional[Matrix3D] = None, + order: int | None = None, + equilibrium_stress: Matrix3D | None = None, symprec: float = SETTINGS.SYMPREC, allow_elastically_unstable_structs: bool = True, failed_uuids: list[str] = None, diff --git a/src/atomate2/common/schemas/gruneisen.py b/src/atomate2/common/schemas/gruneisen.py index 379dc173c2..f4ad6f97fd 100644 --- a/src/atomate2/common/schemas/gruneisen.py +++ b/src/atomate2/common/schemas/gruneisen.py @@ -2,7 +2,7 @@ import logging from pathlib import Path -from typing import Optional, Union +from typing import Union import phonopy from emmet.core.structure import StructureMetadata @@ -31,13 +31,13 @@ class GruneisenInputDirs(BaseModel): """Collection with all input directories relevant for the Grueneisen run.""" - ground: Optional[str] = Field( + ground: str | None = Field( None, description="The directory with ground state structure phonopy yaml" ) - plus: Optional[str] = Field( + plus: str | None = Field( None, description="The directory with expanded structure phonopy yaml" ) - minus: Optional[str] = Field( + minus: str | None = Field( None, description="The directory with contracted structure phonopy yaml" ) @@ -48,13 +48,13 @@ class PhononRunsImaginaryModes(BaseModel): Information extracted from phonon run for ground, expanded and contracted structures """ - ground: Optional[bool] = Field( + ground: bool | None = Field( None, description="if true, ground state structure has imaginary modes" ) - plus: Optional[bool] = Field( + plus: bool | None = Field( None, description="if true, expanded structure has imaginary modes" ) - minus: Optional[bool] = Field( + minus: bool | None = Field( None, description="if true, contracted structure has imaginary modes" ) @@ -62,10 +62,10 @@ class PhononRunsImaginaryModes(BaseModel): class GruneisenDerivedProperties(BaseModel): """Collection of data derived from the Grueneisen workflow.""" - average_gruneisen: Optional[float] = Field( + average_gruneisen: float | None = Field( None, description="The average Grueneisen parameter" ) - thermal_conductivity_slack: Optional[float] = Field( + thermal_conductivity_slack: float | None = Field( None, description="The thermal conductivity at the acoustic " "Debye temperature with the Slack formula.", @@ -79,18 +79,18 @@ class GruneisenParameterDocument(StructureMetadata): gruneisen_parameter_inputs: GruneisenInputDirs = Field( None, description="The directories where the phonon jobs were run." ) - phonon_runs_has_imaginary_modes: Optional[PhononRunsImaginaryModes] = Field( + phonon_runs_has_imaginary_modes: PhononRunsImaginaryModes | None = Field( None, description="Collection indicating whether the structures from the " "phonon runs have imaginary modes", ) - gruneisen_parameter: Optional[GruneisenParameter] = Field( + gruneisen_parameter: GruneisenParameter | None = Field( None, description="Grueneisen parameter object" ) - gruneisen_band_structure: Optional[GruneisenPhononBandStructureSymmLine] = Field( + gruneisen_band_structure: GruneisenPhononBandStructureSymmLine | None = Field( None, description="Grueneisen phonon band structure symmetry line object" ) - derived_properties: Optional[GruneisenDerivedProperties] = Field( + derived_properties: GruneisenDerivedProperties | None = Field( None, description="Properties derived from the Grueneisen parameter." ) diff --git a/src/atomate2/common/schemas/magnetism.py b/src/atomate2/common/schemas/magnetism.py index eda018c43f..98bdb6cc07 100644 --- a/src/atomate2/common/schemas/magnetism.py +++ b/src/atomate2/common/schemas/magnetism.py @@ -2,8 +2,6 @@ from __future__ import annotations -from typing import Optional - import numpy as np from emmet.core.tasks import TaskDoc from pydantic import BaseModel, Field @@ -21,18 +19,18 @@ class MagneticOrderingInput(BaseModel): documents. """ - structure: Optional[Structure] = Field(None, description="Input structure") - ordering: Optional[Ordering] = Field( + structure: Structure | None = Field(None, description="Input structure") + ordering: Ordering | None = Field( None, description=( "The magnetic ordering of the input structure, " "as defined in pymatgen.analysis.magnetism.analyzer." ), ) - magmoms: Optional[list[float]] = Field( + magmoms: list[float] | None = Field( None, description="Magnetic moments of the structure." ) - symmetry: Optional[str] = Field(None, description="Detected space group symbol.") + symmetry: str | None = Field(None, description="Detected space group symbol.") class MagneticOrderingRelaxation(BaseModel): @@ -41,46 +39,46 @@ class MagneticOrderingRelaxation(BaseModel): This is embedded within the MagneticOrderingOutput. """ - uuid: Optional[str] = Field(None, description="Unique ID of the calculation.") - dir_name: Optional[str] = Field(None, description="Directory of the calculation.") - input: Optional[MagneticOrderingInput] = Field( + uuid: str | None = Field(None, description="Unique ID of the calculation.") + dir_name: str | None = Field(None, description="Directory of the calculation.") + input: MagneticOrderingInput | None = Field( None, description="Input ordering information." ) - structure: Optional[Structure] = Field( + structure: Structure | None = Field( None, description="Final structure from the calculation." ) - symmetry_changed: Optional[bool] = Field( + symmetry_changed: bool | None = Field( None, description="Whether or not symmetry changed." ) - ordering_changed: Optional[bool] = Field( + ordering_changed: bool | None = Field( None, description=( "Specifies whether or not the magnetic ordering changed during the" " calculation." ), ) - ordering: Optional[Ordering] = Field( + ordering: Ordering | None = Field( None, description="Final ordering from the calculation." ) - magmoms: Optional[list[float]] = Field( + magmoms: list[float] | None = Field( None, description="Magnetic moments of the structure." ) - symmetry: Optional[str] = Field(None, description="Detected space group symbol.") - energy: Optional[float] = Field( + symmetry: str | None = Field(None, description="Detected space group symbol.") + energy: float | None = Field( None, description="Final energy result from the calculation." ) - energy_per_atom: Optional[float] = Field(None, description="Final energy per atom.") - total_magnetization: Optional[float] = Field( + energy_per_atom: float | None = Field(None, description="Final energy per atom.") + total_magnetization: float | None = Field( None, description=( "Total magnetization as a sum of individual atomic moments in " "the calculated unit cell." ), ) - total_magnetization_per_formula_unit: Optional[float] = Field( + total_magnetization_per_formula_unit: float | None = Field( None, description="Total magnetization normalized to per formula unit." ) - total_magnetization_per_unit_volume: Optional[float] = Field( + total_magnetization_per_unit_volume: float | None = Field( None, description="Total magnetization normalized to per unit volume." ) @@ -141,62 +139,62 @@ class MagneticOrderingOutput(BaseModel): field. """ - uuid: Optional[str] = Field(None, description="Unique ID of the calculation.") - dir_name: Optional[str] = Field(None, description="Directory of the calculation.") - input: Optional[MagneticOrderingInput] = Field( + uuid: str | None = Field(None, description="Unique ID of the calculation.") + dir_name: str | None = Field(None, description="Directory of the calculation.") + input: MagneticOrderingInput | None = Field( None, description="Input ordering information." ) - structure: Optional[Structure] = Field( + structure: Structure | None = Field( None, description="Final structure from the calculation." ) - ordering: Optional[Ordering] = Field( + ordering: Ordering | None = Field( None, description=( "The magnetic ordering of the output structure, " "as defined in pymatgen.analysis.magnetism.analyzer." ), ) - magmoms: Optional[list[float]] = Field( + magmoms: list[float] | None = Field( None, description="Magnetic moments of the structure." ) - symmetry: Optional[str] = Field(None, description="Detected space group symbol.") - energy: Optional[float] = Field( + symmetry: str | None = Field(None, description="Detected space group symbol.") + energy: float | None = Field( None, description="Final energy result from the calculation." ) - energy_per_atom: Optional[float] = Field(None, description="Final energy per atom.") - total_magnetization: Optional[float] = Field( + energy_per_atom: float | None = Field(None, description="Final energy per atom.") + total_magnetization: float | None = Field( None, description=( "Total magnetization as a sum of individual atomic moments in " "the calculated unit cell." ), ) - total_magnetization_per_formula_unit: Optional[float] = Field( + total_magnetization_per_formula_unit: float | None = Field( None, description="Total magnetization normalized to per formula unit." ) - total_magnetization_per_unit_volume: Optional[float] = Field( + total_magnetization_per_unit_volume: float | None = Field( None, description="Total magnetization normalized to per unit volume." ) - ordering_changed: Optional[bool] = Field( + ordering_changed: bool | None = Field( None, description=( "Specifies whether or not the magnetic ordering changed during the" " calculation." ), ) - symmetry_changed: Optional[bool] = Field( + symmetry_changed: bool | None = Field( None, description=( "Specifies whether or not the symmetry changed during the calculation." ), ) - energy_above_ground_state_per_atom: Optional[float] = Field( + energy_above_ground_state_per_atom: float | None = Field( None, description="Energy per atom above the calculated ground state ordering." ) - relax_output: Optional[MagneticOrderingRelaxation] = Field( + relax_output: MagneticOrderingRelaxation | None = Field( None, description="Relaxation output, if relaxation performed." ) - energy_diff_relax_static: Optional[float] = Field( + energy_diff_relax_static: float | None = Field( None, description=( "Difference in energy between relaxation and final static calculation, if" @@ -279,38 +277,36 @@ class MagneticOrderingsDocument(BaseModel): MagneticOrderingsBuilder corresponding to your DFT code. """ - formula: Optional[str] = Field( + formula: str | None = Field( None, description="Formula taken from pymatgen.core.structure.Structure.formula.", ) - formula_pretty: Optional[str] = Field( + formula_pretty: str | None = Field( None, description="Cleaned representation of the formula", ) - parent_structure: Optional[Structure] = Field( + parent_structure: Structure | None = Field( None, description=( "The parent structure from which individual magnetic " "orderings are generated." ), ) - outputs: Optional[list[MagneticOrderingOutput]] = Field( + outputs: list[MagneticOrderingOutput] | None = Field( None, description="All magnetic ordering calculation results for this structure.", ) - ground_state_uuid: Optional[str] = Field( + ground_state_uuid: str | None = Field( None, description="UUID of the ground state ordering." ) - ground_state_structure: Optional[Structure] = Field( + ground_state_structure: Structure | None = Field( None, description="Ground state structure." ) - ground_state_ordering: Optional[Ordering] = Field( + ground_state_ordering: Ordering | None = Field( None, description="Ground state magnetic ordering." ) - ground_state_energy: Optional[float] = Field( - None, description="Ground state energy." - ) - ground_state_energy_per_atom: Optional[float] = Field( + ground_state_energy: float | None = Field(None, description="Ground state energy.") + ground_state_energy_per_atom: float | None = Field( None, description="Ground state energy, normalized per atom." ) diff --git a/src/atomate2/common/schemas/phonons.py b/src/atomate2/common/schemas/phonons.py index 99e95c4c10..000ef72d48 100644 --- a/src/atomate2/common/schemas/phonons.py +++ b/src/atomate2/common/schemas/phonons.py @@ -3,7 +3,7 @@ import copy import logging from pathlib import Path -from typing import Optional, Union +from typing import Union import numpy as np from emmet.core.math import Matrix3D @@ -78,15 +78,15 @@ class ThermalDisplacementData(BaseModel): "cutoff frequency in THz to avoid numerical issues in the " "computation of the thermal displacement parameters" ) - thermal_displacement_matrix_cif: Optional[list[list[Matrix3D]]] = Field( + thermal_displacement_matrix_cif: list[list[Matrix3D]] | None = Field( None, description="field including thermal displacement matrices in CIF format" ) - thermal_displacement_matrix: Optional[list[list[Matrix3D]]] = Field( + thermal_displacement_matrix: list[list[Matrix3D]] | None = Field( None, description="field including thermal displacement matrices in Cartesian " "coordinate system", ) - temperatures_thermal_displacements: Optional[list[int]] = Field( + temperatures_thermal_displacements: list[int] | None = Field( None, description="temperatures at which the thermal displacement matrices" "have been computed", @@ -96,14 +96,12 @@ class ThermalDisplacementData(BaseModel): class PhononUUIDs(BaseModel): """Collection to save all uuids connected to the phonon run.""" - optimization_run_uuid: Optional[str] = Field( - None, description="optimization run uuid" - ) - displacements_uuids: Optional[list[str]] = Field( + optimization_run_uuid: str | None = Field(None, description="optimization run uuid") + displacements_uuids: list[str] | None = Field( None, description="The uuids of the displacement jobs." ) - static_run_uuid: Optional[str] = Field(None, description="static run uuid") - born_run_uuid: Optional[str] = Field(None, description="born run uuid") + static_run_uuid: str | None = Field(None, description="static run uuid") + born_run_uuid: str | None = Field(None, description="born run uuid") class ForceConstants(MSONable): @@ -116,19 +114,19 @@ def __init__(self, force_constants: list[list[Matrix3D]]) -> None: class PhononJobDirs(BaseModel): """Collection to save all job directories relevant for the phonon run.""" - displacements_job_dirs: Optional[list[Optional[str]]] = Field( + displacements_job_dirs: list[str | None] | None = Field( None, description="The directories where the displacement jobs were run." ) - static_run_job_dir: Optional[Optional[str]] = Field( + static_run_job_dir: Union[None, str] = Field( None, description="Directory where static run was performed." ) - born_run_job_dir: Optional[str] = Field( + born_run_job_dir: str | None = Field( None, description="Directory where born run was performed." ) - optimization_run_job_dir: Optional[str] = Field( + optimization_run_job_dir: str | None = Field( None, description="Directory where optimization run was performed." ) - taskdoc_run_job_dir: Optional[str] = Field( + taskdoc_run_job_dir: str | None = Field( None, description="Directory where task doc was generated." ) @@ -136,104 +134,104 @@ class PhononJobDirs(BaseModel): class PhononBSDOSDoc(StructureMetadata, extra="allow"): # type: ignore[call-arg] """Collection of all data produced by the phonon workflow.""" - structure: Optional[Structure] = Field( + structure: Structure | None = Field( None, description="Structure of Materials Project." ) - phonon_bandstructure: Optional[PhononBandStructureSymmLine] = Field( + phonon_bandstructure: PhononBandStructureSymmLine | None = Field( None, description="Phonon band structure object.", ) - phonon_dos: Optional[PhononDos] = Field( + phonon_dos: PhononDos | None = Field( None, description="Phonon density of states object.", ) - free_energies: Optional[list[float]] = Field( + free_energies: list[float] | None = Field( None, description="vibrational part of the free energies in J/mol per " "formula unit for temperatures in temperature_list", ) - heat_capacities: Optional[list[float]] = Field( + heat_capacities: list[float] | None = Field( None, description="heat capacities in J/K/mol per " "formula unit for temperatures in temperature_list", ) - internal_energies: Optional[list[float]] = Field( + internal_energies: list[float] | None = Field( None, description="internal energies in J/mol per " "formula unit for temperatures in temperature_list", ) - entropies: Optional[list[float]] = Field( + entropies: list[float] | None = Field( None, description="entropies in J/(K*mol) per formula unit" "for temperatures in temperature_list ", ) - temperatures: Optional[list[int]] = Field( + temperatures: list[int] | None = Field( None, description="temperatures at which the vibrational" " part of the free energies" " and other properties have been computed", ) - total_dft_energy: Optional[float] = Field( + total_dft_energy: float | None = Field( None, description="total DFT energy per formula unit in eV" ) - volume_per_formula_unit: Optional[float] = Field( + volume_per_formula_unit: float | None = Field( None, description="volume per formula unit in Angstrom**3" ) - formula_units: Optional[int] = Field(None, description="Formula units per cell") + formula_units: int | None = Field(None, description="Formula units per cell") - has_imaginary_modes: Optional[bool] = Field( + has_imaginary_modes: bool | None = Field( None, description="if true, structure has imaginary modes" ) # needed, e.g. to compute Grueneisen parameter etc - force_constants: Optional[ForceConstants] = Field( + force_constants: ForceConstants | None = Field( None, description="Force constants between every pair of atoms in the structure" ) - born: Optional[list[Matrix3D]] = Field( + born: list[Matrix3D] | None = Field( None, description="Born charges as computed from phonopy. Only for symmetrically " "different atoms", ) - epsilon_static: Optional[Matrix3D] = Field( + epsilon_static: Matrix3D | None = Field( None, description="The high-frequency dielectric constant" ) - supercell_matrix: Optional[Matrix3D] = Field( + supercell_matrix: Matrix3D | None = Field( None, description="matrix describing the supercell" ) - primitive_matrix: Optional[Matrix3D] = Field( + primitive_matrix: Matrix3D | None = Field( None, description="matrix describing relationship to primitive cell" ) - code: Optional[str] = Field( + code: str | None = Field( None, description="String describing the code for the computation" ) - phonopy_settings: Optional[PhononComputationalSettings] = Field( + phonopy_settings: PhononComputationalSettings | None = Field( None, description="Field including settings for Phonopy" ) - thermal_displacement_data: Optional[ThermalDisplacementData] = Field( + thermal_displacement_data: ThermalDisplacementData | None = Field( None, description="Includes all data of the computation of the thermal displacements", ) - jobdirs: Optional[PhononJobDirs] = Field( + jobdirs: PhononJobDirs | None = Field( None, description="Field including all relevant job directories" ) - uuids: Optional[PhononUUIDs] = Field( + uuids: PhononUUIDs | None = Field( None, description="Field including all relevant uuids" ) diff --git a/src/atomate2/common/schemas/qha.py b/src/atomate2/common/schemas/qha.py index 0f5acad6e2..6d6fc3e261 100644 --- a/src/atomate2/common/schemas/qha.py +++ b/src/atomate2/common/schemas/qha.py @@ -1,7 +1,7 @@ """Schemas for qha documents.""" import logging -from typing import Optional, Union +from typing import Union import numpy as np from emmet.core.math import Matrix3D @@ -17,77 +17,77 @@ class PhononQHADoc(StructureMetadata, extra="allow"): # type: ignore[call-arg] """Collection of all data produced by the qha workflow.""" - structure: Optional[Structure] = Field( + structure: Structure | None = Field( None, description="Structure of Materials Project." ) - temperatures: Optional[list[float]] = Field( + temperatures: list[float] | None = Field( None, description="temperatures at which the vibrational part of the free energies" " and other properties have been computed", ) - bulk_modulus: Optional[float] = Field( + bulk_modulus: float | None = Field( None, description="Bulk modulus in GPa computed without phonon contribution." ) - thermal_expansion: Optional[list[float]] = Field( + thermal_expansion: list[float] | None = Field( None, description="Thermal expansion coefficients at temperatures. " "Shape=(temperatures,).", ) - helmholtz_volume: Optional[list[list[float]]] = Field( + helmholtz_volume: list[list[float]] | None = Field( None, description="Free energies (eV) at temperatures and volumes (Angstrom^3)." "shape (temperatures, volumes)", # TODO: add units here ) - volume_temperature: Optional[list[float]] = Field( + volume_temperature: list[float] | None = Field( None, description="Volumes in Angstrom^3 at temperatures.Shape: (temperatures,)", ) - gibbs_temperature: Optional[list[float]] = Field( + gibbs_temperature: list[float] | None = Field( None, description="Gibbs free energies in eV at temperatures. Shape: (temperatures,)", ) - bulk_modulus_temperature: Optional[list[float]] = Field( + bulk_modulus_temperature: list[float] | None = Field( None, description="Bulk modulus in GPa at temperature.Shape: (temperatures,)", ) - heat_capacity_p_numerical: Optional[list[float]] = Field( + heat_capacity_p_numerical: list[float] | None = Field( None, description="Heat capacities in J/K/mol at constant pressure at temperatures." "Shape: (temperatures,)", ) - gruneisen_temperature: Optional[list[float]] = Field( + gruneisen_temperature: list[float] | None = Field( None, description="Gruneisen parameters at temperatures.Shape: (temperatures,)", ) - pressure: Optional[float] = Field( + pressure: float | None = Field( None, description="Pressure in GPA at which Gibb's energy was computed" ) - t_max: Optional[float] = Field( + t_max: float | None = Field( None, description="Maximum temperature in K up to" " which free energy volume curves are evaluated", ) - volumes: Optional[list[float]] = Field(None, description="Volumes in Angstrom^3.") - free_energies: Optional[list[list[float]]] = Field( + volumes: list[float] | None = Field(None, description="Volumes in Angstrom^3.") + free_energies: list[list[float]] | None = Field( None, description="List of free energies in J/mol for per formula unit. " "Shape: (temperatures, volumes)", ) - heat_capacities: Optional[list[list[float]]] = Field( + heat_capacities: list[list[float]] | None = Field( None, description="List of heat capacities in J/K/mol per formula unit. " "Shape: (temperatures, volumes)", ) - entropies: Optional[list[list[float]]] = Field( + entropies: list[list[float]] | None = Field( None, description="List of entropies in J/(K*mol) per formula unit. " "Shape: (temperatures, volumes) ", ) - formula_units: Optional[int] = Field(None, description="Formula units") + formula_units: int | None = Field(None, description="Formula units") - supercell_matrix: Optional[Matrix3D] = Field(None, description="Supercell matrix") + supercell_matrix: Matrix3D | None = Field(None, description="Supercell matrix") @classmethod def from_phonon_runs( diff --git a/src/atomate2/cp2k/schemas/calculation.py b/src/atomate2/cp2k/schemas/calculation.py index 6c2454014e..478a4fd730 100644 --- a/src/atomate2/cp2k/schemas/calculation.py +++ b/src/atomate2/cp2k/schemas/calculation.py @@ -5,7 +5,7 @@ from datetime import datetime, timezone from pathlib import Path from shutil import which -from typing import Any, Optional, Union +from typing import Any, Union from jobflow.utils import ValueEnum from pydantic import BaseModel, Field, field_validator @@ -152,21 +152,21 @@ class CalculationOutput(BaseModel): structure: Union[Structure, Molecule] = Field( None, description="The final structure/molecule from the calculation" ) - efermi: Optional[float] = Field( + efermi: float | None = Field( None, description="The Fermi level from the calculation in eV" ) is_metal: bool = Field(None, description="Whether the system is metallic") - bandgap: Optional[float] = Field( + bandgap: float | None = Field( None, description="The band gap from the calculation in eV" ) v_hartree: Union[dict[int, list[float]], None] = Field( None, description="Plane averaged electrostatic potential" ) - cbm: Optional[float] = Field( + cbm: float | None = Field( None, description="The conduction band minimum in eV (if system is not metallic)", ) - vbm: Optional[float] = Field( + vbm: float | None = Field( None, description="The valence band maximum in eV (if system is not metallic)" ) ionic_steps: list[dict[str, Any]] = Field( @@ -179,13 +179,13 @@ class CalculationOutput(BaseModel): None, description="Summary of runtime statistics for this calculation" ) - scf: Optional[list] = Field(None, description="SCF optimization steps") + scf: list | None = Field(None, description="SCF optimization steps") @classmethod def from_cp2k_output( cls, output: Cp2kOutput, # Must use auto_load kwarg when passed - v_hartree: Optional[VolumetricData] = None, + v_hartree: VolumetricData | None = None, store_trajectory: bool = False, store_scf: bool = False, ) -> Self: @@ -274,7 +274,7 @@ class Calculation(BaseModel): description="Paths (relative to dir_name) of the CP2K output files " "associated with this calculation", ) - bader: Optional[dict] = Field(None, description="Output from the bader software") + bader: dict | None = Field(None, description="Output from the bader software") run_type: RunType = Field( None, description="Calculation run type (e.g., HF, HSE06, PBE)" ) @@ -300,9 +300,7 @@ def from_cp2k_files( strip_dos_projections: bool = False, store_trajectory: bool = False, store_scf: bool = False, - store_volumetric_data: Optional[ - tuple[str] - ] = SETTINGS.CP2K_STORE_VOLUMETRIC_DATA, + store_volumetric_data: tuple[str] | None = SETTINGS.CP2K_STORE_VOLUMETRIC_DATA, ) -> tuple[Self, dict[Cp2kObject, dict]]: """Create a CP2K calculation document from a directory and file paths. @@ -494,7 +492,7 @@ def _get_basis_and_potential_files(dir_name: Path) -> dict[Cp2kObject, DataFile] def _get_volumetric_data( dir_name: Path, output_file_paths: dict[Cp2kObject, str], - store_volumetric_data: Optional[tuple[str]], + store_volumetric_data: tuple[str] | None, ) -> dict[Cp2kObject, VolumetricData]: """ Load volumetric data files from a directory. @@ -538,7 +536,7 @@ def _get_volumetric_data( # and it has to be requested (not default). Should this method grab overall # dos / elemental project dos if the complete dos is not available, or stick # to grabbing the complete dos? -def _parse_dos(parse_dos: Union[str, bool], cp2k_output: Cp2kOutput) -> Optional[Dos]: +def _parse_dos(parse_dos: Union[str, bool], cp2k_output: Cp2kOutput) -> Dos | None: """ Parse DOS outputs from cp2k calculation. @@ -565,7 +563,7 @@ def _parse_dos(parse_dos: Union[str, bool], cp2k_output: Cp2kOutput) -> Optional def _parse_bandstructure( parse_bandstructure: Union[str, bool], cp2k_output: Cp2kOutput -) -> Optional[BandStructure]: +) -> BandStructure | None: """ Get the band structure. @@ -579,7 +577,7 @@ def _parse_bandstructure( return None -def _parse_trajectory(cp2k_output: Cp2kOutput) -> Optional[Trajectory]: +def _parse_trajectory(cp2k_output: Cp2kOutput) -> Trajectory | None: """ Grab a Trajectory object given a cp2k output object. diff --git a/src/atomate2/cp2k/schemas/task.py b/src/atomate2/cp2k/schemas/task.py index 6dbc45bdc7..5afa3b88db 100644 --- a/src/atomate2/cp2k/schemas/task.py +++ b/src/atomate2/cp2k/schemas/task.py @@ -3,7 +3,7 @@ import logging from collections import OrderedDict from pathlib import Path -from typing import Any, Optional, Union +from typing import Any, Union import numpy as np from emmet.core.math import Matrix3D, Vector3D @@ -104,7 +104,7 @@ class AtomicKind(BaseModel): potential: str = Field( None, description="Name of pseudopotential for this atom kind" ) - auxiliary_basis: Optional[str] = Field( + auxiliary_basis: str | None = Field( None, description="Auxiliary basis for this (if any) for this atom kind" ) ghost: bool = Field(None, description="Whether this atom kind is a ghost") @@ -185,15 +185,15 @@ class OutputSummary(BaseModel): energy_per_atom: float = Field( None, description="The final DFT energy per atom for the last calculation" ) - bandgap: Optional[float] = Field( + bandgap: float | None = Field( None, description="The DFT bandgap for the last calculation" ) - cbm: Optional[float] = Field(None, description="CBM for this calculation") - vbm: Optional[float] = Field(None, description="VBM for this calculation") + cbm: float | None = Field(None, description="CBM for this calculation") + vbm: float | None = Field(None, description="VBM for this calculation") forces: list[Vector3D] = Field( None, description="Forces on atoms from the last calculation" ) - stress: Optional[Matrix3D] = Field( + stress: Matrix3D | None = Field( None, description="Stress on the unit cell from the last calculation" ) @@ -237,59 +237,57 @@ def from_cp2k_calc_doc(cls, calc_doc: Calculation) -> Self: class TaskDocument(StructureMetadata, MoleculeMetadata): """Definition of CP2K task document.""" - dir_name: Optional[str] = Field( - None, description="The directory for this CP2K task" - ) + dir_name: str | None = Field(None, description="The directory for this CP2K task") last_updated: str = Field( default_factory=datetime_str, description="Timestamp for this task document was last updated", ) - completed_at: Optional[str] = Field( + completed_at: str | None = Field( None, description="Timestamp for when this task was completed" ) - input: Optional[InputSummary] = Field( + input: InputSummary | None = Field( None, description="The input to the first calculation" ) - output: Optional[OutputSummary] = Field( + output: OutputSummary | None = Field( None, description="The output of the final calculation" ) structure: Union[Structure, Molecule] = Field( None, description="Final output structure from the task" ) - state: Optional[Status] = Field(None, description="State of this task") - included_objects: Optional[list[Cp2kObject]] = Field( + state: Status | None = Field(None, description="State of this task") + included_objects: list[Cp2kObject] | None = Field( None, description="list of CP2K objects included with this task document" ) - cp2k_objects: Optional[dict[Cp2kObject, Any]] = Field( + cp2k_objects: dict[Cp2kObject, Any] | None = Field( None, description="CP2K objects associated with this task" ) - entry: Optional[ComputedEntry] = Field( + entry: ComputedEntry | None = Field( None, description="The ComputedEntry from the task doc" ) - analysis: Optional[AnalysisSummary] = Field( + analysis: AnalysisSummary | None = Field( None, description="Summary of structural relaxation and forces" ) - run_stats: Optional[dict[str, RunStatistics]] = Field( + run_stats: dict[str, RunStatistics] | None = Field( None, description="Summary of runtime statistics for each calculation in this task", ) - orig_inputs: Optional[dict[str, Cp2kInput]] = Field( + orig_inputs: dict[str, Cp2kInput] | None = Field( None, description="Summary of the original CP2K inputs written by custodian" ) - task_label: Optional[str] = Field(None, description="A description of the task") - tags: Optional[list[str]] = Field( + task_label: str | None = Field(None, description="A description of the task") + tags: list[str] | None = Field( None, description="Metadata tags for this task document" ) - author: Optional[str] = Field( + author: str | None = Field( None, description="Author extracted from transformations" ) - icsd_id: Optional[str] = Field( + icsd_id: str | None = Field( None, description="International crystal structure database id of the structure" ) - calcs_reversed: Optional[list[Calculation]] = Field( + calcs_reversed: list[Calculation] | None = Field( None, description="The inputs and outputs for all CP2K runs in this task." ) - transformations: Optional[dict[str, Any]] = Field( + transformations: dict[str, Any] | None = Field( None, description="Information on the structural transformations, parsed from a " "transformations.json file", @@ -299,7 +297,7 @@ class TaskDocument(StructureMetadata, MoleculeMetadata): description="Information on the custodian settings used to run this " "calculation, parsed from a custodian.json file", ) - additional_json: Optional[dict[str, Any]] = Field( + additional_json: dict[str, Any] | None = Field( None, description="Additional json loaded from the calculation directory" ) schema: str = Field( @@ -421,7 +419,7 @@ def from_directory( @staticmethod def get_entry( - calc_docs: list[Calculation], job_id: Optional[str] = None + calc_docs: list[Calculation], job_id: str | None = None ) -> ComputedEntry: """Get a computed entry from a list of CP2K calculation documents. @@ -560,7 +558,7 @@ def _parse_orig_inputs(dir_name: Path) -> dict[str, Cp2kInput]: return orig_inputs -def _get_max_force(calc_doc: Calculation) -> Optional[float]: +def _get_max_force(calc_doc: Calculation) -> float | None: """Get max force acting on atoms from a calculation document.""" forces = ( calc_doc.output.ionic_steps[-1].get("forces") diff --git a/src/atomate2/forcefields/schemas.py b/src/atomate2/forcefields/schemas.py index 14f737ec09..dc76c2c055 100644 --- a/src/atomate2/forcefields/schemas.py +++ b/src/atomate2/forcefields/schemas.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Optional +from typing import Any from emmet.core.utils import ValueEnum from emmet.core.vasp.calculation import StoreTrajectoryOption @@ -18,7 +18,7 @@ class ForcefieldResult(AseResult): """Schema to store outputs; deprecated.""" - final_structure: Optional[Structure] = Field( + final_structure: Structure | None = Field( None, description="The structure in the final trajectory frame." ) @@ -39,28 +39,28 @@ class ForcefieldObject(ValueEnum): class ForceFieldTaskDocument(AseStructureTaskDoc): """Document containing information on structure manipulation using a force field.""" - forcefield_name: Optional[str] = Field( + forcefield_name: str | None = Field( None, description="name of the interatomic potential used for relaxation.", ) - forcefield_version: Optional[str] = Field( + forcefield_version: str | None = Field( "Unknown", description="version of the interatomic potential used for relaxation.", ) - dir_name: Optional[str] = Field( + dir_name: str | None = Field( None, description="Directory where the force field calculations are performed." ) - included_objects: Optional[list[AseObject]] = Field( + included_objects: list[AseObject] | None = Field( None, description="list of forcefield objects included with this task document" ) - objects: Optional[dict[AseObject, Any]] = Field( + objects: dict[AseObject, Any] | None = Field( None, description="Forcefield objects associated with this task" ) - is_force_converged: Optional[bool] = Field( + is_force_converged: bool | None = Field( None, description=( "Whether the calculation is converged with respect to interatomic forces." @@ -156,6 +156,6 @@ def from_ase_compatible_result( return cls.from_ase_task_doc(ase_task_doc, **ff_kwargs) @property - def forcefield_objects(self) -> Optional[dict[AseObject, Any]]: + def forcefield_objects(self) -> dict[AseObject, Any] | None: """Alias `objects` attr for backwards compatibility.""" return self.objects diff --git a/src/atomate2/forcefields/utils.py b/src/atomate2/forcefields/utils.py index f4ee4c012d..ac4fd34f02 100644 --- a/src/atomate2/forcefields/utils.py +++ b/src/atomate2/forcefields/utils.py @@ -73,8 +73,7 @@ def ase_calculator(calculator_meta: str | dict, **kwargs: Any) -> Calculator | N if isinstance(model, str | Path) and Path(model).exists(): model_path = model device = kwargs.get("device") or "cpu" - if "device" in kwargs: - del kwargs["device"] + kwargs.pop("device", None) calculator = MACECalculator( model_paths=model_path, device=device, diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index 56b3f3e2f9..ecdd4629fc 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -5,7 +5,7 @@ import logging import time from pathlib import Path -from typing import Any, Optional, Union +from typing import Any, Union import numpy as np from emmet.core.structure import StructureMetadata @@ -55,15 +55,15 @@ def zpath(pathname: Union[str, Path]) -> str: class LobsteroutModel(BaseModel): """Definition of computational settings from the LOBSTER computation.""" - restart_from_projection: Optional[bool] = Field( + restart_from_projection: bool | None = Field( None, description="Bool indicating if the run has been restarted from a projection", ) - lobster_version: Optional[str] = Field(None, description="Lobster version") - threads: Optional[int] = Field( + lobster_version: str | None = Field(None, description="Lobster version") + threads: int | None = Field( None, description="Number of threads that Lobster ran on" ) - dft_program: Optional[str] = Field( + dft_program: str | None = Field( None, description="DFT program was used for this run" ) charge_spilling: list[float] = Field(description="Absolute charge spilling") @@ -72,48 +72,48 @@ class LobsteroutModel(BaseModel): basis_type: list[str] = Field(description="Basis set used in Lobster") basis_functions: list[list[str]] = Field(description="basis_functions") timing: dict[str, dict[str, str]] = Field(description="Dict with infos on timing") - warning_lines: Optional[list] = Field(None, description="Warnings") - info_orthonormalization: Optional[list] = Field( + warning_lines: list | None = Field(None, description="Warnings") + info_orthonormalization: list | None = Field( None, description="additional information on orthonormalization" ) - info_lines: Optional[list] = Field( + info_lines: list | None = Field( None, description="list of strings with additional info lines" ) - has_doscar: Optional[bool] = Field( + has_doscar: bool | None = Field( None, description="Bool indicating if DOSCAR is present." ) - has_doscar_lso: Optional[bool] = Field( + has_doscar_lso: bool | None = Field( None, description="Bool indicating if DOSCAR.LSO is present." ) - has_cohpcar: Optional[bool] = Field( + has_cohpcar: bool | None = Field( None, description="Bool indicating if COHPCAR is present." ) - has_coopcar: Optional[bool] = Field( + has_coopcar: bool | None = Field( None, description="Bool indicating if COOPCAR is present." ) - has_cobicar: Optional[bool] = Field( + has_cobicar: bool | None = Field( None, description="Bool indicating if COBICAR is present." ) - has_charge: Optional[bool] = Field( + has_charge: bool | None = Field( None, description="Bool indicating if CHARGE is present." ) - has_madelung: Optional[bool] = Field( + has_madelung: bool | None = Field( None, description="Bool indicating if Site Potentials and Madelung file is present.", ) - has_projection: Optional[bool] = Field( + has_projection: bool | None = Field( None, description="Bool indicating if projection file is present." ) - has_bandoverlaps: Optional[bool] = Field( + has_bandoverlaps: bool | None = Field( None, description="Bool indicating if BANDOVERLAPS file is present" ) - has_fatbands: Optional[bool] = Field( + has_fatbands: bool | None = Field( None, description="Bool indicating if Fatbands are present." ) - has_grosspopulation: Optional[bool] = Field( + has_grosspopulation: bool | None = Field( None, description="Bool indicating if GROSSPOP file is present." ) - has_density_of_energies: Optional[bool] = Field( + has_density_of_energies: bool | None = Field( None, description="Bool indicating if DensityofEnergies is present" ) @@ -124,24 +124,24 @@ class LobsterinModel(BaseModel): cohpstartenergy: float = Field(description="Start energy for COHP computation") cohpendenergy: float = Field(description="End energy for COHP computation") - gaussiansmearingwidth: Optional[float] = Field( + gaussiansmearingwidth: float | None = Field( None, description="Set the smearing width in eV,default is 0.2 (eV)" ) - usedecimalplaces: Optional[int] = Field( + usedecimalplaces: int | None = Field( None, description="Set the decimal places to print in output files, default is 5", ) - cohpsteps: Optional[float] = Field( + cohpsteps: float | None = Field( None, description="Number steps in COHPCAR; similar to NEDOS of VASP" ) basisset: str = Field(description="basis set of computation") cohpgenerator: str = Field( description="Build the list of atom pairs to be analyzed using given distance" ) - saveprojectiontofile: Optional[bool] = Field( + saveprojectiontofile: bool | None = Field( None, description="Save the results of projections" ) - lsodos: Optional[bool] = Field( + lsodos: bool | None = Field( None, description="Writes DOS output from the orthonormalized LCAO basis" ) basisfunctions: list[str] = Field( @@ -152,22 +152,20 @@ class LobsterinModel(BaseModel): class Bonding(BaseModel): """Model describing bonding field of BondsInfo.""" - integral: Optional[float] = Field( + integral: float | None = Field( None, description="Integral considering only bonding contributions from COHPs" ) - perc: Optional[float] = Field( - None, description="Percentage of bonding contribution" - ) + perc: float | None = Field(None, description="Percentage of bonding contribution") class Antibonding(BaseModel): """Model describing antibonding field of BondsInfo.""" - integral: Optional[float] = Field( + integral: float | None = Field( None, description="Integral considering only anti-bonding contributions from COHPs", ) - perc: Optional[float] = Field( + perc: float | None = Field( None, description="Percentage of anti-bonding contribution" ) @@ -277,7 +275,7 @@ class CondensedBondingAnalysis(BaseModel): description="Bool that states if the spin channels in the " "cohp_plot_data are summed.", ) - start: Optional[float] = Field( + start: float | None = Field( None, description="Sets the lower limit of energy relative to Fermi for evaluating" " bonding/anti-bonding percentages in the bond" @@ -412,8 +410,7 @@ def from_directory( with open(f"{filename}.json", "w") as fp: json.dump(analyse.condensed_bonding_analysis, fp) with open(f"{filename}.txt", "w") as fp: - for line in describe.text: - fp.write(f"{line}\n") + fp.writelines(f"{line}\n" for line in describe.text) # Read in strongest icohp values sb = _identify_strongest_bonds( @@ -432,27 +429,27 @@ def from_directory( class DosComparisons(BaseModel): """Model describing the DOS comparisons field in the CalcQualitySummary model.""" - tanimoto_orb_s: Optional[float] = Field( + tanimoto_orb_s: float | None = Field( None, description="Tanimoto similarity index between s orbital of " "VASP and LOBSTER DOS", ) - tanimoto_orb_p: Optional[float] = Field( + tanimoto_orb_p: float | None = Field( None, description="Tanimoto similarity index between p orbital of " "VASP and LOBSTER DOS", ) - tanimoto_orb_d: Optional[float] = Field( + tanimoto_orb_d: float | None = Field( None, description="Tanimoto similarity index between d orbital of " "VASP and LOBSTER DOS", ) - tanimoto_orb_f: Optional[float] = Field( + tanimoto_orb_f: float | None = Field( None, description="Tanimoto similarity index between f orbital of " "VASP and LOBSTER DOS", ) - tanimoto_summed: Optional[float] = Field( + tanimoto_summed: float | None = Field( None, description="Tanimoto similarity index for summed PDOS between " "VASP and LOBSTER", @@ -460,7 +457,7 @@ class DosComparisons(BaseModel): e_range: list[Union[float, None]] = Field( description="Energy range used for evaluating the Tanimoto similarity index" ) - n_bins: Optional[int] = Field( + n_bins: int | None = Field( None, description="Number of bins used for discretizing the VASP and LOBSTER PDOS" "(Affects the Tanimoto similarity index)", @@ -470,13 +467,13 @@ class DosComparisons(BaseModel): class ChargeComparisons(BaseModel): """Model describing the charges field in the CalcQualitySummary model.""" - bva_mulliken_agree: Optional[bool] = Field( + bva_mulliken_agree: bool | None = Field( None, description="Bool indicating whether atoms classification as cation " "or anion based on Mulliken charge signs of LOBSTER " "agree with BVA analysis", ) - bva_loewdin_agree: Optional[bool] = Field( + bva_loewdin_agree: bool | None = Field( None, description="Bool indicating whether atoms classification as cations " "or anions based on Loewdin charge signs of LOBSTER " @@ -491,22 +488,22 @@ class BandOverlapsComparisons(BaseModel): description="Boolean indicating whether the bandOverlaps.lobster " "file is generated during the LOBSTER run", ) - limit_maxDeviation: Optional[float] = Field( # noqa: N815 + limit_maxDeviation: float | None = Field( # noqa: N815 None, description="Limit set for maximal deviation in pymatgen parser", ) - has_good_quality_maxDeviation: Optional[bool] = Field( # noqa: N815 + has_good_quality_maxDeviation: bool | None = Field( # noqa: N815 None, description="Boolean indicating whether the deviation at each k-point " "is within the threshold set using limit_maxDeviation " "for analyzing the bandOverlaps.lobster file data", ) - max_deviation: Optional[float] = Field( + max_deviation: float | None = Field( None, description="Maximum deviation from ideal identity matrix from the observed in " "the bandOverlaps.lobster file", ) - percent_kpoints_abv_limit: Optional[float] = Field( + percent_kpoints_abv_limit: float | None = Field( None, description="Percent of k-points that show deviations above " "the limit_maxDeviation threshold set in pymatgen parser.", @@ -534,15 +531,15 @@ class CalcQualitySummary(BaseModel): charge_spilling: ChargeSpilling = Field( description="Model describing the charge spilling from the LOBSTER runs", ) - charge_comparisons: Optional[ChargeComparisons] = Field( + charge_comparisons: ChargeComparisons | None = Field( None, description="Model describing the charge sign comparison results", ) - band_overlaps_analysis: Optional[BandOverlapsComparisons] = Field( + band_overlaps_analysis: BandOverlapsComparisons | None = Field( None, description="Model describing the band overlap file analysis results", ) - dos_comparisons: Optional[DosComparisons] = Field( + dos_comparisons: DosComparisons | None = Field( None, description="Model describing the VASP and LOBSTER PDOS comparisons results", ) @@ -618,20 +615,20 @@ class StrongestBonds(BaseModel): LobsterPy is used for the extraction. """ - which_bonds: Optional[str] = Field( + which_bonds: str | None = Field( None, description="Denotes whether the information " "is for cation-anion pairs or all bonds", ) - strongest_bonds_icoop: Optional[dict] = Field( + strongest_bonds_icoop: dict | None = Field( None, description="Dict with infos on bond strength and bond length based on ICOOP.", ) - strongest_bonds_icohp: Optional[dict] = Field( + strongest_bonds_icohp: dict | None = Field( None, description="Dict with infos on bond strength and bond length based on ICOHP.", ) - strongest_bonds_icobi: Optional[dict] = Field( + strongest_bonds_icobi: dict | None = Field( None, description="Dict with infos on bond strength and bond length based on ICOBI.", ) @@ -648,85 +645,85 @@ class LobsterTaskDocument(StructureMetadata, extra="allow"): # type: ignore[cal default_factory=datetime_str, description="Timestamp for this task document was last updated", ) - charges: Optional[Charge] = Field( + charges: Charge | None = Field( None, description="pymatgen Charge obj. Contains atomic charges based on Mulliken " "and Loewdin charge analysis", ) lobsterout: LobsteroutModel = Field(description="Lobster out data") lobsterin: LobsterinModel = Field(description="Lobster calculation inputs") - lobsterpy_data: Optional[CondensedBondingAnalysis] = Field( + lobsterpy_data: CondensedBondingAnalysis | None = Field( None, description="Model describing the LobsterPy data" ) - lobsterpy_text: Optional[str] = Field( + lobsterpy_text: str | None = Field( None, description="Stores LobsterPy automatic analysis summary text" ) - calc_quality_summary: Optional[CalcQualitySummary] = Field( + calc_quality_summary: CalcQualitySummary | None = Field( None, description="Model summarizing results of lobster runs like charge spillings, " "band overlaps, DOS comparisons with VASP runs and quantum chemical LOBSTER " "charge sign comparisons with BVA method", ) - calc_quality_text: Optional[str] = Field( + calc_quality_text: str | None = Field( None, description="Stores calculation quality analysis summary text" ) - strongest_bonds: Optional[StrongestBonds] = Field( + strongest_bonds: StrongestBonds | None = Field( None, description="Describes the strongest cation-anion ICOOP, ICOBI and ICOHP bonds", ) - lobsterpy_data_cation_anion: Optional[CondensedBondingAnalysis] = Field( + lobsterpy_data_cation_anion: CondensedBondingAnalysis | None = Field( None, description="Model describing the LobsterPy data" ) - lobsterpy_text_cation_anion: Optional[str] = Field( + lobsterpy_text_cation_anion: str | None = Field( None, description="Stores LobsterPy automatic analysis summary text", ) - strongest_bonds_cation_anion: Optional[StrongestBonds] = Field( + strongest_bonds_cation_anion: StrongestBonds | None = Field( None, description="Describes the strongest cation-anion ICOOP, ICOBI and ICOHP bonds", ) - dos: Optional[LobsterCompleteDos] = Field( + dos: LobsterCompleteDos | None = Field( None, description="pymatgen pymatgen.io.lobster.Doscar.completedos data" ) - lso_dos: Optional[LobsterCompleteDos] = Field( + lso_dos: LobsterCompleteDos | None = Field( None, description="pymatgen pymatgen.io.lobster.Doscar.completedos data" ) - madelung_energies: Optional[MadelungEnergies] = Field( + madelung_energies: MadelungEnergies | None = Field( None, description="pymatgen Madelung energies obj. Contains madelung energies" "based on Mulliken and Loewdin charges", ) - site_potentials: Optional[SitePotential] = Field( + site_potentials: SitePotential | None = Field( None, description="pymatgen Site potentials obj. Contains site potentials " "based on Mulliken and Loewdin charges", ) - gross_populations: Optional[Grosspop] = Field( + gross_populations: Grosspop | None = Field( None, description="pymatgen Grosspopulations obj. Contains gross populations " " based on Mulliken and Loewdin charges ", ) - band_overlaps: Optional[Bandoverlaps] = Field( + band_overlaps: Bandoverlaps | None = Field( None, description="pymatgen Bandoverlaps obj for each k-point from" " bandOverlaps.lobster file if it exists", ) - cohp_data: Optional[CompleteCohp] = Field( + cohp_data: CompleteCohp | None = Field( None, description="pymatgen CompleteCohp object with COHP data" ) - coop_data: Optional[CompleteCohp] = Field( + coop_data: CompleteCohp | None = Field( None, description="pymatgen CompleteCohp object with COOP data" ) - cobi_data: Optional[CompleteCohp] = Field( + cobi_data: CompleteCohp | None = Field( None, description="pymatgen CompleteCohp object with COBI data" ) - icohp_list: Optional[Icohplist] = Field( + icohp_list: Icohplist | None = Field( None, description="pymatgen Icohplist object with ICOHP data" ) - icoop_list: Optional[Icohplist] = Field( + icoop_list: Icohplist | None = Field( None, description="pymatgen Icohplist object with ICOOP data" ) - icobi_list: Optional[Icohplist] = Field( + icobi_list: Icohplist | None = Field( None, description="pymatgen Icohplist object with ICOBI data" ) diff --git a/src/atomate2/settings.py b/src/atomate2/settings.py index 912013fc27..d1c66f73bd 100644 --- a/src/atomate2/settings.py +++ b/src/atomate2/settings.py @@ -4,7 +4,7 @@ import warnings from pathlib import Path -from typing import Any, Literal, Optional, Union +from typing import Any, Literal, Union from pydantic import Field, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -41,7 +41,7 @@ class Atomate2Settings(BaseSettings): description="Tolerance for determining if a material is a semiconductor or " "metal", ) - CUSTODIAN_SCRATCH_DIR: Optional[str] = Field( + CUSTODIAN_SCRATCH_DIR: str | None = Field( None, description="Path to scratch directory used by custodian." ) @@ -55,7 +55,7 @@ class Atomate2Settings(BaseSettings): VASP_NCL_CMD: str = Field( "vasp_ncl", description="Command to run non-collinear version of VASP." ) - VASP_VDW_KERNEL_DIR: Optional[str] = Field( + VASP_VDW_KERNEL_DIR: str | None = Field( None, description="Path to VDW VASP kernel." ) VASP_INCAR_UPDATES: dict = Field( @@ -76,7 +76,7 @@ class Atomate2Settings(BaseSettings): VASP_CUSTODIAN_MAX_ERRORS: int = Field( 5, description="Maximum number of errors to correct before custodian gives up" ) - VASP_STORE_VOLUMETRIC_DATA: Optional[tuple[str]] = Field( + VASP_STORE_VOLUMETRIC_DATA: tuple[str] | None = Field( None, description="Store data from these files in database if present" ) VASP_STORE_ADDITIONAL_JSON: bool = Field( @@ -94,7 +94,7 @@ class Atomate2Settings(BaseSettings): description="Whether to run the DDEC6 program when parsing VASP calculations." "Requires the chargemol executable to be on the path.", ) - DDEC6_ATOMIC_DENSITIES_DIR: Optional[str] = Field( + DDEC6_ATOMIC_DENSITIES_DIR: str | None = Field( default=None, description="Directory where the atomic densities are stored.", # TODO uncomment below once that functionality is actually implemented @@ -163,7 +163,7 @@ class Atomate2Settings(BaseSettings): CP2K_CUSTODIAN_MAX_ERRORS: int = Field( 5, description="Maximum number of errors to correct before custodian gives up" ) - CP2K_STORE_VOLUMETRIC_DATA: Optional[tuple[str]] = Field( + CP2K_STORE_VOLUMETRIC_DATA: tuple[str] | None = Field( None, description="Store data from these files in database if present" ) CP2K_STORE_ADDITIONAL_JSON: bool = Field( @@ -190,12 +190,12 @@ class Atomate2Settings(BaseSettings): ) # AMSET settings - AMSET_SETTINGS_UPDATE: Optional[dict] = Field( + AMSET_SETTINGS_UPDATE: dict | None = Field( None, description="Additional settings applied to AMSET settings file." ) # ABINIT settings - ABINIT_MPIRUN_CMD: Optional[str] = Field(None, description="Mpirun command.") + ABINIT_MPIRUN_CMD: str | None = Field(None, description="Mpirun command.") ABINIT_CMD: str = Field("abinit", description="Abinit command.") ABINIT_MRGDDB_CMD: str = Field("mrgddb", description="Mrgddb command.") ABINIT_ANADDB_CMD: str = Field("anaddb", description="Anaddb command.") @@ -207,7 +207,7 @@ class Atomate2Settings(BaseSettings): default=False, description="Use autoparal to determine optimal parallel configuration.", ) - ABINIT_ABIPY_MANAGER_FILE: Optional[str] = Field( + ABINIT_ABIPY_MANAGER_FILE: str | None = Field( None, description="Config file for task manager of abipy.", ) diff --git a/src/atomate2/utils/datetime.py b/src/atomate2/utils/datetime.py index b491e2c412..cdb9977d4a 100644 --- a/src/atomate2/utils/datetime.py +++ b/src/atomate2/utils/datetime.py @@ -1,4 +1,4 @@ -"""Helper functions for datetime objects.""" # noqa: A005 +"""Helper functions for datetime objects.""" from __future__ import annotations diff --git a/src/atomate2/vasp/schemas/defect.py b/src/atomate2/vasp/schemas/defect.py index f6f5a18795..54bf60bc6c 100644 --- a/src/atomate2/vasp/schemas/defect.py +++ b/src/atomate2/vasp/schemas/defect.py @@ -2,7 +2,7 @@ import logging from pathlib import Path -from typing import Optional, Union +from typing import Union from pydantic import BaseModel, Field from pymatgen.io.vasp.outputs import WSWQ @@ -35,8 +35,8 @@ class FiniteDifferenceDocument(BaseModel): def from_directory( cls, directory: Union[str, Path], - ref_dir: Optional[Union[str, Path]] = None, - distorted_dirs: Optional[list[str]] = None, + ref_dir: Union[str, Path] | None = None, + distorted_dirs: list[str] | None = None, ) -> Self: """Read the FiniteDiff file. diff --git a/tests/abinit/conftest.py b/tests/abinit/conftest.py index 47d77adb04..49a760fb95 100644 --- a/tests/abinit/conftest.py +++ b/tests/abinit/conftest.py @@ -284,10 +284,8 @@ def _get_differences_tol( self_dataset_dict = dict(self_dataset) other_dataset_dict = dict(other_dataset) for k in to_ignore: - if k in self_dataset_dict: - del self_dataset_dict[k] - if k in other_dataset_dict: - del other_dataset_dict[k] + self_dataset_dict.pop(k, None) + other_dataset_dict.pop(k, None) common_keys = set(self_dataset_dict.keys()).intersection( other_dataset_dict.keys() )