From 25f0a9ca53c35cfbfe887e097fdee04207a610aa Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Fri, 9 Aug 2024 21:20:12 +0800 Subject: [PATCH] Fix mypy errors for `io.cp2k` (#3984) * ignore mypy override error globally * standardize CP2K names * pre-commit auto-fixes * standarize more CP2K names * old trick: relocate magic methods to the top * format tweaks * fix type errors in cp2k inputs * remove reimport Sequence * use list[Kpoint] as getter return type * remove unnecessary cast --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- src/pymatgen/analysis/local_env.py | 8 +- src/pymatgen/core/interface.py | 12 +- src/pymatgen/core/ion.py | 2 +- src/pymatgen/core/lattice.py | 2 +- src/pymatgen/core/sites.py | 4 +- src/pymatgen/core/structure.py | 28 ++-- src/pymatgen/core/surface.py | 6 +- .../electronic_structure/bandstructure.py | 2 +- src/pymatgen/electronic_structure/dos.py | 6 +- src/pymatgen/entries/compatibility.py | 2 +- src/pymatgen/entries/mixing_scheme.py | 2 +- src/pymatgen/io/cp2k/inputs.py | 143 ++++++++---------- src/pymatgen/io/cp2k/outputs.py | 26 ++-- src/pymatgen/io/cp2k/sets.py | 26 ++-- src/pymatgen/io/cp2k/utils.py | 14 +- src/pymatgen/io/lammps/data.py | 4 +- src/pymatgen/io/lammps/inputs.py | 6 +- src/pymatgen/io/lammps/sets.py | 2 +- src/pymatgen/io/lobster/inputs.py | 2 +- src/pymatgen/io/lobster/lobsterenv.py | 2 +- src/pymatgen/io/qchem/inputs.py | 4 +- src/pymatgen/io/vasp/inputs.py | 9 +- src/pymatgen/symmetry/structure.py | 4 +- tests/io/test_core.py | 2 +- 25 files changed, 155 insertions(+), 165 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d27514a211f..6d4df081ab5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -285,7 +285,7 @@ ignore_missing_imports = true namespace_packages = true explicit_package_bases = true no_implicit_optional = false -disable_error_code = "annotation-unchecked" +disable_error_code = ["annotation-unchecked", "override"] [[tool.mypy.overrides]] module = ["requests.*", "tabulate.*"] diff --git a/src/pymatgen/analysis/local_env.py b/src/pymatgen/analysis/local_env.py index ac27b579472..61a0e338707 100644 --- a/src/pymatgen/analysis/local_env.py +++ b/src/pymatgen/analysis/local_env.py @@ -1490,7 +1490,7 @@ def get_nn_info(self, structure: Structure, n: int): return siw - def get_bonded_structure(self, structure: Structure, decorate: bool = False) -> StructureGraph: # type: ignore[override] + def get_bonded_structure(self, structure: Structure, decorate: bool = False) -> StructureGraph: """ Obtain a MoleculeGraph object using this NearNeighbor class. Requires the optional dependency networkx @@ -1637,7 +1637,7 @@ def get_nn_info(self, structure: Structure, n: int): return siw - def get_bonded_structure(self, structure: Structure, decorate: bool = False) -> MoleculeGraph: # type: ignore[override] + def get_bonded_structure(self, structure: Structure, decorate: bool = False) -> MoleculeGraph: """ Obtain a MoleculeGraph object using this NearNeighbor class. @@ -4000,7 +4000,7 @@ def get_nn_data(self, structure: Structure, n: int, length=None): return self.transform_to_length(self.NNData(nn, cn_weights, cn_nninfo), length) - def get_cn(self, structure: Structure, n: int, **kwargs) -> float: # type: ignore[override] + def get_cn(self, structure: Structure, n: int, **kwargs) -> float: """Get coordination number, CN, of site with index n in structure. Args: @@ -4306,7 +4306,7 @@ def extend_structure_molecules(self) -> bool: """ return True - def get_bonded_structure(self, structure: Structure, decorate: bool = False) -> StructureGraph: # type: ignore[override] + def get_bonded_structure(self, structure: Structure, decorate: bool = False) -> StructureGraph: """ Args: structure (Structure): Input structure diff --git a/src/pymatgen/core/interface.py b/src/pymatgen/core/interface.py index a7a49004409..c5516aece5f 100644 --- a/src/pymatgen/core/interface.py +++ b/src/pymatgen/core/interface.py @@ -166,7 +166,7 @@ def to_str(number: float, rjust: int = 10) -> str: outs.append(f"{idx} {site.species_string} {' '.join(to_str(coord, 12) for coord in site.frac_coords)}") return "\n".join(outs) - def copy(self) -> Self: # type: ignore[override] + def copy(self) -> Self: """Make a copy of the GrainBoundary.""" return type(self)( self.lattice, @@ -261,7 +261,7 @@ def coincidents(self) -> list[Site]: coincident_sites.append(self.sites[idx]) return coincident_sites - def as_dict(self) -> dict: # type: ignore[override] + def as_dict(self) -> dict: """ Returns: Dictionary representation of GrainBoundary object. @@ -281,7 +281,7 @@ def as_dict(self) -> dict: # type: ignore[override] } @classmethod - def from_dict(cls, dct: dict) -> Self: # type: ignore[override] + def from_dict(cls, dct: dict) -> Self: """Generate GrainBoundary from a dict created by as_dict(). Args: @@ -2580,7 +2580,7 @@ def film(self) -> Structure: """A Structure for just the film.""" return Structure.from_sites(self.film_sites) - def copy(self) -> Self: # type: ignore[override] + def copy(self) -> Self: """Make a copy of the Interface.""" return type(self).from_dict(self.as_dict()) @@ -2689,7 +2689,7 @@ def _update_c(self, new_c: float) -> None: site._lattice = new_lattice # Update the lattice site.coords = c_coords # Put back into original Cartesian space - def as_dict(self) -> dict: # type: ignore[override] + def as_dict(self) -> dict: """MSONable dict.""" return { **super().as_dict(), @@ -2700,7 +2700,7 @@ def as_dict(self) -> dict: # type: ignore[override] } @classmethod - def from_dict(cls, dct: dict) -> Self: # type: ignore[override] + def from_dict(cls, dct: dict) -> Self: """ Args: dct: dict. diff --git a/src/pymatgen/core/ion.py b/src/pymatgen/core/ion.py index 03055da8095..fb2dd3a527a 100644 --- a/src/pymatgen/core/ion.py +++ b/src/pymatgen/core/ion.py @@ -304,7 +304,7 @@ def composition(self) -> Composition: """Composition of ion.""" return Composition(self._data) - def oxi_state_guesses( # type: ignore[override] + def oxi_state_guesses( self, oxi_states_override: dict | None = None, all_oxi_states: bool = False, diff --git a/src/pymatgen/core/lattice.py b/src/pymatgen/core/lattice.py index 7782ac10ebb..280e95c9bf2 100644 --- a/src/pymatgen/core/lattice.py +++ b/src/pymatgen/core/lattice.py @@ -443,7 +443,7 @@ def from_dict( dct: dict, fmt: str | None = None, **kwargs, - ) -> Self: # type: ignore[override] + ) -> Self: """Create a Lattice from a dictionary. If fmt is None, the dict should contain the a, b, c, diff --git a/src/pymatgen/core/sites.py b/src/pymatgen/core/sites.py index b9771cc3f25..3f31c26ea16 100644 --- a/src/pymatgen/core/sites.py +++ b/src/pymatgen/core/sites.py @@ -84,7 +84,7 @@ def __getattr__(self, attr: str) -> Any: return props[attr] raise AttributeError(f"{attr=} not found on {type(self).__name__}") - def __getitem__(self, el: Element) -> float: # type: ignore[override] + def __getitem__(self, el: Element) -> float: """Get the occupancy for element.""" return self.species[el] @@ -102,7 +102,7 @@ def __eq__(self, other: object) -> bool: and self.properties == other.properties ) - def __hash__(self) -> int: # type: ignore[override] + def __hash__(self) -> int: """Minimally effective hash function that just distinguishes between Sites with different elements. """ diff --git a/src/pymatgen/core/structure.py b/src/pymatgen/core/structure.py index 0b8f980822f..311c2a83120 100644 --- a/src/pymatgen/core/structure.py +++ b/src/pymatgen/core/structure.py @@ -104,7 +104,7 @@ def __len__(self) -> Literal[3]: """Make neighbor Tuple-like to retain backwards compatibility.""" return 3 - def __getitem__(self, idx: int) -> Self | float: # type: ignore[override] + def __getitem__(self, idx: int) -> Self | float: """Make neighbor Tuple-like to retain backwards compatibility.""" return (self, self.nn_distance, self.index)[idx] @@ -170,7 +170,7 @@ def __len__(self) -> Literal[4]: """Make neighbor Tuple-like to retain backwards compatibility.""" return 4 - def __getitem__(self, idx: int | slice): # type: ignore[override] + def __getitem__(self, idx: int | slice): """Make neighbor Tuple-like to retain backwards compatibility.""" return (self, self.nn_distance, self.index, self.image)[idx] @@ -179,12 +179,12 @@ def coords(self) -> NDArray: """Cartesian coords.""" return self._lattice.get_cartesian_coords(self._frac_coords) - def as_dict(self) -> dict: # type: ignore[override] + def as_dict(self) -> dict: """Note that method calls the super of Site, which is MSONable itself.""" return super(Site, self).as_dict() @classmethod - def from_dict(cls, dct: dict) -> Self: # type: ignore[override] + def from_dict(cls, dct: dict) -> Self: """Get a PeriodicNeighbor from a dict. Args: @@ -2960,7 +2960,7 @@ def from_id(cls, id_: str, source: StructureSources = "Materials Project", **kwa raise ValueError(f"Invalid source: {source}") @classmethod - def from_str( # type: ignore[override] + def from_str( cls, input_string: str, fmt: FileFormats, @@ -3044,7 +3044,7 @@ def from_str( # type: ignore[override] return cls.from_sites(struct, properties=struct.properties) @classmethod - def from_file( # type: ignore[override] + def from_file( cls, filename: PathLike, primitive: bool = False, @@ -3774,7 +3774,7 @@ def to(self, filename: str = "", fmt: str = "") -> str | None: return str(writer) @classmethod - def from_str( # type: ignore[override] + def from_str( cls, input_string: str, fmt: Literal["xyz", "gjf", "g03", "g09", "com", "inp", "json", "yaml"], @@ -3821,7 +3821,7 @@ def from_str( # type: ignore[override] return cls.from_sites(mol, properties=mol.properties) @classmethod - def from_file(cls, filename: PathLike) -> Self | None: # type: ignore[override] + def from_file(cls, filename: PathLike) -> Self | None: """Read a molecule from a file. Supported formats include xyz, gaussian input (gjf|g03|g09|com|inp), Gaussian output (.out|and pymatgen's JSON-serialized molecules. Using openbabel, @@ -3933,7 +3933,7 @@ def __init__( self._sites: list[PeriodicSite] = list(self._sites) # type: ignore[assignment] - def __setitem__( # type: ignore[override] + def __setitem__( self, idx: int | slice | Sequence[int] | SpeciesLike, site: SpeciesLike | PeriodicSite | Sequence | dict[SpeciesLike, float], @@ -4018,7 +4018,7 @@ def lattice(self, lattice: ArrayLike | Lattice) -> None: for site in self: site.lattice = lattice - def append( # type: ignore[override] + def append( self, species: CompositionLike, coords: ArrayLike, @@ -4049,7 +4049,7 @@ def append( # type: ignore[override] properties=properties, ) - def insert( # type: ignore[override] + def insert( self, idx: int, species: CompositionLike, @@ -4765,7 +4765,7 @@ def __init__( ) self._sites: list[Site] = list(self._sites) - def __setitem__( # type: ignore[override] + def __setitem__( self, idx: int | slice | Sequence[int] | SpeciesLike, site: SpeciesLike | Site | Sequence, @@ -4810,7 +4810,7 @@ def __delitem__(self, idx: SupportsIndex | slice) -> None: """Deletes a site from the Structure.""" self._sites.__delitem__(idx) - def append( # type: ignore[override] + def append( self, species: CompositionLike, coords: ArrayLike, @@ -4874,7 +4874,7 @@ def set_charge_and_spin( return self - def insert( # type: ignore[override] + def insert( self, idx: int, species: CompositionLike, diff --git a/src/pymatgen/core/surface.py b/src/pymatgen/core/surface.py index 6bc5678f70d..8eed9340450 100644 --- a/src/pymatgen/core/surface.py +++ b/src/pymatgen/core/surface.py @@ -218,7 +218,7 @@ def surface_area(self) -> float: return np.linalg.norm(np.cross(matrix[0], matrix[1])) @classmethod - def from_dict(cls, dct: dict[str, Any]) -> Self: # type: ignore[override] + def from_dict(cls, dct: dict[str, Any]) -> Self: """ Args: dct: dict. @@ -242,7 +242,7 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: # type: ignore[override] energy=dct["energy"], ) - def as_dict(self, **kwargs) -> dict: # type: ignore[override] + def as_dict(self, **kwargs) -> dict: """MSONable dict.""" dct = super().as_dict(**kwargs) dct["@module"] = type(self).__module__ @@ -255,7 +255,7 @@ def as_dict(self, **kwargs) -> dict: # type: ignore[override] dct["energy"] = self.energy return dct - def copy(self, site_properties: dict[str, Any] | None = None) -> Self: # type: ignore[override] + def copy(self, site_properties: dict[str, Any] | None = None) -> Self: """Get a copy of the Slab, with options to update site properties. Args: diff --git a/src/pymatgen/electronic_structure/bandstructure.py b/src/pymatgen/electronic_structure/bandstructure.py index 74a25d4edb6..290f8293474 100644 --- a/src/pymatgen/electronic_structure/bandstructure.py +++ b/src/pymatgen/electronic_structure/bandstructure.py @@ -1037,7 +1037,7 @@ def get_projection_on_elements(self) -> dict[Spin, list]: def get_projections_on_elements_and_orbitals( self, - el_orb_spec: dict[Element, list], # type: ignore[override] + el_orb_spec: dict[Element, list], ) -> dict[Spin, list]: """Get projections on elements and specific orbitals. diff --git a/src/pymatgen/electronic_structure/dos.py b/src/pymatgen/electronic_structure/dos.py index a5abd47d472..1e5c6a7f8ae 100644 --- a/src/pymatgen/electronic_structure/dos.py +++ b/src/pymatgen/electronic_structure/dos.py @@ -1397,7 +1397,7 @@ def as_dict(self) -> dict[str, Any]: class LobsterCompleteDos(CompleteDos): """Extended CompleteDos for LOBSTER.""" - def get_site_orbital_dos(self, site: PeriodicSite, orbital: str) -> Dos: # type: ignore[override] + def get_site_orbital_dos(self, site: PeriodicSite, orbital: str) -> Dos: """Get the DOS for a particular orbital of a particular site. Args: @@ -1448,7 +1448,7 @@ def get_site_t2g_eg_resolved_dos( "e_g": Dos(self.efermi, self.energies, functools.reduce(add_densities, eg_dos)), } - def get_spd_dos(self) -> dict[str, Dos]: # type: ignore[override] + def get_spd_dos(self) -> dict[str, Dos]: """Get orbital projected DOS. For example, if 3s and 4s are included in the basis of some element, @@ -1469,7 +1469,7 @@ def get_spd_dos(self) -> dict[str, Dos]: # type: ignore[override] return {orb: Dos(self.efermi, self.energies, densities) for orb, densities in spd_dos.items()} # type: ignore[misc] - def get_element_spd_dos(self, el: SpeciesLike) -> dict[str, Dos]: # type: ignore[override] + def get_element_spd_dos(self, el: SpeciesLike) -> dict[str, Dos]: """Get element and s/p/d projected DOS. Args: diff --git a/src/pymatgen/entries/compatibility.py b/src/pymatgen/entries/compatibility.py index d3b5d7251f7..058abe7aae6 100644 --- a/src/pymatgen/entries/compatibility.py +++ b/src/pymatgen/entries/compatibility.py @@ -837,7 +837,7 @@ def get_explanation_dict(self, entry: ComputedEntry) -> dict[str, Any]: dct["corrections"] = corrections return dct - def explain(self, entry: ComputedEntry) -> None: # type: ignore[override] + def explain(self, entry: ComputedEntry) -> None: """Print an explanation of the corrections that are being applied for a given compatibility scheme. Inspired by the "explain" methods in many database methodologies. diff --git a/src/pymatgen/entries/mixing_scheme.py b/src/pymatgen/entries/mixing_scheme.py index 92b56931c04..8f8fb637ab5 100644 --- a/src/pymatgen/entries/mixing_scheme.py +++ b/src/pymatgen/entries/mixing_scheme.py @@ -114,7 +114,7 @@ def __init__( if hasattr(compat, "check_potcar"): compat.check_potcar = check_potcar # type: ignore[union-attr] - def process_entries( # type: ignore[override] + def process_entries( self, entries: AnyComputedEntry | list[AnyComputedEntry], clean: bool = True, diff --git a/src/pymatgen/io/cp2k/inputs.py b/src/pymatgen/io/cp2k/inputs.py index ae0c020c2a2..a6f8f0f4c20 100644 --- a/src/pymatgen/io/cp2k/inputs.py +++ b/src/pymatgen/io/cp2k/inputs.py @@ -1,20 +1,20 @@ """ -This module defines the building blocks of a CP2K input file. The cp2k input structure is +This module defines the building blocks of a CP2K input file. The CP2K input structure is essentially a collection of "sections" which are similar to dictionary objects that activate -modules of the cp2k executable, and then "keywords" which adjust variables inside of those +modules of the CP2K executable, and then "keywords" which adjust variables inside of those modules. For example, FORCE_EVAL section will activate CP2K's ability to calculate forces, and inside FORCE_EVAL, the Keyword "METHOD can be set to "QS" to set the method of force evaluation to be the quickstep (DFT) module. A quick overview of the module: --- Section class defines the basis of Cp2k input and contains methods for manipulating these +-- Section class defines the basis of CP2K input and contains methods for manipulating these objects similarly to Dicts. -- Keyword class defines the keywords used inside of Section objects that changes variables in - Cp2k programs. + CP2K programs. -- SectionList and KeywordList classes are lists of Section and Keyword objects that have the same dictionary key. This deals with repeated sections and keywords. --- Cp2kInput class is special instantiation of Section that is used to represent the full cp2k +-- Cp2kInput class is special instantiation of Section that is used to represent the full CP2K calculation input. -- The rest of the classes are children of Section intended to make initialization of common sections easier. @@ -29,9 +29,7 @@ import os import re import textwrap -from collections.abc import Iterable, Sequence from dataclasses import dataclass, field -from pathlib import Path from typing import TYPE_CHECKING from monty.dev import deprecated @@ -45,7 +43,8 @@ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Iterable, Sequence + from pathlib import Path from typing import Any, Literal from typing_extensions import Self @@ -59,8 +58,6 @@ __email__ = "nwinner@berkeley.edu" __date__ = "September 2022" -MODULE_DIR = Path(__file__).resolve().parent - class Keyword(MSONable): """A keyword argument in CP2K. Within CP2K Sections, which activate features @@ -238,8 +235,8 @@ def verbosity(self, verbosity): class Section(MSONable): """ - Basic input representation of input to Cp2k. Activates functionality inside of the - Cp2k executable. + Basic input representation of input to CP2K. Activates functionality inside of the + CP2K executable. """ def __init__( @@ -281,7 +278,7 @@ def __init__( location: the path to the section in the form 'SECTION/SUBSECTION1/SUBSECTION3', example for QS module: 'FORCE_EVAL/DFT/QS'. This location is used to automatically determine if a subsection requires a supersection to be activated. - verbose: Controls how much is printed to Cp2k input files (Also see Keyword). + verbose: Controls how much is printed to CP2K input files (Also see Keyword). If True, then a description of the section will be printed with it as a comment (if description is set). Default=True. alias: An alias for this class to use in place of the name. @@ -334,10 +331,28 @@ def __add__(self, other) -> Section: def __setitem__(self, key, value): self.setitem(key, value) - # dict merge def __or__(self, other: dict) -> Section: + """Dict merge.""" return self.update(other) + def __delitem__(self, key): + """ + Delete section with name matching key OR delete all keywords + with names matching this key. + """ + if lst := [sub_sec for sub_sec in self.subsections if sub_sec.upper() == key.upper()]: + del self.subsections[lst[0]] + return + + if lst := [kw for kw in self.keywords if kw.upper() == key.upper()]: + del self.keywords[lst[0]] + return + + raise KeyError("No section or keyword matching the given key.") + + def __sub__(self, other): + return self.__delitem__(other) + def setitem(self, key, value, strict=False): """ Helper function for setting items. Kept separate from the double-underscore function so that @@ -359,24 +374,6 @@ def setitem(self, key, value, strict=False): elif not strict: self.keywords[key] = value - def __delitem__(self, key): - """ - Delete section with name matching key OR delete all keywords - with names matching this key. - """ - if lst := [sub_sec for sub_sec in self.subsections if sub_sec.upper() == key.upper()]: - del self.subsections[lst[0]] - return - - if lst := [kw for kw in self.keywords if kw.upper() == key.upper()]: - del self.keywords[lst[0]] - return - - raise KeyError("No section or keyword matching the given key.") - - def __sub__(self, other): - return self.__delitem__(other) - def add(self, other): """Add another keyword to the current section.""" if not isinstance(other, (Keyword, KeywordList)): @@ -393,8 +390,7 @@ def get(self, d, default=None): """ if kw := self.get_keyword(d): return kw - sec = self.get_section(d) - if sec: + if sec := self.get_section(d): return sec return default @@ -657,7 +653,7 @@ def verbosity(self, verbosity) -> None: class Cp2kInput(Section): """ - Special instance of 'Section' class that is meant to represent the overall cp2k input. + Special instance of 'Section' class that is meant to represent the overall CP2K input. Distinguishes itself from Section by overriding get_str() to not print this section's title and by implementing the file i/o. """ @@ -680,10 +676,7 @@ def __init__(self, name: str = "CP2K_INPUT", subsections: dict | None = None, ** def get_str(self): """Get string representation of the Cp2kInput.""" - string = "" - for v in self.subsections.values(): - string += v.get_str() - return string + return "".join(v.get_str() for v in self.subsections.values()) @classmethod def _from_dict(cls, dct: dict): @@ -782,7 +775,7 @@ def write_file( class Global(Section): - """Controls 'global' settings for cp2k execution such as RUN_TYPE and PROJECT_NAME.""" + """Controls 'global' settings for CP2K execution such as RUN_TYPE and PROJECT_NAME.""" def __init__( self, @@ -822,7 +815,7 @@ def __init__( class ForceEval(Section): - """Controls the calculation of energy and forces in Cp2k.""" + """Controls the calculation of energy and forces in CP2K.""" def __init__(self, keywords: dict | None = None, subsections: dict | None = None, **kwargs): """Initialize the ForceEval section.""" @@ -847,7 +840,7 @@ def __init__(self, keywords: dict | None = None, subsections: dict | None = None class Dft(Section): - """Controls the DFT parameters in Cp2k.""" + """Controls the DFT parameters in CP2K.""" def __init__( self, @@ -1356,7 +1349,7 @@ def __init__( subsections = subsections or {} description = "The description of this kind of atom including basis sets, element, etc." - # Special case for closed-shell elements. Cannot impose magnetization in cp2k. + # Special case for closed-shell elements. Cannot impose magnetization in CP2K. closed_shell_elems = {2, 4, 10, 12, 18, 20, 30, 36, 38, 48, 54, 56, 70, 80, 86, 88, 102, 112, 118} if Element(self.specie).Z in closed_shell_elems: self.magnetization = 0 @@ -1940,7 +1933,7 @@ def __init__( weigh each by 1 eps_geo (float): tolerance for symmetry. Default=1e-6 full_grid (bool): use full (not reduced) kpoint grid. Default=False. - parallel_group_size (int): from cp2k manual: Number of processors + parallel_group_size (int): from CP2K manual: Number of processors to be used for a single kpoint. This number must divide the total number of processes. The number of groups must divide the total number of kpoints. Value=-1 (smallest possible @@ -1998,7 +1991,7 @@ def __init__( ) @classmethod - def from_kpoints(cls, kpoints: VaspKpoints, structure=None) -> Self: + def from_kpoints(cls, kpoints: VaspKpoints, structure: Structure | None = None) -> Self: """ Initialize the section from a Kpoints object (pymatgen.io.vasp.inputs). CP2K does not have an automatic gamma-point constructor, so this is generally used @@ -2007,18 +2000,18 @@ def from_kpoints(cls, kpoints: VaspKpoints, structure=None) -> Self: so long as the grid is fine enough. Args: - kpoints: A pymatgen kpoints object. - structure: Pymatgen structure object. Required for automatically performing + kpoints (Kpoints): A pymatgen kpoints object. + structure (Structure): Required for automatically performing symmetry analysis and reducing the kpoint grid. reduce: whether or not to reduce the grid using symmetry. CP2K itself cannot do this automatically without spglib present at execution time. """ kpts = kpoints.kpts - weights = kpoints.kpts_weights + weights: Sequence[float] | None = kpoints.kpts_weights if kpoints.style == KpointsSupportedModes.Monkhorst: - kpt: Kpoint = kpts[0] # type: ignore[assignment] - x, y, z = (kpt, kpt, kpt) if isinstance(kpt, (int, float)) else kpt # type: ignore[misc] + kpt: Kpoint = kpts[0] + x, y, z = (kpt, kpt, kpt) if len(kpt) == 1 else kpt scheme = f"MONKHORST-PACK {x} {y} {z}" units = "B_VECTOR" @@ -2036,14 +2029,12 @@ def from_kpoints(cls, kpoints: VaspKpoints, structure=None) -> Self: "No cp2k automatic gamma constructor. A structure is required to construct from spglib" ) - if (isinstance(kpts[0], Iterable) and tuple(kpts[0]) == (1, 1, 1)) or ( - isinstance(kpts[0], (float, int)) and int(kpts[0]) == 1 - ): + if tuple(kpts[0]) in {(1, 1, 1), (1,)}: scheme = "GAMMA" else: sga = SpacegroupAnalyzer(structure) - _kpts, weights = zip(*sga.get_ir_reciprocal_mesh(mesh=kpts)) # type: ignore[assignment] - kpts = tuple(itertools.chain.from_iterable(_kpts)) + _kpts, weights = zip(*sga.get_ir_reciprocal_mesh(mesh=kpts)) # type: ignore[arg-type] + kpts = list(itertools.chain.from_iterable(_kpts)) scheme = "GENERAL" units = "B_VECTOR" @@ -2133,8 +2124,6 @@ def __init__( keywords=keywords, ) - # TODO kpoints objects are defined in the vasp module instead of a code agnostic module - # if this changes in the future as other codes are added, then this will need to change @classmethod def from_kpoints(cls, kpoints: VaspKpoints, kpoints_line_density: int = 20) -> Self: """ @@ -2143,6 +2132,9 @@ def from_kpoints(cls, kpoints: VaspKpoints, kpoints_line_density: int = 20) -> S Args: kpoints: a kpoint object from the vasp module, which was constructed in line mode kpoints_line_density: Number of kpoints along each path + + TODO: kpoints objects are defined in the vasp module instead of a code agnostic module + if this changes in the future as other codes are added, then this will need to change """ if kpoints.style == KpointsSupportedModes.Line_mode: @@ -2226,14 +2218,13 @@ def softmatch(self, other): return False d1 = self.as_dict() d2 = other.as_dict() - return all(not (v is not None and v != d2[k]) for k, v in d1.items()) + return all(v is None or v == d2[k] for k, v in d1.items()) @classmethod def from_str(cls, string: str) -> Self: """Get summary info from a string.""" string = string.upper() - data: dict[str, Any] = {} - data["cc"] = "CC" in string + data: dict[str, Any] = {"cc": "CC" in string} string = string.replace("CC", "") data["pc"] = "PC" in string string = string.replace("PC", "") @@ -2284,7 +2275,7 @@ def from_str(cls, string: str) -> Self: @dataclass class AtomicMetadata(MSONable): """ - Metadata for basis sets and potentials in cp2k. + Metadata for basis sets and potentials in CP2K. Attributes: info: Info about this object @@ -2324,7 +2315,7 @@ def softmatch(self, other): other_names = [other.name] if other.alias_names: other_names.extend(other.alias_names) - return all(not (nm is not None and nm not in other_names) for nm in this_names) + return all(nm is None or nm in other_names for nm in this_names) def get_hash(self) -> str: """Get a hash of this object.""" @@ -2368,7 +2359,7 @@ def __post_init__(self) -> None: if self.info and self.potential == "All Electron" and self.element: self.info.electrons = self.element.Z if self.name == "ALLELECTRON": - self.name = "ALL" # cp2k won't parse ALLELECTRON for some reason + self.name = "ALL" # CP2K won't parse ALLELECTRON for some reason def cast(d): new = {} @@ -2399,7 +2390,7 @@ def nexp(self): return [len(exp) for exp in self.exponents] def get_str(self) -> str: - """Get standard cp2k GTO formatted string.""" + """Get standard CP2K GTO formatted string.""" if ( # written verbosely so mypy can perform type narrowing self.info is None or self.nset is None @@ -2429,7 +2420,7 @@ def get_str(self) -> str: @classmethod def from_str(cls, string: str) -> Self: - """Read from standard cp2k GTO formatted string.""" + """Read from standard CP2K GTO formatted string.""" lines = [line for line in string.split("\n") if line] firstline = lines[0].split() element = Element(firstline[0]) @@ -2524,11 +2515,11 @@ def softmatch(self, other): return False d1 = self.as_dict() d2 = other.as_dict() - return all(not (v is not None and v != d2[k]) for k, v in d1.items()) + return all(v is None or v == d2[k] for k, v in d1.items()) @classmethod def from_str(cls, string: str) -> Self: - """Get a cp2k formatted string representation.""" + """Get a CP2K formatted string representation.""" string = string.upper() data: dict[str, Any] = {} if "NLCC" in string: @@ -2580,7 +2571,7 @@ def __post_init__(self) -> None: if self.potential == "All Electron" and self.element: self.info.electrons = self.element.Z if self.name == "ALLELECTRON": - self.name = "ALL" # cp2k won't parse ALLELECTRON for some reason + self.name = "ALL" # CP2K won't parse ALLELECTRON for some reason def cast(d): new = {} @@ -2654,11 +2645,11 @@ def get_str(self) -> str: for idx in range(self.nprj): total_fill = self.nprj_ppnl[idx] * 20 + 24 tmp = f"{self.radii[idx]: .14f} {self.nprj_ppnl[idx]: d}" - out += f"{tmp:>{''}{24}}" + out += f"{tmp:>24}" for i in range(self.nprj_ppnl[idx]): k = total_fill - 24 if i == 0 else total_fill tmp = " ".join(f"{v: .14f}" for v in self.hprj_ppnl[idx][i].values()) - out += f"{tmp:>{''}{k}}" + out += f"{tmp:>{k}}" out += "\n" return out @@ -2734,10 +2725,13 @@ def from_str(cls, string: str) -> Self: @dataclass class DataFile(MSONable): - """A data file for a cp2k calc.""" + """A data file for a CP2K calc.""" objects: Sequence | None = None + def __str__(self): + return self.get_str() + @classmethod def from_file(cls, filename) -> Self: """Load from a file, reserved for child classes.""" @@ -2762,16 +2756,13 @@ def get_str(self) -> str: """Get string representation.""" return "\n".join(b.get_str() for b in self.objects or []) - def __str__(self): - return self.get_str() - @dataclass class BasisFile(DataFile): """Data file for basis sets only.""" @classmethod - def from_str(cls, string: str) -> Self: # type: ignore[override] + def from_str(cls, string: str) -> Self: """Initialize from a string representation.""" basis_sets = [GaussianTypeOrbitalBasisSet.from_str(c) for c in chunk(string)] return cls(objects=basis_sets) @@ -2782,7 +2773,7 @@ class PotentialFile(DataFile): """Data file for potentials only.""" @classmethod - def from_str(cls, string: str) -> Self: # type: ignore[override] + def from_str(cls, string: str) -> Self: """Initialize from a string representation.""" basis_sets = [GthPotential.from_str(c) for c in chunk(string)] return cls(objects=basis_sets) diff --git a/src/pymatgen/io/cp2k/outputs.py b/src/pymatgen/io/cp2k/outputs.py index 19c63761eba..ec539e97329 100644 --- a/src/pymatgen/io/cp2k/outputs.py +++ b/src/pymatgen/io/cp2k/outputs.py @@ -94,7 +94,7 @@ def __init__(self, filename, verbose=False, auto_load=False): @property def cp2k_version(self): - """The cp2k version used in the calculation.""" + """The CP2K version used in the calculation.""" return self.data.get("cp2k_version")[0][0] @property @@ -190,7 +190,7 @@ def multiplicity(self) -> int: @property def is_molecule(self) -> bool: """ - True if the cp2k output was generated for a molecule (i.e. + True if the CP2K output was generated for a molecule (i.e. no periodicity in the cell). """ return self.data.get("poisson_periodicity", [[""]])[0][0].upper() == "NONE" @@ -210,7 +210,7 @@ def is_hubbard(self) -> bool: def parse_files(self): """ - Identify files present in the directory with the cp2k output file. Looks for trajectories, + Identify files present in the directory with the CP2K output file. Looks for trajectories, dos, and cubes. """ self.filenames["DOS"] = glob(os.path.join(self.dir, "*.dos*")) @@ -259,11 +259,11 @@ def parse_files(self): def parse_structures(self, trajectory_file=None, lattice_file=None): """ - Parses the structures from a cp2k calculation. Static calculations simply use the initial + Parse the structures from a CP2K calculation. Static calculations simply use the initial structure. For calculations with ionic motion, the function will look for the appropriate trajectory and lattice files based on naming convention. If no file is given, and no file is found, it is assumed that the lattice/structure remained constant, and the initial - lattice/structure is used. Cp2k does not output the trajectory in the main output file by + lattice/structure is used. CP2K does not output the trajectory in the main output file by default, so non static calculations have to reference the trajectory file. """ self.parse_initial_structure() @@ -316,7 +316,7 @@ def parse_structures(self, trajectory_file=None, lattice_file=None): self.final_structure = self.structures[-1] def parse_initial_structure(self): - """Parse the initial structure from the main cp2k output file.""" + """Parse the initial structure from the main CP2K output file.""" patterns = {"num_atoms": re.compile(r"- Atoms:\s+(\d+)")} self.read_pattern( patterns=patterns, @@ -426,7 +426,7 @@ def convergence(self): if not all(self.data["scf_converged"]): warnings.warn( - "There is at least one unconverged SCF cycle in the provided cp2k calculation", + "There is at least one unconverged SCF cycle in the provided CP2K calculation", UserWarning, ) if any(self.data["geo_opt_not_converged"]): @@ -993,7 +993,7 @@ def parse_hirshfeld(self): self.structures[i].add_site_property("hirshfield", hirshfeld) def parse_mo_eigenvalues(self): - """Parse the MO eigenvalues from the cp2k output file. Will get the eigenvalues (and band gap) + """Parse the MO eigenvalues from the CP2K output file. Will get the eigenvalues (and band gap) at each ionic step (if more than one exist). Everything is decomposed by spin channel. If calculation was performed without spin @@ -1167,13 +1167,13 @@ def parse_homo_lumo(self): self.band_gap = (bg[Spin.up][-1] + bg[Spin.down][-1]) / 2 if bg[Spin.up] and bg[Spin.down] else None def parse_dos(self, dos_file=None, pdos_files=None, ldos_files=None): - """Parse the dos files produced by cp2k calculation. CP2K produces different files based + """Parse the dos files produced by CP2K calculation. CP2K produces different files based on the input file rather than assimilating them all into one file. One file type is the overall DOS file, which is used for k-point calculations. For non-kpoint calculation, the overall DOS is generally not calculated, but the element-projected pDOS is. Separate files are created for each spin channel and each - atom kind. If requested, cp2k can also do site/local projected dos (ldos). Each site + atom kind. If requested, CP2K can also do site/local projected dos (ldos). Each site requested will have a separate file for each spin channel (if spin polarized calculation is performed). @@ -1325,7 +1325,7 @@ def parse_bandstructure(self, bandstructure_filename=None) -> None: efermi=efermi, labels_dict=labels, structure=self.final_structure, - projections=None, # not implemented in cp2k + projections=None, # not implemented in CP2K ) self.band_gap = self.data["band_structure"].get_band_gap().get("energy") @@ -1647,7 +1647,7 @@ def parse_energy_file(energy_file): return {c: df[c].to_numpy() for c in columns} -# TODO The DOS file that cp2k outputs as of 2022.1 seems to have a lot of problems. +# TODO: The DOS file that CP2K outputs as of 2022.1 seems to have a lot of problems. def parse_dos(dos_file=None): """Parse a dos file. This format is different from the pdos files.""" data = np.loadtxt(dos_file) @@ -1669,7 +1669,7 @@ def parse_dos(dos_file=None): def parse_pdos(dos_file=None, spin_channel=None, total=False): """ - Parse a single DOS file created by cp2k. Must contain one PDOS snapshot. i.e. you cannot + Parse a single DOS file created by CP2K. Must contain one PDOS snapshot. i.e. you cannot use this cannot deal with multiple concatenated dos files. Args: diff --git a/src/pymatgen/io/cp2k/sets.py b/src/pymatgen/io/cp2k/sets.py index 16a79842efa..b5ea47cb1f1 100644 --- a/src/pymatgen/io/cp2k/sets.py +++ b/src/pymatgen/io/cp2k/sets.py @@ -118,7 +118,7 @@ def __init__( speed-ups for this part of the calculation, but the system must have a band gap for OT to be used (higher band-gap --> faster convergence). energy_gap (float): Estimate of energy gap for pre-conditioner. Default is -1, leaving - it up to cp2k. + it up to CP2K. eps_default (float): Replaces all EPS_XX Keywords in the DFT section value, ensuring an overall accuracy of at least this much. eps_scf (float): The convergence criteria for leaving the SCF loop. Default is 1e-6. @@ -147,13 +147,13 @@ def __init__( transformation is not analytically correct and uses a truncated polynomial expansion, but is robust to the problems with STRICT, and so is the default. linesearch (str): Linesearch method for CG. 2PNT is the default, and is the fastest, - but is not as robust as 3PNT. 2PNT is required as of cp2k v9.1 for compatibility + but is not as robust as 3PNT. 2PNT is required as of CP2K v9.1 for compatibility with irac+rotation. This may be upgraded in the future. 3PNT can be good for wide gapped transition metal systems as an alternative. rotation (bool): Whether or not to allow for rotation of the orbitals in the OT method. This equates to allowing for fractional occupations in the calculation. occupation_preconditioner (bool): Whether or not to account for fractional occupations - in the preconditioner. This method is not fully integrated as of cp2k v9.1 and is + in the preconditioner. This method is not fully integrated as of CP2K v9.1 and is set to false by default. cutoff (int): Cutoff energy (in Ry) for the finest level of the multigrid. A high cutoff will allow you to have very accurate calculations PROVIDED that REL_CUTOFF @@ -210,7 +210,7 @@ def __init__( self.insert(ForceEval()) if self.kpoints: - # As of cp2k v2022.1 kpoint module is not fully integrated, so even specifying + # As of CP2K v2022.1 kpoint module is not fully integrated, so even specifying # 0,0,0 will disable certain features. So, you have to drop it all together to # get full support if ( @@ -358,7 +358,7 @@ def get_basis_and_potential(structure, basis_and_potential): el: {'basis': obj, 'potential': obj} - 2. Provide a hash of the object that matches the keys in the pmg configured cp2k data files. + 2. Provide a hash of the object that matches the keys in the pmg configured CP2K data files. el: {'basis': hash, 'potential': hash} @@ -373,7 +373,7 @@ def get_basis_and_potential(structure, basis_and_potential): Strategy 2: global descriptors In this case, any elements not present in the argument will be dealt with by searching the pmg - configured cp2k data files to find a objects matching your requirements. + configured CP2K data files to find a objects matching your requirements. - functional: Find potential and basis that have been optimized for a specific functional like PBE. Can be None if you do not require them to match. @@ -402,7 +402,7 @@ def get_basis_and_potential(structure, basis_and_potential): desired_basis, desired_aux_basis, desired_potential = None, None, None have_element_file = os.path.isfile(os.path.join(SETTINGS.get("PMG_CP2K_DATA_DIR", "."), el)) - # Necessary if matching data to cp2k data files + # Necessary if matching data to CP2K data files if have_element_file: with open(os.path.join(SETTINGS.get("PMG_CP2K_DATA_DIR", "."), el), encoding="utf-8") as file: yaml = YAML(typ="unsafe", pure=True) @@ -675,7 +675,7 @@ def print_bandstructure(self, kpoints_line_density: int = 20) -> None: """ Attaches a non-scf band structure calc the end of an SCF loop. - This requires a kpoint calculation, which is not always default in cp2k. + This requires a kpoint calculation, which is not always default in CP2K. Args: kpoints_line_density: number of kpoints along each branch in line-mode calc. @@ -728,7 +728,7 @@ def activate_hybrid( """ Basic set for activating hybrid DFT calculation using Auxiliary Density Matrix Method. - Note 1: When running ADMM with cp2k, memory is very important. If the memory requirements + Note 1: When running ADMM with CP2K, memory is very important. If the memory requirements exceed what is available (see max_memory), then CP2K will have to calculate the 4-electron integrals for HFX during each step of the SCF cycle. ADMM provides a huge speed up by making the memory requirements *feasible* to fit into RAM, which means you only need to @@ -742,14 +742,14 @@ def activate_hybrid( Args: hybrid_functional (str): Type of hybrid functional. This set supports HSE (screened) and PBE0 (truncated). Default is PBE0, which converges easier in the GPW basis - used by cp2k. + used by CP2K. hf_fraction (float): fraction of exact HF exchange energy to mix. Default: 0.25 gga_x_fraction (float): fraction of gga exchange energy to retain. Default: 0.75 gga_c_fraction (float): fraction of gga correlation energy to retain. Default: 1.0 max_memory (int): Maximum memory available to each MPI process (in Mb) in the calculation. Most modern computing nodes will have ~2Gb per core, or 2048 Mb, but check for your specific system. This value should be as large as possible - while still leaving some memory for the other parts of cp2k. Important: If + while still leaving some memory for the other parts of CP2K. Important: If this value is set larger than the memory limits, CP2K will likely seg-fault. Default: 2000 cutoff_radius (float): for truncated hybrid functional (i.e. PBE0), this is the cutoff @@ -1377,9 +1377,9 @@ def __init__(self, **kwargs) -> None: class Cp2kValidationError(Exception): """ - Cp2k Validation Exception. Not exhausted. May raise validation + CP2K validation exception. Not exhausted. May raise validation errors for features which actually do work if using a newer version - of cp2k. + of CP2K. """ CP2K_VERSION = "v2022.1" diff --git a/src/pymatgen/io/cp2k/utils.py b/src/pymatgen/io/cp2k/utils.py index d82beca9c64..84373f58ece 100644 --- a/src/pymatgen/io/cp2k/utils.py +++ b/src/pymatgen/io/cp2k/utils.py @@ -1,4 +1,4 @@ -"""Utility functions for assisting with cp2k IO.""" +"""Utility functions for assisting with CP2K IO.""" from __future__ import annotations @@ -52,9 +52,9 @@ def postprocessor(data: str) -> str | float | bool | None: def preprocessor(data: str, dir: str = ".") -> str: # noqa: A002 """ - Cp2k contains internal preprocessor flags that are evaluated before execution. This helper - function recognizes those preprocessor flags and replaces them with an equivalent cp2k input - (this way everything is contained neatly in the cp2k input structure, even if the user preferred + CP2K contains internal preprocessor flags that are evaluated before execution. This helper + function recognizes those preprocessor flags and replaces them with an equivalent CP2K input + (this way everything is contained neatly in the CP2K input structure, even if the user preferred to use the flags. CP2K preprocessor flags (with arguments) are: @@ -67,7 +67,7 @@ def preprocessor(data: str, dir: str = ".") -> str: # noqa: A002 @IF/@ELIF: Not implemented yet. Args: - data (str): cp2k input to preprocess + data (str): CP2K input to preprocess dir (str, optional): Path for include files. Default is '.' (current directory). Returns: @@ -92,12 +92,12 @@ def preprocessor(data: str, dir: str = ".") -> str: # noqa: A002 c1 = re.findall(r"@IF", data, re.IGNORECASE) c2 = re.findall(r"@ELIF", data, re.IGNORECASE) if len(c1) > 0 or len(c2) > 0: - raise NotImplementedError("This cp2k input processor does not currently support conditional blocks.") + raise NotImplementedError("This CP2K input processor does not currently support conditional blocks.") return data def chunk(string: str): - """Chunk the string from a cp2k basis or potential file.""" + """Chunk the string from a CP2K basis or potential file.""" lines = iter(line for line in (line.strip() for line in string.split("\n")) if line and not line.startswith("#")) chunks: list = [] for line in lines: diff --git a/src/pymatgen/io/lammps/data.py b/src/pymatgen/io/lammps/data.py index 5fb50b053d3..0d3f66ec8ff 100644 --- a/src/pymatgen/io/lammps/data.py +++ b/src/pymatgen/io/lammps/data.py @@ -1360,12 +1360,12 @@ def disassemble( # NOTE (@janosh): The following two methods for override parent class LammpsData @classmethod - def from_ff_and_topologies(cls) -> None: # type: ignore[override] + def from_ff_and_topologies(cls) -> None: """Unsupported constructor for CombinedData objects.""" raise AttributeError("Unsupported constructor for CombinedData objects") @classmethod - def from_structure(cls) -> None: # type: ignore[override] + def from_structure(cls) -> None: """Unsupported constructor for CombinedData objects.""" raise AttributeError("Unsupported constructor for CombinedData objects") diff --git a/src/pymatgen/io/lammps/inputs.py b/src/pymatgen/io/lammps/inputs.py index 953f0978b90..0a5742898d2 100644 --- a/src/pymatgen/io/lammps/inputs.py +++ b/src/pymatgen/io/lammps/inputs.py @@ -531,7 +531,7 @@ def write_file(self, filename: str | PathLike, ignore_comments: bool = False, ke file.write(self.get_str(ignore_comments=ignore_comments, keep_stages=keep_stages)) @classmethod - def from_str(cls, contents: str, ignore_comments: bool = False, keep_stages: bool = False) -> Self: # type: ignore[override] + def from_str(cls, contents: str, ignore_comments: bool = False, keep_stages: bool = False) -> Self: """ Helper method to parse string representation of LammpsInputFile. If you created the input file by hand, there is no guarantee that the representation @@ -609,7 +609,7 @@ def from_str(cls, contents: str, ignore_comments: bool = False, keep_stages: boo return lammps_in_file @classmethod - def from_file(cls, path: str | Path, ignore_comments: bool = False, keep_stages: bool = False) -> Self: # type: ignore[override] + def from_file(cls, path: str | Path, ignore_comments: bool = False, keep_stages: bool = False) -> Self: """ Creates an InputFile object from a file. @@ -934,7 +934,7 @@ class LammpsTemplateGen(TemplateInputGen): See pymatgen.io.template.py for additional documentation of this method. """ - def get_input_set( # type: ignore[override] + def get_input_set( self, script_template: PathLike, settings: dict | None = None, diff --git a/src/pymatgen/io/lammps/sets.py b/src/pymatgen/io/lammps/sets.py index d61510f3d67..85d11f81410 100644 --- a/src/pymatgen/io/lammps/sets.py +++ b/src/pymatgen/io/lammps/sets.py @@ -75,7 +75,7 @@ def __init__( super().__init__(inputs={"in.lammps": self.inputfile, "system.data": self.data}) @classmethod - def from_directory(cls, directory: PathLike, keep_stages: bool = False) -> Self: # type: ignore[override] + def from_directory(cls, directory: PathLike, keep_stages: bool = False) -> Self: """Construct a LammpsInputSet from a directory of two or more files. TODO: accept directories with only the input file, that should include the structure as well. diff --git a/src/pymatgen/io/lobster/inputs.py b/src/pymatgen/io/lobster/inputs.py index 9eef7c654a0..bd80f07a927 100644 --- a/src/pymatgen/io/lobster/inputs.py +++ b/src/pymatgen/io/lobster/inputs.py @@ -184,7 +184,7 @@ def __getitem__(self, key: str) -> Any: except KeyError as exc: raise KeyError(f"{key=} is not available") from exc - def __contains__(self, key: str) -> bool: # type: ignore[override] + def __contains__(self, key: str) -> bool: """To avoid cases sensitivity problems.""" return super().__contains__(key.lower().strip()) diff --git a/src/pymatgen/io/lobster/lobsterenv.py b/src/pymatgen/io/lobster/lobsterenv.py index 919ea6bb678..c36694f02f0 100644 --- a/src/pymatgen/io/lobster/lobsterenv.py +++ b/src/pymatgen/io/lobster/lobsterenv.py @@ -253,7 +253,7 @@ def anion_types(self) -> set[Element]: def get_anion_types(self): return self.anion_types - def get_nn_info(self, structure: Structure, n: int, use_weights: bool = False) -> dict: # type: ignore[override] + def get_nn_info(self, structure: Structure, n: int, use_weights: bool = False) -> dict: """Get coordination number, CN, of site with index n in structure. Args: diff --git a/src/pymatgen/io/qchem/inputs.py b/src/pymatgen/io/qchem/inputs.py index 7776940c27d..38ef5909971 100644 --- a/src/pymatgen/io/qchem/inputs.py +++ b/src/pymatgen/io/qchem/inputs.py @@ -306,7 +306,7 @@ def multi_job_string(job_list: list[QCInput]) -> str: return multi_job_string @classmethod - def from_str(cls, string: str) -> Self: # type: ignore[override] + def from_str(cls, string: str) -> Self: """ Read QcInput from string. @@ -380,7 +380,7 @@ def write_multi_job_file(job_list: list[QCInput], filename: str): file.write(QCInput.multi_job_string(job_list)) @classmethod - def from_file(cls, filename: str | Path) -> Self: # type: ignore[override] + def from_file(cls, filename: str | Path) -> Self: """ Create QcInput from file. diff --git a/src/pymatgen/io/vasp/inputs.py b/src/pymatgen/io/vasp/inputs.py index 351dbdcaefd..8c12dad8279 100644 --- a/src/pymatgen/io/vasp/inputs.py +++ b/src/pymatgen/io/vasp/inputs.py @@ -15,7 +15,6 @@ import re import subprocess import warnings -from collections.abc import Sequence from enum import Enum, unique from glob import glob from hashlib import sha256 @@ -40,7 +39,7 @@ from pymatgen.util.typing import Kpoint, Tuple3Floats, Tuple3Ints, Vector3D if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Iterator, Sequence from typing import Any, ClassVar, Literal from numpy.typing import ArrayLike @@ -1137,7 +1136,7 @@ def __init__( self.comment = comment self.num_kpts = num_kpts - self.kpts = kpts + self.kpts = kpts # type: ignore[assignment] self.style = style self.coord_type = coord_type self.kpts_weights = kpts_weights @@ -1186,10 +1185,10 @@ def __repr__(self) -> str: return "\n".join(lines) + "\n" @property - def kpts(self) -> Sequence[Kpoint]: + def kpts(self) -> list[Kpoint]: """A sequence of Kpoints, where each Kpoint is a tuple of 3 or 1.""" if all(isinstance(kpt, (list, tuple, np.ndarray)) and len(kpt) in {1, 3} for kpt in self._kpts): - return cast(Sequence[Kpoint], list(map(tuple, self._kpts))) # type: ignore[arg-type] + return list(map(tuple, self._kpts)) # type: ignore[arg-type] if all(isinstance(point, (int, float)) for point in self._kpts) and len(self._kpts) == 3: return [cast(Kpoint, tuple(self._kpts))] diff --git a/src/pymatgen/symmetry/structure.py b/src/pymatgen/symmetry/structure.py index a4761ea74bd..c44da9aa8fe 100644 --- a/src/pymatgen/symmetry/structure.py +++ b/src/pymatgen/symmetry/structure.py @@ -67,7 +67,7 @@ def __init__( self.wyckoff_letters = wyckoff_letters self.wyckoff_symbols = [f"{len(symb)}{symb[0]}" for symb in wyckoff_symbols] - def copy(self) -> Self: # type: ignore[override] + def copy(self) -> Self: """Make a copy of the SymmetrizedStructure.""" return type(self)( self, @@ -134,7 +134,7 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct: dict) -> Self: # type: ignore[override] + def from_dict(cls, dct: dict) -> Self: """ Args: dct (dict): Dict representation. diff --git a/tests/io/test_core.py b/tests/io/test_core.py index f3f08b46185..e0b9fa9db60 100644 --- a/tests/io/test_core.py +++ b/tests/io/test_core.py @@ -27,7 +27,7 @@ def get_str(self) -> str: return str(cw) @classmethod - def from_str(cls, contents: str) -> Self: # type: ignore[override] + def from_str(cls, contents: str) -> Self: struct = Structure.from_str(contents, fmt="cif") return cls(structure=struct)