From 2a55e4579aa61107f23305c8d921cfc1a00306ed Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Mon, 10 Jun 2024 11:00:48 +0800 Subject: [PATCH 01/39] add types for bandstructure --- .../electronic_structure/bandstructure.py | 244 ++++++++++-------- pymatgen/electronic_structure/boltztrap2.py | 15 +- pymatgen/io/lobster/__init__.py | 2 +- 3 files changed, 150 insertions(+), 111 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index f844674be2b..3ce071eb13d 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -7,7 +7,7 @@ import re import warnings from collections import defaultdict -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, overload import numpy as np from monty.json import MSONable @@ -20,6 +20,7 @@ if TYPE_CHECKING: from typing import Any + from numpy.typing import NDArray from typing_extensions import Self __author__ = "Geoffroy Hautier, Shyue Ping Ong, Michael Kocher" @@ -39,7 +40,7 @@ class Kpoint(MSONable): def __init__( self, - coords: np.ndarray, + coords: NDArray, lattice: Lattice, to_unit_cell: bool = False, coords_are_cartesian: bool = False, @@ -67,6 +68,20 @@ def __init__( self._cart_coords = lattice.get_cartesian_coords(self._frac_coords) + def __str__(self) -> str: + """String with fractional, Cartesian coordinates and label.""" + return f"{self.frac_coords} {self.cart_coords} {self.label}" + + def __eq__(self, other: object) -> bool: + """Check if two Kpoints are equal.""" + if not isinstance(other, type(self)): + return NotImplemented + return ( + np.allclose(self.frac_coords, other.frac_coords) + and self.lattice == other.lattice + and self.label == other.label + ) + @property def lattice(self) -> Lattice: """The lattice associated with the kpoint. It's a @@ -85,12 +100,12 @@ def label(self, label: str | None) -> None: self._label = label @property - def frac_coords(self) -> np.ndarray: + def frac_coords(self) -> NDArray: """The fractional coordinates of the kpoint as a numpy array.""" return np.copy(self._frac_coords) @property - def cart_coords(self) -> np.ndarray: + def cart_coords(self) -> NDArray: """The Cartesian coordinates of the kpoint as a numpy array.""" return np.copy(self._cart_coords) @@ -109,20 +124,6 @@ def c(self) -> float: """Fractional c coordinate of the kpoint.""" return self._frac_coords[2] - def __str__(self) -> str: - """Get a string with fractional, Cartesian coordinates and label.""" - return f"{self.frac_coords} {self.cart_coords} {self.label}" - - def __eq__(self, other: object) -> bool: - """Check if two kpoints are equal.""" - if not isinstance(other, Kpoint): - return NotImplemented - return ( - np.allclose(self.frac_coords, other.frac_coords) - and self.lattice == other.lattice - and self.label == other.label - ) - def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of a kpoint.""" return { @@ -150,7 +151,7 @@ def from_dict(cls, dct: dict) -> Self: class BandStructure: """This is the most generic band structure data possible - it's defined by a list of kpoints + energies for each of them. + it's defined by a list of Kpoints and energies for each of them. Attributes: kpoints (list): The list of kpoints (as Kpoint objects) in the band structure. @@ -170,18 +171,18 @@ class BandStructure: def __init__( self, - kpoints: np.ndarray, - eigenvals: dict[Spin, np.ndarray], + kpoints: NDArray, + eigenvals: dict[Spin, NDArray], lattice: Lattice, efermi: float, labels_dict=None, coords_are_cartesian: bool = False, structure: Structure | None = None, - projections: dict[Spin, np.ndarray] | None = None, + projections: dict[Spin, NDArray] | None = None, ) -> None: """ Args: - kpoints: list of kpoint as numpy arrays, in frac_coords of the + kpoints: list of Kpoint as numpy arrays, in frac_coords of the given lattice by default eigenvals: dict of energies for spin up and spin down {Spin.up:[][],Spin.down:[][]}, the first index of the array @@ -215,27 +216,27 @@ def __init__( if labels_dict is None: labels_dict = {} - if len(self.projections) != 0 and self.structure is None: + if self.projections and self.structure is None: raise RuntimeError("if projections are provided a structure object is also required") - for k in kpoints: - # let see if this kpoint has been assigned a label + for kpt in kpoints: + # Check if this Kpoint has a label label = None for c in labels_dict: - if np.linalg.norm(k - np.array(labels_dict[c])) < 0.0001: + if np.linalg.norm(kpt - np.array(labels_dict[c])) < 0.0001: label = c self.labels_dict[label] = Kpoint( - k, + kpt, lattice, label=label, coords_are_cartesian=coords_are_cartesian, ) - self.kpoints.append(Kpoint(k, lattice, label=label, coords_are_cartesian=coords_are_cartesian)) + self.kpoints.append(Kpoint(kpt, lattice, label=label, coords_are_cartesian=coords_are_cartesian)) self.bands = {spin: np.array(v) for spin, v in eigenvals.items()} self.nb_bands = len(eigenvals[Spin.up]) self.is_spin_polarized = len(self.bands) == 2 - def get_projection_on_elements(self): + def get_projection_on_elements(self) -> dict[Spin, NDArray]: """Get a dictionary of projections on elements. Returns: @@ -245,14 +246,14 @@ def get_projection_on_elements(self): returns an empty dict """ result = {} - for spin, v in self.projections.items(): - result[spin] = [[defaultdict(float) for i in range(len(self.kpoints))] for j in range(self.nb_bands)] + for spin, val in self.projections.items(): + result[spin] = [[defaultdict(float) for _ in range(len(self.kpoints))] for _ in range(self.nb_bands)] for i, j, k in itertools.product( range(self.nb_bands), range(len(self.kpoints)), range(len(self.structure)), ): - result[spin][i][j][str(self.structure[k].specie)] += np.sum(v[i, j, :, k]) + result[spin][i][j][str(self.structure[k].specie)] += np.sum(val[i, j, :, k]) return result def get_projections_on_elements_and_orbitals(self, el_orb_spec: dict[str, list[str]]): @@ -274,8 +275,8 @@ def get_projections_on_elements_and_orbitals(self, el_orb_spec: dict[str, list[s species_orb_spec = {get_el_sp(el): orbs for el, orbs in el_orb_spec.items()} for spin, v in self.projections.items(): result[spin] = [ - [{str(e): defaultdict(float) for e in species_orb_spec} for i in range(len(self.kpoints))] - for j in range(self.nb_bands) + [{str(e): defaultdict(float) for e in species_orb_spec} for _ in range(len(self.kpoints))] + for _ in range(self.nb_bands) ] for i, j, k in itertools.product( @@ -290,7 +291,7 @@ def get_projections_on_elements_and_orbitals(self, el_orb_spec: dict[str, list[s result[spin][i][j][str(sp)][o] += v[i][j][orb_i][k] return result - def is_metal(self, efermi_tol=1e-4) -> bool: + def is_metal(self, efermi_tol: float = 1e-4) -> bool: """Check if the band structure indicates a metal by looking if the fermi level crosses a band. @@ -303,7 +304,7 @@ def is_metal(self, efermi_tol=1e-4) -> bool: return True return False - def get_vbm(self): + def get_vbm(self) -> dict[str, Any]: """Get data about the VBM. Returns: @@ -335,25 +336,26 @@ def get_vbm(self): max_tmp = -float("inf") index = kpoint_vbm = None for value in self.bands.values(): - for i, j in zip(*np.where(value < self.efermi)): - if value[i, j] > max_tmp: - max_tmp = float(value[i, j]) + for idx, j in zip(*np.where(value < self.efermi)): + if value[idx, j] > max_tmp: + max_tmp = float(value[idx, j]) index = j kpoint_vbm = self.kpoints[j] list_ind_kpts = [] if kpoint_vbm.label is not None: - for i, kpt in enumerate(self.kpoints): + for idx, kpt in enumerate(self.kpoints): if kpt.label == kpoint_vbm.label: - list_ind_kpts.append(i) + list_ind_kpts.append(idx) else: list_ind_kpts.append(index) - # get all other bands sharing the vbm + + # Get all other bands sharing the vbm list_ind_band = defaultdict(list) for spin in self.bands: - for i in range(self.nb_bands): - if math.fabs(self.bands[spin][i][index] - max_tmp) < 0.001: - list_ind_band[spin].append(i) + for idx in range(self.nb_bands): + if math.fabs(self.bands[spin][idx][index] - max_tmp) < 0.001: + list_ind_band[spin].append(idx) proj = {} for spin, value in self.projections.items(): if len(list_ind_band[spin]) == 0: @@ -367,7 +369,7 @@ def get_vbm(self): "projections": proj, } - def get_cbm(self): + def get_cbm(self) -> dict[str, Any]: """Get data about the CBM. Returns: @@ -399,26 +401,26 @@ def get_cbm(self): index = kpoint_cbm = None for value in self.bands.values(): - for i, j in zip(*np.where(value >= self.efermi)): - if value[i, j] < max_tmp: - max_tmp = float(value[i, j]) + for idx, j in zip(*np.where(value >= self.efermi)): + if value[idx, j] < max_tmp: + max_tmp = float(value[idx, j]) index = j kpoint_cbm = self.kpoints[j] list_index_kpoints = [] if kpoint_cbm.label is not None: - for i, kpt in enumerate(self.kpoints): + for idx, kpt in enumerate(self.kpoints): if kpt.label == kpoint_cbm.label: - list_index_kpoints.append(i) + list_index_kpoints.append(idx) else: list_index_kpoints.append(index) # get all other bands sharing the cbm list_index_band = defaultdict(list) for spin in self.bands: - for i in range(self.nb_bands): - if math.fabs(self.bands[spin][i][index] - max_tmp) < 0.001: - list_index_band[spin].append(i) + for idx in range(self.nb_bands): + if math.fabs(self.bands[spin][idx][index] - max_tmp) < 0.001: + list_index_band[spin].append(idx) proj = {} for spin, value in self.projections.items(): if len(list_index_band[spin]) == 0: @@ -433,22 +435,25 @@ def get_cbm(self): "projections": proj, } - def get_band_gap(self): + def get_band_gap(self) -> dict[str, Any]: r"""Get band gap data. Returns: - A dict {"energy","direct","transition"}: + A dict {"energy", "direct", "transition"}: "energy": band gap energy "direct": A boolean telling if the gap is direct or not "transition": kpoint labels of the transition (e.g., "\\Gamma-X") """ if self.is_metal(): return {"energy": 0.0, "direct": False, "transition": None} + cbm = self.get_cbm() vbm = self.get_vbm() - result = {"direct": False, "energy": 0.0, "transition": None} - - result["energy"] = cbm["energy"] - vbm["energy"] + result = { + "direct": False, + "transition": None, + "energy": cbm["energy"] - vbm["energy"], + } if (cbm["kpoint"].label is not None and cbm["kpoint"].label == vbm["kpoint"].label) or np.linalg.norm( cbm["kpoint"].cart_coords - vbm["kpoint"].cart_coords @@ -464,7 +469,7 @@ def get_band_gap(self): return result - def get_direct_band_gap_dict(self): + def get_direct_band_gap_dict(self) -> dict[Spin, dict[str, Any]]: """Get a dictionary of information about the direct band gap. @@ -493,7 +498,7 @@ def get_direct_band_gap_dict(self): } return direct_gap_dict - def get_direct_band_gap(self): + def get_direct_band_gap(self) -> float: """Get the direct band gap. Returns: @@ -501,14 +506,20 @@ def get_direct_band_gap(self): """ if self.is_metal(): return 0.0 + dg = self.get_direct_band_gap_dict() return min(v["value"] for v in dg.values()) - def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol: float = 1e-2): - """Get a list of unique symmetrically equivalent k-points. + def get_sym_eq_kpoints( + self, + kpoint: NDArray, + cartesian: bool = False, + tol: float = 1e-2, + ) -> NDArray: + """Get a list of unique symmetrically equivalent Kpoints. Args: - kpoint (1x3 array): coordinate of the k-point + kpoint (1x3 array): coordinate of the Kpoint cartesian (bool): kpoint is in Cartesian or fractional coordinates tol (float): tolerance below which coordinates are considered equal @@ -517,11 +528,12 @@ def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol: float = 1e-2): """ if not self.structure: return None + sg = SpacegroupAnalyzer(self.structure) symm_ops = sg.get_point_group_operations(cartesian=cartesian) points = np.dot(kpoint, [m.rotation_matrix for m in symm_ops]) rm_list = [] - # identify and remove duplicates from the list of equivalent k-points: + # Identify and remove duplicates from the list of equivalent k-points: for i in range(len(points) - 1): for j in range(i + 1, len(points)): if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol): @@ -529,7 +541,12 @@ def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol: float = 1e-2): break return np.delete(points, rm_list, axis=0) - def get_kpoint_degeneracy(self, kpoint, cartesian=False, tol: float = 1e-2): + def get_kpoint_degeneracy( + self, + kpoint: NDArray, + cartesian: bool = False, + tol: float = 1e-2, + ) -> NDArray | None: """Get degeneracy of a given k-point based on structure symmetry. Args: @@ -541,11 +558,9 @@ def get_kpoint_degeneracy(self, kpoint, cartesian=False, tol: float = 1e-2): int | None: degeneracy or None if structure is not available """ all_kpts = self.get_sym_eq_kpoints(kpoint, cartesian, tol=tol) - if all_kpts is not None: - return len(all_kpts) - return None + return len(all_kpts) if all_kpts is not None else None - def as_dict(self): + def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of BandStructure.""" dct = { "@module": type(self).__module__, @@ -581,7 +596,7 @@ def as_dict(self): # MongoDB does not accept keys starting with $. Add a blank space to fix the problem for c, label in self.labels_dict.items(): - mongo_key = c if not c.startswith("$") else f" {c}" + mongo_key = f" {c}" if c.startswith("$") else c dct["labels_dict"][mongo_key] = label.as_dict()["fcoords"] dct["projections"] = {} if len(self.projections) != 0: @@ -590,7 +605,7 @@ def as_dict(self): return dct @classmethod - def from_dict(cls, dct: dict) -> Self: + def from_dict(cls, dct: dict[str, Any]) -> Self: """Create from dict. Args: @@ -638,7 +653,7 @@ def from_dict(cls, dct: dict) -> Self: return cls.from_old_dict(dct) @classmethod - def from_old_dict(cls, dct) -> Self: + def from_old_dict(cls, dct: dict[str, Any]) -> Self: """ Args: dct (dict): A dict with all data for a band structure symmetry line object. @@ -687,14 +702,14 @@ class BandStructureSymmLine(BandStructure, MSONable): def __init__( self, - kpoints, - eigenvals, - lattice, - efermi, - labels_dict, - coords_are_cartesian=False, - structure=None, - projections=None, + kpoints: NDArray, + eigenvals: dict[Spin, list], + lattice: Lattice, + efermi: float, + labels_dict: dict, + coords_are_cartesian: bool = False, + structure: Structure | None = None, + projections: dict[Spin, NDArray] | None = None, ) -> None: """ Args: @@ -735,7 +750,7 @@ def __init__( self.branches = [] one_group: list = [] branches_tmp = [] - # get labels and distance for each kpoint + # Get labels and distance for each kpoint previous_kpoint = self.kpoints[0] previous_distance = 0.0 @@ -770,7 +785,7 @@ def __init__( if len(self.bands) == 2: self.is_spin_polarized = True - def get_equivalent_kpoints(self, index): + def get_equivalent_kpoints(self, index: int) -> list[int]: """Get the list of kpoint indices equivalent (meaning they are the same frac coords) to the given one. @@ -783,20 +798,20 @@ def get_equivalent_kpoints(self, index): TODO: now it uses the label we might want to use coordinates instead (in case there was a mislabel) """ - # if the kpoint has no label it can't have a repetition along the band - # structure line object + # If the kpoint has no label it can't have a repetition + # along the band structure line object if self.kpoints[index].label is None: return [index] list_index_kpoints = [] - for i, kpt in enumerate(self.kpoints): + for idx, kpt in enumerate(self.kpoints): if kpt.label == self.kpoints[index].label: - list_index_kpoints.append(i) + list_index_kpoints.append(idx) return list_index_kpoints - def get_branch(self, index): + def get_branch(self, index: int) -> list[dict[str, Any]]: r"""Get in what branch(es) is the kpoint. There can be several branches. @@ -823,7 +838,7 @@ def get_branch(self, index): ) return to_return - def apply_scissor(self, new_band_gap): + def apply_scissor(self, new_band_gap: float) -> Self: """Apply a scissor operator (shift of the CBM) to fit the given band gap. If it's a metal, we look for the band crossing the Fermi level and shift this one up. This will not work all the time for metals! @@ -835,7 +850,7 @@ def apply_scissor(self, new_band_gap): BandStructureSymmLine: with the applied scissor shift """ if self.is_metal(): - # moves then the highest index band crossing the Fermi level find this band... + # Move then the highest index band crossing the Fermi level find this band... max_index = -1000 # spin_index = None for idx in range(self.nb_bands): @@ -867,6 +882,7 @@ def apply_scissor(self, new_band_gap): for v in range(len(old_dict["bands"][spin][k])): if k >= max_index: old_dict["bands"][spin][k][v] = old_dict["bands"][spin][k][v] + shift + else: shift = new_band_gap - self.get_band_gap()["energy"] old_dict = self.as_dict() @@ -876,9 +892,10 @@ def apply_scissor(self, new_band_gap): if old_dict["bands"][spin][k][v] >= old_dict["cbm"]["energy"]: old_dict["bands"][spin][k][v] = old_dict["bands"][spin][k][v] + shift old_dict["efermi"] = old_dict["efermi"] + shift + return self.from_dict(old_dict) - def as_dict(self): + def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of BandStructureSymmLine.""" dct = super().as_dict() dct["branches"] = self.branches @@ -888,7 +905,7 @@ def as_dict(self): class LobsterBandStructureSymmLine(BandStructureSymmLine): """Lobster subclass of BandStructure with customized functions.""" - def as_dict(self): + def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of BandStructureSymmLine.""" dct = { "@module": type(self).__module__, @@ -921,6 +938,7 @@ def as_dict(self): dct["band_gap"] = self.get_band_gap() dct["labels_dict"] = {} dct["is_spin_polarized"] = self.is_spin_polarized + # MongoDB does not accept keys starting with $. Add a blank space to fix the problem for c, label in self.labels_dict.items(): mongo_key = c if not c.startswith("$") else " " + c @@ -931,7 +949,7 @@ def as_dict(self): return dct @classmethod - def from_dict(cls, dct: dict) -> Self: + def from_dict(cls, dct: dict[str, Any]) -> Self: """ Args: dct (dict): A dict with all data for a band structure symmetry line @@ -970,7 +988,7 @@ def from_dict(cls, dct: dict) -> Self: return cls.from_old_dict(dct) @classmethod - def from_old_dict(cls, dct) -> Self: + def from_old_dict(cls, dct: dict[str, Any]) -> Self: """ Args: dct (dict): A dict with all data for a band structure symmetry line @@ -1005,7 +1023,7 @@ def from_old_dict(cls, dct) -> Self: projections=projections, ) - def get_projection_on_elements(self): + def get_projection_on_elements(self) -> dict[Spin, list]: """Get a dictionary of projections on elements. It sums over all available orbitals for each element. @@ -1017,7 +1035,7 @@ def get_projection_on_elements(self): """ result = {} for spin, v in self.projections.items(): - result[spin] = [[defaultdict(float) for i in range(len(self.kpoints))] for j in range(self.nb_bands)] + result[spin] = [[defaultdict(float) for _ in range(len(self.kpoints))] for _ in range(self.nb_bands)] for i, j in itertools.product(range(self.nb_bands), range(len(self.kpoints))): for key, item in v[i][j].items(): for item2 in item.values(): @@ -1025,7 +1043,10 @@ def get_projection_on_elements(self): result[spin][i][j][specie] += item2 return result - def get_projections_on_elements_and_orbitals(self, el_orb_spec): + def get_projections_on_elements_and_orbitals( + self, + el_orb_spec: dict[Element, list], + ) -> dict[Spin, list]: """Return a dictionary of projections on elements and specific orbitals. Args: @@ -1040,12 +1061,12 @@ def get_projections_on_elements_and_orbitals(self, el_orb_spec): if there is no projections in the band structure returns an empty dict. """ - result = {} + result: dict[Spin, list] = {} el_orb_spec = {get_el_sp(el): orbs for el, orbs in el_orb_spec.items()} for spin, v in self.projections.items(): result[spin] = [ - [{str(e): defaultdict(float) for e in el_orb_spec} for i in range(len(self.kpoints))] - for j in range(self.nb_bands) + [{str(e): defaultdict(float) for e in el_orb_spec} for _ in range(len(self.kpoints))] + for _ in range(self.nb_bands) ] for i, j in itertools.product(range(self.nb_bands), range(len(self.kpoints))): @@ -1057,7 +1078,26 @@ def get_projections_on_elements_and_orbitals(self, el_orb_spec): return result -def get_reconstructed_band_structure(list_bs, efermi=None): +@overload +def get_reconstructed_band_structure( + list_bs: list[BandStructure], + efermi: float | None = None, +) -> BandStructure: + pass + + +@overload +def get_reconstructed_band_structure( + list_bs: list[BandStructureSymmLine], + efermi: float | None = None, +) -> BandStructureSymmLine: + pass + + +def get_reconstructed_band_structure( + list_bs: list[BandStructure] | list[BandStructureSymmLine], + efermi: float | None = None, +) -> BandStructure | BandStructureSymmLine: """Take a list of band structures and reconstructs one band structure object from all of them. diff --git a/pymatgen/electronic_structure/boltztrap2.py b/pymatgen/electronic_structure/boltztrap2.py index d3ed99d5167..50d09d116ac 100644 --- a/pymatgen/electronic_structure/boltztrap2.py +++ b/pymatgen/electronic_structure/boltztrap2.py @@ -1,10 +1,10 @@ -"""BoltzTraP2 is a python software interpolating band structures and -computing materials properties from dft band structure using Boltzmann -semi-classical transport theory. -This module provides a pymatgen interface to BoltzTraP2. +"""This module provides a pymatgen interface to BoltzTraP2. Some of the code is written following the examples provided in BoltzTraP2. -BoltzTraP2 has been developed by Georg Madsen, Jesús Carrete, Matthieu J. Verstraete. +BoltzTraP2 is a Python software interpolating band structures and +computing materials properties from dft band structure using Boltzmann +semi-classical transport theory, developed by Georg Madsen, Jesús Carrete, +Matthieu J. Verstraete. https://gitlab.com/sousaw/BoltzTraP2 https://www.sciencedirect.com/science/article/pii/S0010465518301632 @@ -21,9 +21,8 @@ Computer Physics Communications, 175, 67-71 Todo: -- DONE: spin polarized bands -- read first derivative of the eigenvalues from vasprun.xml (mommat) -- handle magnetic moments (magmom) +- Read first derivative of the eigenvalues from vasprun.xml (mommat) +- Handle magnetic moments (MAGMOM) """ from __future__ import annotations diff --git a/pymatgen/io/lobster/__init__.py b/pymatgen/io/lobster/__init__.py index f5e4a5f4234..e4b1100a2d6 100644 --- a/pymatgen/io/lobster/__init__.py +++ b/pymatgen/io/lobster/__init__.py @@ -1,5 +1,5 @@ """ -This package implements modules for input and output to and from Lobster. It +This package implements modules for input and output to and from LOBSTER. It imports the key classes form both lobster.inputs and lobster_outputs to allow most classes to be simply called as pymatgen.io.lobster.Lobsterin for example, to retain backwards compatibility. From 1cd0037abc2f19fcef2a28b9dec3614ff7bc9df7 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Mon, 10 Jun 2024 11:45:54 +0800 Subject: [PATCH 02/39] relocate magic methods to top --- pymatgen/electronic_structure/dos.py | 83 ++++++++++++++-------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/pymatgen/electronic_structure/dos.py b/pymatgen/electronic_structure/dos.py index be074213ca6..fc7378290a2 100644 --- a/pymatgen/electronic_structure/dos.py +++ b/pymatgen/electronic_structure/dos.py @@ -189,6 +189,33 @@ def __init__( vol = norm_vol or 1 self.densities = {k: np.array(d) / vol for k, d in densities.items()} + def __add__(self, other): + """Add two DOS together. Checks that energy scales are the same. + Otherwise, a ValueError is thrown. + + Args: + other: Another DOS object. + + Returns: + Sum of the two DOSs. + """ + if not all(np.equal(self.energies, other.energies)): + raise ValueError("Energies of both DOS are not compatible!") + densities = {spin: self.densities[spin] + other.densities[spin] for spin in self.densities} + return Dos(self.efermi, self.energies, densities) + + def __str__(self) -> str: + """Get a string which can be easily plotted (using gnuplot).""" + if Spin.down in self.densities: + str_arr = [f"#{'Energy':30s} {'DensityUp':30s} {'DensityDown':30s}"] + for i, energy in enumerate(self.energies): + str_arr.append(f"{energy:.5f} {self.densities[Spin.up][i]:.5f} {self.densities[Spin.down][i]:.5f}") + else: + str_arr = [f"#{'Energy':30s} {'DensityUp':30s}"] + for i, energy in enumerate(self.energies): + str_arr.append(f"{energy:.5f} {self.densities[Spin.up][i]:.5f}") + return "\n".join(str_arr) + def get_densities(self, spin: Spin | None = None): """Get the density of states for a particular spin. @@ -200,8 +227,9 @@ def get_densities(self, spin: Spin | None = None): None, the sum of all spins is returned. """ if self.densities is None: - result = None - elif spin is None: + return None + + if spin is None: if Spin.down in self.densities: result = self.densities[Spin.up] + self.densities[Spin.down] else: @@ -227,21 +255,6 @@ def get_smeared_densities(self, sigma: float): smeared_dens[spin] = gaussian_filter1d(dens, sigma / avg_diff) return smeared_dens - def __add__(self, other): - """Add two DOS together. Checks that energy scales are the same. - Otherwise, a ValueError is thrown. - - Args: - other: Another DOS object. - - Returns: - Sum of the two DOSs. - """ - if not all(np.equal(self.energies, other.energies)): - raise ValueError("Energies of both DOS are not compatible!") - densities = {spin: self.densities[spin] + other.densities[spin] for spin in self.densities} - return Dos(self.efermi, self.energies, densities) - def get_interpolated_value(self, energy: float) -> dict[Spin, float]: """Get interpolated density for a particular energy. @@ -341,18 +354,6 @@ def get_gap(self, tol: float = 0.001, abs_tol: bool = False, spin: Spin | None = cbm, vbm = self.get_cbm_vbm(tol, abs_tol, spin) return max(cbm - vbm, 0.0) - def __str__(self) -> str: - """Get a string which can be easily plotted (using gnuplot).""" - if Spin.down in self.densities: - str_arr = [f"#{'Energy':30s} {'DensityUp':30s} {'DensityDown':30s}"] - for i, energy in enumerate(self.energies): - str_arr.append(f"{energy:.5f} {self.densities[Spin.up][i]:.5f} {self.densities[Spin.down][i]:.5f}") - else: - str_arr = [f"#{'Energy':30s} {'DensityUp':30s}"] - for i, energy in enumerate(self.energies): - str_arr.append(f"{energy:.5f} {self.densities[Spin.up][i]:.5f}") - return "\n".join(str_arr) - @classmethod def from_dict(cls, dct: dict) -> Self: """Get Dos object from dict representation of Dos.""" @@ -626,6 +627,9 @@ def __init__( self.pdos = pdoss self.structure = structure + def __str__(self) -> str: + return f"Complete DOS for {self.structure}" + def get_normalized(self) -> CompleteDos: """Get a normalized version of the CompleteDos.""" if self.norm_vol is not None: @@ -775,7 +779,7 @@ def spin_polarization(self) -> float | None: n_F_down = n_F[Spin.down] if (n_F_up + n_F_down) == 0: - # only well defined for metals or half-metals + # Only well defined for metals or half-metals return float("NaN") spin_polarization = (n_F_up - n_F_down) / (n_F_up + n_F_down) @@ -1169,7 +1173,7 @@ class fingerprint(NamedTuple): dos_rebin = np.zeros(ener.shape) - for ii, e1, e2 in zip(range(len(ener)), ener_bounds[0:-1], ener_bounds[1:]): + for ii, e1, e2 in zip(range(len(ener)), ener_bounds[:-1], ener_bounds[1:]): inds = np.where((energies >= e1) & (energies < e2)) dos_rebin[ii] = np.sum(densities[inds]) if normalize: # scale DOS bins to make area under histogram equal 1 @@ -1291,9 +1295,6 @@ def as_dict(self) -> dict: dct["spd_dos"] = {str(orb): dos.as_dict() for orb, dos in self.get_spd_dos().items()} return dct - def __str__(self) -> str: - return f"Complete DOS for {self.structure}" - class LobsterCompleteDos(CompleteDos): """Extended CompleteDOS for Lobster.""" @@ -1312,7 +1313,7 @@ def get_site_orbital_dos(self, site: PeriodicSite, orbital: str) -> Dos: # type Returns: Dos containing densities of an orbital of a specific site. """ - if orbital[1:] not in [ + if orbital[1:] not in { "s", "p_y", "p_z", @@ -1329,7 +1330,7 @@ def get_site_orbital_dos(self, site: PeriodicSite, orbital: str) -> Dos: # type "f_xz^2", "f_z(x^2-y^2)", "f_x(x^2-3y^2)", - ]: + }: raise ValueError("orbital is not correct") return Dos(self.efermi, self.energies, self.pdos[site][orbital]) # type: ignore @@ -1457,8 +1458,8 @@ def _get_orb_type_lobster(orb) -> OrbitalType | None: Returns: OrbitalType """ - orb_labs = ["s", "p_y", "p_z", "p_x", "d_xy", "d_yz", "d_z^2", "d_xz", "d_x^2-y^2"] - orb_labs += ["f_y(3x^2-y^2)", "f_xyz", "f_yz^2", "f_z^3", "f_xz^2", "f_z(x^2-y^2)", "f_x(x^2-3y^2)"] + orb_labs = ("s", "p_y", "p_z", "p_x", "d_xy", "d_yz", "d_z^2", "d_xz", "d_x^2-y^2") + orb_labs += ("f_y(3x^2-y^2)", "f_xyz", "f_yz^2", "f_z^3", "f_xz^2", "f_z(x^2-y^2)", "f_x(x^2-3y^2)") try: orbital = Orbital(orb_labs.index(orb[1:])) @@ -1476,7 +1477,7 @@ def _get_orb_lobster(orb): Returns: Orbital. """ - orb_labs = [ + orb_labs = ( "s", "p_y", "p_z", @@ -1493,10 +1494,10 @@ def _get_orb_lobster(orb): "f_xz^2", "f_z(x^2-y^2)", "f_x(x^2-3y^2)", - ] + ) try: return Orbital(orb_labs.index(orb[1:])) except AttributeError: print("Orb not in list") - return None + return None From 79e8868a7c21a5877e8193222b6b108779896735 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Mon, 10 Jun 2024 11:46:33 +0800 Subject: [PATCH 03/39] add some types --- .../electronic_structure/bandstructure.py | 2 +- pymatgen/electronic_structure/cohp.py | 198 ++++++++++++------ pymatgen/electronic_structure/core.py | 180 ++++++++-------- 3 files changed, 227 insertions(+), 153 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index 3ce071eb13d..ee12fa1148e 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -799,7 +799,7 @@ def get_equivalent_kpoints(self, index: int) -> list[int]: (in case there was a mislabel) """ # If the kpoint has no label it can't have a repetition - # along the band structure line object + # along the BandStructureSymmLine object if self.kpoints[index].label is None: return [index] diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index bd1fa4b794f..e9eaa928bf7 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -1,6 +1,7 @@ """This module defines classes to represent crystal orbital Hamilton -populations (COHP) and integrated COHP (ICOHP), but can also be used +populations (COHP) and integrated COHP (ICOHP), and can also be used for crystal orbital overlap populations (COOP) or crystal orbital bond indices (COBIs). + If you use this module, please cite: J. George, G. Petretto, A. Naik, M. Esters, A. J. Jackson, R. Nelson, R. Dronskowski, G.-M. Rignanese, G. Hautier, "Automated Bonding Analysis with Crystal Orbital Hamilton Populations", @@ -29,8 +30,10 @@ from pymatgen.util.num import round_to_sigfigs if TYPE_CHECKING: - from typing import Any + from collections.abc import Sequence + from typing import Any, Literal + from numpy.typing import NDArray from typing_extensions import Self __author__ = "Marco Esters, Janine George" @@ -50,16 +53,23 @@ class Cohp(MSONable): """Basic COHP object.""" def __init__( - self, efermi, energies, cohp, are_coops=False, are_cobis=False, are_multi_center_cobis=False, icohp=None + self, + efermi: float, + energies: Sequence[float], + cohp: dict[Spin, NDArray], + are_coops: bool = False, + are_cobis: bool = False, + are_multi_center_cobis: bool = False, + icohp: dict[Spin, NDArray] | None = None, ) -> None: """ Args: - are_coops: Indicates whether this object describes COOPs. - are_cobis: Indicates whether this object describes COBIs. - are_multi_center_cobis: Indicates whether this object describes multi-center COBIs efermi: Fermi energy. energies: A sequence of energies. cohp ({Spin: np.array}): representing the COHP for each spin. + are_coops: Indicates whether this object describes COOPs. + are_cobis: Indicates whether this object describes COBIs. + are_multi_center_cobis: Indicates whether this object describes multi-center COBIs icohp ({Spin: np.array}): representing the ICOHP for each spin. """ self.are_coops = are_coops @@ -97,7 +107,7 @@ def __repr__(self) -> str: str_arr.append(format_data.format(*(d[idx] for d in data))) return "\n".join(str_arr) - def as_dict(self): + def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of COHP.""" dct = { "@module": type(self).__module__, @@ -113,11 +123,15 @@ def as_dict(self): dct["ICOHP"] = {str(spin): pops.tolist() for spin, pops in self.icohp.items()} return dct - def get_cohp(self, spin=None, integrated=False): + def get_cohp( + self, + spin: Spin | Literal[-1, 1, "up", "down"] | None = None, + integrated: bool = False, + ) -> dict[Spin, NDArray]: """Get the COHP or ICOHP for a particular spin. Args: - spin: Spin. Can be parsed as spin object, integer (-1/1) + spin: Can be Spin object, integer (-1/1) or str ("up"/"down") integrated: Return COHP (False) or ICOHP (True) @@ -126,7 +140,7 @@ def get_cohp(self, spin=None, integrated=False): None and both spins are present, both spins will be returned as a dictionary. """ - populations = self.cohp if not integrated else self.icohp + populations = self.icohp if integrated else self.cohp if populations is None: return None @@ -138,11 +152,18 @@ def get_cohp(self, spin=None, integrated=False): spin = Spin({"up": 1, "down": -1}[spin.lower()]) return {spin: populations[spin]} - def get_icohp(self, spin=None): - """Convenient alternative to get the ICOHP for a particular spin.""" + def get_icohp( + self, + spin: Spin | Literal[-1, 1, "up", "down"] | None = None, + ) -> dict[Spin, NDArray]: + """Convenient wrapper to get the ICOHP for a particular spin.""" return self.get_cohp(spin=spin, integrated=True) - def get_interpolated_value(self, energy, integrated=False): + def get_interpolated_value( + self, + energy: float, + integrated: bool = False, + ) -> dict[Spin, float]: """Get the COHP for a particular energy. Args: @@ -159,7 +180,11 @@ def get_interpolated_value(self, energy, integrated=False): raise ValueError("ICOHP is empty.") return inter - def has_antibnd_states_below_efermi(self, spin=None, limit=0.01): + def has_antibnd_states_below_efermi( + self, + spin: Spin | Literal[-1, 1, "up", "down"] | None = None, + limit: float = 0.01, + ) -> dict[Spin, bool] | None: """Get dict indicating if there are antibonding states below the Fermi level depending on the spin spin: Spin limit: -COHP smaller -limit will be considered. @@ -169,23 +194,18 @@ def has_antibnd_states_below_efermi(self, spin=None, limit=0.01): if populations is None: return None + + dict_to_return = {} if spin is None: - dict_to_return = {} for sp, cohp_vals in populations.items(): - if (max(cohp_vals[0:n_energies_below_efermi])) > limit: - dict_to_return[sp] = True - else: - dict_to_return[sp] = False + dict_to_return[sp] = (max(cohp_vals[:n_energies_below_efermi])) > limit + else: - dict_to_return = {} if isinstance(spin, int): spin = Spin(spin) elif isinstance(spin, str): spin = Spin({"up": 1, "down": -1}[spin.lower()]) - if (max(populations[spin][0:n_energies_below_efermi])) > limit: - dict_to_return[spin] = True - else: - dict_to_return[spin] = False + dict_to_return[spin] = (max(populations[spin][:n_energies_below_efermi])) > limit return dict_to_return @@ -223,19 +243,19 @@ class CompleteCohp(Cohp): def __init__( self, - structure, - avg_cohp, - cohp_dict, - bonds=None, - are_coops=False, - are_cobis=False, - are_multi_center_cobis=False, - orb_res_cohp=None, + structure: Structure, + avg_cohp: Cohp, + cohp_dict: dict[str, Cohp], + bonds: dict | None = None, + are_coops: bool = False, + are_cobis: bool = False, + are_multi_center_cobis: bool = False, + orb_res_cohp=None, # TODO: add type ) -> None: """ Args: structure: Structure associated with this COHP. - avg_cohp: The average cohp as a COHP object. + avg_cohp: The average COHP as a Cohp object. cohp_dict: A dict of COHP objects for individual bonds of the form {label: COHP} bonds: A dict containing information on the bonds of the form @@ -257,6 +277,7 @@ def __init__( or (are_cobis and are_multi_center_cobis) ): raise ValueError("You cannot have info about COOPs, COBIs and/or multi-center COBIS in the same file.") + super().__init__( avg_cohp.efermi, avg_cohp.energies, @@ -281,7 +302,7 @@ def __str__(self) -> str: return f"Complete COBIs for {self.structure}" return f"Complete COHPs for {self.structure}" - def as_dict(self): + def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of CompleteCohp.""" dct = { "@module": type(self).__module__, @@ -332,11 +353,15 @@ def as_dict(self): return dct - def get_cohp_by_label(self, label, summed_spin_channels=False): + def get_cohp_by_label( + self, + label: str, # TODO: can this be a float/int (according to the following docstring) + summed_spin_channels: bool = False, + ) -> Cohp: """Get specific COHP object. Args: - label: string (for newer Lobster versions: a number) + label: string (for newer LOBSTER versions: a number) summed_spin_channels: bool, will sum the spin channels and return the sum in Spin.up if true Returns: @@ -368,7 +393,12 @@ def get_cohp_by_label(self, label, summed_spin_channels=False): icohp=final_icohp, ) - def get_summed_cohp_by_label_list(self, label_list, divisor=1, summed_spin_channels=False): + def get_summed_cohp_by_label_list( + self, + label_list, + divisor=1, + summed_spin_channels=False, + ): """Get a COHP object that includes a summed COHP divided by divisor. Args: @@ -422,7 +452,11 @@ def get_summed_cohp_by_label_list(self, label_list, divisor=1, summed_spin_chann ) def get_summed_cohp_by_label_and_orbital_list( - self, label_list, orbital_list, divisor=1, summed_spin_channels=False + self, + label_list, + orbital_list, + divisor=1, + summed_spin_channels=False, ): """Get a COHP object that includes a summed COHP divided by divisor. @@ -479,7 +513,12 @@ def get_summed_cohp_by_label_and_orbital_list( icohp=final_icohp, ) - def get_orbital_resolved_cohp(self, label, orbitals, summed_spin_channels=False): + def get_orbital_resolved_cohp( + self, + label, + orbitals, + summed_spin_channels=False, + ): """Get orbital-resolved COHP. Args: @@ -548,8 +587,10 @@ def get_orbital_resolved_cohp(self, label, orbitals, summed_spin_channels=False) @classmethod def from_dict(cls, dct: dict) -> Self: - """Get CompleteCohp object from dict representation.""" - # TODO: clean that mess up? + """Get CompleteCohp object from dict representation. + + TODO: clean that up? + """ cohp_dict = {} efermi = dct["efermi"] energies = dct["energies"] @@ -666,7 +707,7 @@ def from_file( cls, fmt, filename=None, structure_file=None, are_coops=False, are_cobis=False, are_multi_center_cobis=False ) -> Self: """ - Creates a CompleteCohp object from an output file of a COHP + Create a CompleteCohp object from an output file of a COHP calculation. Valid formats are either LMTO (for the Stuttgart LMTO-ASA code) or LOBSTER (for the LOBSTER code). @@ -733,7 +774,7 @@ def from_file( cohp_data = cohp_file.cohp_data energies = cohp_file.energies - # Lobster shifts the energies so that the Fermi energy is at zero. + # LOBSTER shifts the energies so that the Fermi energy is at zero. # Shifting should be done by the plotter object though. spins = [Spin.up, Spin.down] if cohp_file.is_spin_polarized else [Spin.up] @@ -866,11 +907,21 @@ class IcohpValue(MSONable): are_cobis (bool): Whether the object is a COBIS/ICOBIS or not. icohp (dict): A dictionary of the ICOHP/COHP values. The keys are Spin.up and Spin.down. summed_icohp (float): The summed ICOHP/COHP values. - num_bonds (int): The number of bonds used for the average COHP (relevant for Lobster versions <3.0). + num_bonds (int): The number of bonds used for the average COHP (relevant for LOBSTER versions <3.0). """ def __init__( - self, label, atom1, atom2, length, translation, num, icohp, are_coops=False, are_cobis=False, orbitals=None + self, + label, + atom1, + atom2, + length, + translation, + num, + icohp, + are_coops=False, + are_cobis=False, + orbitals=None, ) -> None: """ Args: @@ -888,6 +939,7 @@ def __init__( """ if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") + self._are_coops = are_coops self._are_cobis = are_cobis self._label = label @@ -898,10 +950,7 @@ def __init__( self._num = num self._icohp = icohp self._orbitals = orbitals - if Spin.down in self._icohp: - self._is_spin_polarized = True - else: - self._is_spin_polarized = False + self._is_spin_polarized = Spin.down in self._icohp def __str__(self) -> str: """String representation of the ICOHP/ICOOP.""" @@ -915,6 +964,7 @@ def __str__(self) -> str: f"ICOHP {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " f"{self._icohp[Spin.up]} eV (Spin up)" ) + if self._are_coops and not self._are_cobis: if self._is_spin_polarized: return ( @@ -925,6 +975,7 @@ def __str__(self) -> str: f"ICOOP {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " f"{self._icohp[Spin.up]} eV (Spin up)" ) + if self._are_cobis and not self._are_coops: if self._is_spin_polarized: return ( @@ -940,7 +991,7 @@ def __str__(self) -> str: @property def num_bonds(self): - """Tells the number of bonds for which the ICOHP value is an average. + """The number of bonds for which the ICOHP value is an average. Returns: Int. @@ -949,7 +1000,7 @@ def num_bonds(self): @property def are_coops(self) -> bool: - """Tells if ICOOPs or not. + """Check if ICOOPs or not. Returns: Boolean. @@ -958,7 +1009,7 @@ def are_coops(self) -> bool: @property def are_cobis(self) -> bool: - """Tells if ICOBIs or not. + """Check if ICOBIs or not. Returns: Boolean. @@ -967,7 +1018,7 @@ def are_cobis(self) -> bool: @property def is_spin_polarized(self) -> bool: - """Tells if spin polarized calculation or not. + """Check if is spin polarized calculation. Returns: Boolean. @@ -1013,7 +1064,7 @@ def icohp(self): @property def summed_icohp(self): - """Sums ICOHPs of both spin channels for spin polarized compounds. + """Sum ICOHPs of both spin channels for spin polarized compounds. Returns: float: icohp value in eV. @@ -1022,7 +1073,7 @@ def summed_icohp(self): @property def summed_orbital_icohp(self): - """Sums orbital-resolved ICOHPs of both spin channels for spin-polarized compounds. + """Sum orbital-resolved ICOHPs of both spin channels for spin-polarized compounds. Returns: dict[str, float]: "str(Orbital1)-str(Ortibal2)" mapped to ICOHP value in eV. @@ -1076,6 +1127,7 @@ def __init__( """ if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") + self._are_coops = are_coops self._are_cobis = are_cobis self._icohplist = {} @@ -1104,13 +1156,16 @@ def __init__( ) def __str__(self) -> str: - lst = [] - for value in self._icohplist.values(): - lst.append(str(value)) - return "\n".join(lst) + return "\n".join([str(value) for value in self._icohplist.values()]) - def get_icohp_by_label(self, label, summed_spin_channels=True, spin=Spin.up, orbitals=None) -> float: - """Get an icohp value for a certain bond as indicated by the label (bond labels starting by "1" as in + def get_icohp_by_label( + self, + label, + summed_spin_channels=True, + spin=Spin.up, + orbitals=None, + ) -> float: + """Get an ICOHP value for a certain bond as indicated by the label (bond labels starting by "1" as in ICOHPLIST/ICOOPLIST). Args: @@ -1135,7 +1190,13 @@ def get_icohp_by_label(self, label, summed_spin_channels=True, spin=Spin.up, orb return icohp_here.icohpvalue_orbital(spin=spin, orbitals=orbitals) - def get_summed_icohp_by_label_list(self, label_list, divisor=1.0, summed_spin_channels=True, spin=Spin.up) -> float: + def get_summed_icohp_by_label_list( + self, + label_list, + divisor=1.0, + summed_spin_channels=True, + spin=Spin.up, + ) -> float: """Get the sum of several ICOHP values that are indicated by a list of labels (labels of the bonds are the same as in ICOHPLIST/ICOOPLIST). @@ -1162,7 +1223,11 @@ def get_summed_icohp_by_label_list(self, label_list, divisor=1.0, summed_spin_ch sum_icohp = sum_icohp + icohp_here.icohpvalue(spin) return sum_icohp / divisor - def get_icohp_dict_by_bondlengths(self, minbondlength=0.0, maxbondlength=8.0): + def get_icohp_dict_by_bondlengths( + self, + minbondlength=0.0, + maxbondlength=8.0, + ): """Get a dict of IcohpValues corresponding to certain bond lengths. Args: @@ -1279,7 +1344,12 @@ def are_cobis(self) -> bool: def get_integrated_cohp_in_energy_range( - cohp, label, orbital=None, energy_range=None, relative_E_Fermi=True, summed_spin_channels=True + cohp, + label, + orbital=None, + energy_range=None, + relative_E_Fermi=True, + summed_spin_channels=True, ): """Integrate CompleteCohp objects which include data on integrated COHPs Args: diff --git a/pymatgen/electronic_structure/core.py b/pymatgen/electronic_structure/core.py index c10ee208137..bd887f64a25 100644 --- a/pymatgen/electronic_structure/core.py +++ b/pymatgen/electronic_structure/core.py @@ -13,6 +13,7 @@ if TYPE_CHECKING: from collections.abc import Sequence + from numpy.typing import NDArray from typing_extensions import Self from pymatgen.core import Lattice @@ -77,7 +78,7 @@ def __str__(self) -> str: return str(self.name) @property - def orbital_type(self): + def orbital_type(self) -> OrbitalType: """OrbitalType of an orbital.""" return OrbitalType[self.name[0]] @@ -127,19 +128,18 @@ class Magmom(MSONable): """ def __init__( - self, moment: float | Sequence[float] | np.ndarray | Magmom, saxis: Sequence[float] = (0, 0, 1) + self, + moment: float | Sequence[float] | NDArray | Magmom, + saxis: Sequence[float] = (0, 0, 1), ) -> None: """ Args: moment: magnetic moment, supplied as float or list/np.ndarray saxis: spin axis, supplied as list/np.ndarray, parameter will be converted to unit vector (default is [0, 0, 1]). - - Returns: - Magmom object """ - # to init from another Magmom instance - if isinstance(moment, Magmom): + # Init from another Magmom instance + if isinstance(moment, type(self)): saxis = moment.saxis # type: ignore[has-type] moment = moment.moment # type: ignore[has-type] @@ -153,6 +153,63 @@ def __init__( self.saxis = saxis / np.linalg.norm(saxis) + def __getitem__(self, key): + return self.moment[key] + + def __iter__(self): + return iter(self.moment) + + def __abs__(self) -> float: + return np.linalg.norm(self.moment) + + def __eq__(self, other: object) -> bool: + """Equal if 'global' magnetic moments are the same, saxis can differ.""" + try: + other_magmom = type(self)(other) + except (TypeError, ValueError): + return NotImplemented + + return np.allclose(self.global_moment, other_magmom.global_moment) + + def __lt__(self, other: Self) -> bool: + return abs(self) < abs(other) + + def __neg__(self) -> Self: + return type(self)(-self.moment, saxis=self.saxis) + + def __hash__(self) -> int: + return hash(tuple(self.moment) + tuple(self.saxis)) + + def __float__(self) -> float: + """Get magnitude of magnetic moment with a sign with respect to + an arbitrary direction. + + Should give unsurprising output if Magmom is treated like a + scalar or if a set of Magmoms describes a collinear structure. + + Implemented this way rather than simpler abs(self) so that + moments will have a consistent sign in case of e.g. + antiferromagnetic collinear structures without additional + user intervention. + + However, should be used with caution for non-collinear + structures and might give nonsensical results except in the case + of only slightly non-collinear structures (e.g. small canting). + + This approach is also used to obtain "diff" VolumetricDensity + in pymatgen.io.vasp.outputs.VolumetricDensity when processing + Chgcars from SOC calculations. + """ + return float(self.get_00t_magmom_with_xyz_saxis()[2]) + + def __str__(self) -> str: + return str(float(self)) + + def __repr__(self) -> str: + if np.allclose(self.saxis, (0, 0, 1)): + return f"Magnetic moment {self.moment}" + return f"Magnetic moment {self.moment} (spin axis = {self.saxis})" + @classmethod def from_global_moment_and_saxis(cls, global_moment, saxis) -> Self: """Convenience method to initialize Magmom from a given global @@ -166,7 +223,7 @@ def from_global_moment_and_saxis(cls, global_moment, saxis) -> Self: global_moment: global magnetic moment saxis: desired saxis """ - magmom = Magmom(global_moment) + magmom = cls(global_moment) return cls(magmom.get_moment(saxis=saxis), saxis=saxis) @classmethod @@ -217,26 +274,26 @@ def get_moment(self, saxis=(0, 0, 1)): Returns: np.ndarray of length 3 """ - # transform back to moment with spin axis [0, 0, 1] + # Transform back to moment with spin axis [0, 0, 1] trafo_mat_inv = self._get_transformation_matrix_inv(self.saxis) moment = np.matmul(self.moment, trafo_mat_inv) - # transform to new saxis + # Transform to new saxis trafo_mat = self._get_transformation_matrix(saxis) moment = np.matmul(moment, trafo_mat) - # round small values to zero + # Round small values to zero moment[np.abs(moment) < 1e-8] = 0 return moment @property - def global_moment(self) -> np.ndarray: + def global_moment(self) -> NDArray: """The magnetic moment defined in an arbitrary global reference frame as an np.array of length 3.""" return self.get_moment() @property - def projection(self): + def projection(self) -> float: """Projects moment along spin quantization axis. Useful for obtaining collinear approximation for slightly non-collinear magmoms. @@ -245,16 +302,16 @@ def projection(self): """ return np.dot(self.moment, self.saxis) - def get_xyz_magmom_with_001_saxis(self): + def get_xyz_magmom_with_001_saxis(self) -> Self: """Get a Magmom in the default setting of saxis = [0, 0, 1] and the magnetic moment rotated as required. Returns: Magmom """ - return Magmom(self.get_moment()) + return type(self)(self.get_moment()) - def get_00t_magmom_with_xyz_saxis(self): + def get_00t_magmom_with_xyz_saxis(self) -> Self: """For internal implementation reasons, in non-collinear calculations VASP prefers the following. MAGMOM = 0 0 total_magnetic_moment @@ -276,7 +333,7 @@ def get_00t_magmom_with_xyz_saxis(self): Returns: Magmom """ - # reference direction gives sign of moment + # Reference direction gives sign of moment # entirely arbitrary, there will always be a pathological case # where a consistent sign is not possible if the magnetic moments # are aligned along the reference direction, but in practice this @@ -288,8 +345,8 @@ def get_00t_magmom_with_xyz_saxis(self): if np.dot(ref_direction, new_saxis) < 0: t = -t new_saxis = -new_saxis - return Magmom([0, 0, t], saxis=new_saxis) - return Magmom(self) + return type(self)([0, 0, t], saxis=new_saxis) + return type(self)(self) @staticmethod def have_consistent_saxis(magmoms) -> bool: @@ -339,14 +396,14 @@ def get_suggested_saxis(magmoms): Returns: np.ndarray of length 3 """ - # heuristic, will pick largest magmom as reference + # Heuristic, will pick largest magmom as reference # useful for creating collinear approximations of # e.g. slightly canted magnetic structures # for fully collinear structures, will return expected # result magmoms = [Magmom(magmom) for magmom in magmoms] - # filter only non-zero magmoms + # Filter only non-zero magmoms magmoms = [magmom for magmom in magmoms if abs(magmom)] magmoms.sort(reverse=True) if len(magmoms) > 0: @@ -367,20 +424,24 @@ def are_collinear(magmoms) -> bool: if not Magmom.have_consistent_saxis(magmoms): magmoms = Magmom.get_consistent_set_and_saxis(magmoms)[0] - # convert to numpy array for convenience + # Convert to numpy array for convenience magmoms = np.array([list(magmom) for magmom in magmoms]) magmoms = magmoms[np.any(magmoms, axis=1)] # remove zero magmoms if len(magmoms) == 0: return True - # use first moment as reference to compare against + # Use first moment as reference to compare against ref_magmom = magmoms[0] - # magnitude of cross products != 0 if non-collinear with reference + # Magnitude of cross products != 0 if non-collinear with reference num_ncl = np.count_nonzero(np.linalg.norm(np.cross(ref_magmom, magmoms), axis=1)) return num_ncl == 0 @classmethod - def from_moment_relative_to_crystal_axes(cls, moment: list[float], lattice: Lattice) -> Self: + def from_moment_relative_to_crystal_axes( + cls, + moment: list[float], + lattice: Lattice, + ) -> Self: """Obtaining a Magmom object from a magnetic moment provided relative to crystal axes. @@ -393,14 +454,14 @@ def from_moment_relative_to_crystal_axes(cls, moment: list[float], lattice: Latt Returns: Magmom """ - # get matrix representing unit lattice vectors + # Get matrix representing unit lattice vectors unit_m = lattice.matrix / np.linalg.norm(lattice.matrix, axis=1)[:, None] moment = np.matmul(list(moment), unit_m) - # round small values to zero + # Round small values to zero moment[np.abs(moment) < 1e-8] = 0 return cls(moment) - def get_moment_relative_to_crystal_axes(self, lattice): + def get_moment_relative_to_crystal_axes(self, lattice: Lattice): """If scalar magmoms, moments will be given arbitrarily along z. Used for writing moments to magCIF file. @@ -410,67 +471,10 @@ def get_moment_relative_to_crystal_axes(self, lattice): Returns: vector as list of floats """ - # get matrix representing unit lattice vectors + # Get matrix representing unit lattice vectors unit_m = lattice.matrix / np.linalg.norm(lattice.matrix, axis=1)[:, None] - # note np.matmul() requires numpy version >= 1.10 + # Note np.matmul() requires numpy version >= 1.10 moment = np.matmul(self.global_moment, np.linalg.inv(unit_m)) - # round small values to zero + # Round small values to zero moment[np.abs(moment) < 1e-8] = 0 return moment - - def __getitem__(self, key): - return self.moment[key] - - def __iter__(self): - return iter(self.moment) - - def __abs__(self): - return np.linalg.norm(self.moment) - - def __eq__(self, other: object) -> bool: - """Equal if 'global' magnetic moments are the same, saxis can differ.""" - try: - other_magmom = Magmom(other) - except (TypeError, ValueError): - return NotImplemented - - return np.allclose(self.global_moment, other_magmom.global_moment) - - def __lt__(self, other): - return abs(self) < abs(other) - - def __neg__(self): - return Magmom(-self.moment, saxis=self.saxis) - - def __hash__(self) -> int: - return hash(tuple(self.moment) + tuple(self.saxis)) - - def __float__(self) -> float: - """Get magnitude of magnetic moment with a sign with respect to - an arbitrary direction. - - Should give unsurprising output if Magmom is treated like a - scalar or if a set of Magmoms describes a collinear structure. - - Implemented this way rather than simpler abs(self) so that - moments will have a consistent sign in case of e.g. - antiferromagnetic collinear structures without additional - user intervention. - - However, should be used with caution for non-collinear - structures and might give nonsensical results except in the case - of only slightly non-collinear structures (e.g. small canting). - - This approach is also used to obtain "diff" VolumetricDensity - in pymatgen.io.vasp.outputs.VolumetricDensity when processing - Chgcars from SOC calculations. - """ - return float(self.get_00t_magmom_with_xyz_saxis()[2]) - - def __str__(self) -> str: - return str(float(self)) - - def __repr__(self) -> str: - if np.allclose(self.saxis, (0, 0, 1)): - return f"Magnetic moment {self.moment}" - return f"Magnetic moment {self.moment} (spin axis = {self.saxis})" From 8a4c4b5fa914b9f40684412885cb898d75352bc8 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Mon, 10 Jun 2024 12:20:47 +0800 Subject: [PATCH 04/39] fix type errors in bandstructure --- .../electronic_structure/bandstructure.py | 19 ++++++++++--------- pymatgen/electronic_structure/plotter.py | 13 +++++++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index ee12fa1148e..602795a866f 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -245,7 +245,8 @@ def get_projection_on_elements(self) -> dict[Spin, NDArray]: if there is no projections in the band structure returns an empty dict """ - result = {} + assert self.structure is not None + result: dict[Spin, NDArray] = {} for spin, val in self.projections.items(): result[spin] = [[defaultdict(float) for _ in range(len(self.kpoints))] for _ in range(self.nb_bands)] for i, j, k in itertools.product( @@ -343,7 +344,7 @@ def get_vbm(self) -> dict[str, Any]: kpoint_vbm = self.kpoints[j] list_ind_kpts = [] - if kpoint_vbm.label is not None: + if kpoint_vbm is not None and kpoint_vbm.label is not None: for idx, kpt in enumerate(self.kpoints): if kpt.label == kpoint_vbm.label: list_ind_kpts.append(idx) @@ -408,7 +409,7 @@ def get_cbm(self) -> dict[str, Any]: kpoint_cbm = self.kpoints[j] list_index_kpoints = [] - if kpoint_cbm.label is not None: + if kpoint_cbm is not None and kpoint_cbm.label is not None: for idx, kpt in enumerate(self.kpoints): if kpt.label == kpoint_cbm.label: list_index_kpoints.append(idx) @@ -562,7 +563,7 @@ def get_kpoint_degeneracy( def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of BandStructure.""" - dct = { + dct: dict[str, Any] = { "@module": type(self).__module__, "@class": type(self).__name__, "lattice_rec": self.lattice_rec.as_dict(), @@ -599,7 +600,7 @@ def as_dict(self) -> dict[str, Any]: mongo_key = f" {c}" if c.startswith("$") else c dct["labels_dict"][mongo_key] = label.as_dict()["fcoords"] dct["projections"] = {} - if len(self.projections) != 0: + if len(self.projections) != 0 and self.structure is not None: dct["structure"] = self.structure.as_dict() dct["projections"] = {str(int(spin)): np.array(v).tolist() for spin, v in self.projections.items()} return dct @@ -907,7 +908,7 @@ class LobsterBandStructureSymmLine(BandStructureSymmLine): def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of BandStructureSymmLine.""" - dct = { + dct: dict[str, Any] = { "@module": type(self).__module__, "@class": type(self).__name__, "lattice_rec": self.lattice_rec.as_dict(), @@ -943,7 +944,7 @@ def as_dict(self) -> dict[str, Any]: for c, label in self.labels_dict.items(): mongo_key = c if not c.startswith("$") else " " + c dct["labels_dict"][mongo_key] = label.as_dict()["fcoords"] - if len(self.projections) != 0: + if len(self.projections) != 0 and self.structure is not None: dct["structure"] = self.structure.as_dict() dct["projections"] = {str(int(spin)): np.array(v).tolist() for spin, v in self.projections.items()} return dct @@ -1033,7 +1034,7 @@ def get_projection_on_elements(self) -> dict[Spin, list]: if there is no projections in the band structure returns an empty dict """ - result = {} + result: dict[Spin, list] = {} for spin, v in self.projections.items(): result[spin] = [[defaultdict(float) for _ in range(len(self.kpoints))] for _ in range(self.nb_bands)] for i, j in itertools.product(range(self.nb_bands), range(len(self.kpoints))): @@ -1045,7 +1046,7 @@ def get_projection_on_elements(self) -> dict[Spin, list]: def get_projections_on_elements_and_orbitals( self, - el_orb_spec: dict[Element, list], + el_orb_spec: dict[Element, list], # type: ignore[override] ) -> dict[Spin, list]: """Return a dictionary of projections on elements and specific orbitals. diff --git a/pymatgen/electronic_structure/plotter.py b/pymatgen/electronic_structure/plotter.py index 29dae3e718d..3df16094164 100644 --- a/pymatgen/electronic_structure/plotter.py +++ b/pymatgen/electronic_structure/plotter.py @@ -51,22 +51,27 @@ class DosPlotter: - """Plot DOSs. The interface is extremely flexible given there are many + """Plot DOS. The interface is extremely flexible given there are many different ways in which people want to view DOS. Typical usage is: - # Initializes plotter with some optional args. Defaults are usually fine + # Initialize plotter with some optional args. Defaults are usually fine plotter = PhononDosPlotter(). # Add DOS with a label plotter.add_dos("Total DOS", dos) - # Alternatively, you can add a dict of DOSes. This is the typical form + # Alternatively, you can add a dict of DOS. This is the typical form # returned by CompletePhononDos.get_element_dos(). plotter.add_dos_dict({"dos1": dos1, "dos2": dos2}) plotter.add_dos_dict(complete_dos.get_spd_dos()) """ - def __init__(self, zero_at_efermi: bool = True, stack: bool = False, sigma: float | None = None) -> None: + def __init__( + self, + zero_at_efermi: bool = True, + stack: bool = False, + sigma: float | None = None, + ) -> None: """ Args: zero_at_efermi (bool): Whether to shift all Dos to have zero energy at the From 51ffc18769801966e375d451e1a3f7f3420a5436 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Tue, 11 Jun 2024 09:55:07 +0800 Subject: [PATCH 05/39] temp save --- .../electronic_structure/bandstructure.py | 2 +- pymatgen/electronic_structure/cohp.py | 29 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index 602795a866f..2b466543270 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -1080,7 +1080,7 @@ def get_projections_on_elements_and_orbitals( @overload -def get_reconstructed_band_structure( +def get_reconstructed_band_structure( # type: ignore[overload-overlap] list_bs: list[BandStructure], efermi: float | None = None, ) -> BandStructure: diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index e9eaa928bf7..e1db10a4f00 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -127,7 +127,7 @@ def get_cohp( self, spin: Spin | Literal[-1, 1, "up", "down"] | None = None, integrated: bool = False, - ) -> dict[Spin, NDArray]: + ) -> dict[Spin, NDArray] | None: """Get the COHP or ICOHP for a particular spin. Args: @@ -155,7 +155,7 @@ def get_cohp( def get_icohp( self, spin: Spin | Literal[-1, 1, "up", "down"] | None = None, - ) -> dict[Spin, NDArray]: + ) -> dict[Spin, NDArray] | None: """Convenient wrapper to get the ICOHP for a particular spin.""" return self.get_cohp(spin=spin, integrated=True) @@ -170,15 +170,15 @@ def get_interpolated_value( energy: Energy to return the COHP value for. integrated: Return COHP (False) or ICOHP (True) """ - inter = {} + inters = {} for spin in self.cohp: if not integrated: - inter[spin] = get_linear_interpolated_value(self.energies, self.cohp[spin], energy) + inters[spin] = get_linear_interpolated_value(self.energies, self.cohp[spin], energy) elif self.icohp is not None: - inter[spin] = get_linear_interpolated_value(self.energies, self.icohp[spin], energy) + inters[spin] = get_linear_interpolated_value(self.energies, self.icohp[spin], energy) else: raise ValueError("ICOHP is empty.") - return inter + return inters def has_antibnd_states_below_efermi( self, @@ -339,7 +339,7 @@ def as_dict(self) -> dict[str, Any]: for bond in self.bonds } if self.orb_res_cohp: - orb_dict = {} + orb_dict: dict[str, Any] = {} for label in self.orb_res_cohp: orb_dict[label] = {} for orbs in self.orb_res_cohp[label]: @@ -368,14 +368,17 @@ def get_cohp_by_label( Returns the COHP object to simplify plotting """ if label.lower() == "average": - divided_cohp = self.cohp - divided_icohp = self.icohp + divided_cohp: dict[Spin, Any] | None = self.cohp + divided_icohp: dict[Spin, Any] | None = self.icohp else: divided_cohp = self.all_cohps[label].get_cohp(spin=None, integrated=False) divided_icohp = self.all_cohps[label].get_icohp(spin=None) + assert divided_cohp is not None + if summed_spin_channels and Spin.down in self.cohp: + assert divided_icohp is not None final_cohp = {} final_icohp = {} final_cohp[Spin.up] = np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0) @@ -704,7 +707,13 @@ def from_dict(cls, dct: dict) -> Self: @classmethod def from_file( - cls, fmt, filename=None, structure_file=None, are_coops=False, are_cobis=False, are_multi_center_cobis=False + cls, + fmt, + filename=None, + structure_file=None, + are_coops=False, + are_cobis=False, + are_multi_center_cobis=False, ) -> Self: """ Create a CompleteCohp object from an output file of a COHP From 0d0615cdaac7fc04ad2d3584df86551581312c1d Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 11:31:00 +0800 Subject: [PATCH 06/39] first run of cohp, mypy errors to fix --- pymatgen/electronic_structure/cohp.py | 462 +++++++++++++------------- pymatgen/io/lobster/outputs.py | 3 +- 2 files changed, 239 insertions(+), 226 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index e1db10a4f00..ab9b3c0fa2c 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -1,6 +1,7 @@ -"""This module defines classes to represent crystal orbital Hamilton -populations (COHP) and integrated COHP (ICOHP), and can also be used -for crystal orbital overlap populations (COOP) or crystal orbital bond indices (COBIs). +"""This module defines classes to represent: + - Crystal orbital Hamilton population (COHP) and integrated COHP (ICOHP). + - Crystal orbital overlap population (COOP). + - Crystal orbital bond index (COBI). If you use this module, please cite: J. George, G. Petretto, A. Naik, M. Esters, A. J. Jackson, R. Nelson, R. Dronskowski, G.-M. Rignanese, G. Hautier, @@ -36,6 +37,8 @@ from numpy.typing import NDArray from typing_extensions import Self + from pymatgen.util.typing import PathLike + __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" __version__ = "0.2" @@ -132,11 +135,11 @@ def get_cohp( Args: spin: Can be Spin object, integer (-1/1) - or str ("up"/"down") - integrated: Return COHP (False) or ICOHP (True) + or str ("up"/"down"). + integrated: Return ICOHP (True) or COHP (False). Returns: - Returns the CHOP or ICOHP for the input spin. If Spin is + The CHOP or ICOHP for the selected spin. If Spin is None and both spins are present, both spins will be returned as a dictionary. """ @@ -164,11 +167,11 @@ def get_interpolated_value( energy: float, integrated: bool = False, ) -> dict[Spin, float]: - """Get the COHP for a particular energy. + """Get the interpolated COHP for a particular energy. Args: energy: Energy to return the COHP value for. - integrated: Return COHP (False) or ICOHP (True) + integrated: Return ICOHP (True) or COHP (False). """ inters = {} for spin in self.cohp: @@ -185,9 +188,11 @@ def has_antibnd_states_below_efermi( spin: Spin | Literal[-1, 1, "up", "down"] | None = None, limit: float = 0.01, ) -> dict[Spin, bool] | None: - """Get dict indicating if there are antibonding states below the Fermi level depending on the spin - spin: Spin - limit: -COHP smaller -limit will be considered. + """Get dict of antibonding states below the Fermi level for the spin. + + Args: + spin: Spin. + limit: only COHP higher than this value will be considered. """ populations = self.cohp n_energies_below_efermi = len([energy for energy in self.energies if energy <= self.efermi]) @@ -211,18 +216,17 @@ def has_antibnd_states_below_efermi( @classmethod def from_dict(cls, dct: dict[str, Any]) -> Self: - """Get a COHP object from a dict representation of the COHP.""" + """Generate Cohp from a dict representation.""" icohp = {Spin(int(key)): np.array(val) for key, val in dct["ICOHP"].items()} if "ICOHP" in dct else None - are_cobis = dct.get("are_cobis", False) - are_multi_center_cobis = dct.get("are_multi_center_cobis", False) + return cls( dct["efermi"], dct["energies"], {Spin(int(key)): np.array(val) for key, val in dct["COHP"].items()}, icohp=icohp, are_coops=dct["are_coops"], - are_cobis=are_cobis, - are_multi_center_cobis=are_multi_center_cobis, + are_cobis=dct.get("are_cobis", False), + are_multi_center_cobis=dct.get("are_multi_center_cobis", False), ) @@ -250,7 +254,7 @@ def __init__( are_coops: bool = False, are_cobis: bool = False, are_multi_center_cobis: bool = False, - orb_res_cohp=None, # TODO: add type + orb_res_cohp: Cohp | None = None, ) -> None: """ Args: @@ -330,6 +334,7 @@ def as_dict(self) -> dict[str, Any]: dct["ICOHP"].update( {label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].icohp.items()}} ) + if False in [bond_dict == {} for bond_dict in self.bonds.values()]: dct["bonds"] = { bond: { @@ -338,6 +343,7 @@ def as_dict(self) -> dict[str, Any]: } for bond in self.bonds } + if self.orb_res_cohp: orb_dict: dict[str, Any] = {} for label in self.orb_res_cohp: @@ -355,14 +361,14 @@ def as_dict(self) -> dict[str, Any]: def get_cohp_by_label( self, - label: str, # TODO: can this be a float/int (according to the following docstring) + label: str | float, # TODO (DanielYang): double-check type of label, perhaps add custom type summed_spin_channels: bool = False, ) -> Cohp: - """Get specific COHP object. + """Get specific Cohp object by label. Args: - label: string (for newer LOBSTER versions: a number) - summed_spin_channels: bool, will sum the spin channels and return the sum in Spin.up if true + label: string (for newer LOBSTER versions: a number) # TODO: check arg label type + summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: Returns the COHP object to simplify plotting @@ -398,21 +404,21 @@ def get_cohp_by_label( def get_summed_cohp_by_label_list( self, - label_list, - divisor=1, - summed_spin_channels=False, - ): + label_list: list[str | float], + divisor: float = 1, + summed_spin_channels: bool = False, + ) -> Cohp: """Get a COHP object that includes a summed COHP divided by divisor. Args: - label_list: list of labels for the COHP that should be included in the summed cohp - divisor: float/int, the summed cohp will be divided by this divisor - summed_spin_channels: bool, will sum the spin channels and return the sum in Spin.up if true + label_list: list of labels for the COHP that should be included in the sum. + divisor (float): The summed COHP will be divided by this divisor. + summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: - Returns a COHP object including a summed COHP + A Cohp object including a summed COHP. """ - # check if cohps are spinpolarized or not + # Check if COHPs are spin polarized first_cohpobject = self.get_cohp_by_label(label_list[0]) summed_cohp = first_cohpobject.cohp.copy() summed_icohp = first_cohpobject.icohp.copy() @@ -456,32 +462,33 @@ def get_summed_cohp_by_label_list( def get_summed_cohp_by_label_and_orbital_list( self, - label_list, - orbital_list, - divisor=1, - summed_spin_channels=False, - ): - """Get a COHP object that includes a summed COHP divided by divisor. + label_list: list[str | float], + orbital_list: list, # TODO (DanielYang): what is the type of orbital? Add custom type for it + divisor: float = 1, + summed_spin_channels: bool = False, + ) -> Cohp: + """Get a Cohp object that includes a summed COHP divided by divisor. Args: - label_list: list of labels for the COHP that should be included in the summed cohp - orbital_list: list of orbitals for the COHPs that should be included in the summed cohp (same order as - label_list) - divisor: float/int, the summed cohp will be divided by this divisor - summed_spin_channels: bool, will sum the spin channels and return the sum in Spin.up if true + label_list: list of labels for the COHP that should be included. + orbital_list: list of orbitals for the COHPs that should be included + (same order as label_list) + divisor (float): the summed COHP will be divided by this divisor. + summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: - Returns a COHP object including a summed COHP + A Cohp object including a summed COHP """ - # check length of label_list and orbital_list: + # Check length of label_list and orbital_list if not len(label_list) == len(orbital_list): raise ValueError("label_list and orbital_list don't have the same length!") - # check if cohps are spinpolarized or not + + # Check if COHPs are spin polarized first_cohpobject = self.get_orbital_resolved_cohp(label_list[0], orbital_list[0]) summed_cohp = first_cohpobject.cohp.copy() summed_icohp = first_cohpobject.icohp.copy() - for ilabel, label in enumerate(label_list[1:], start=1): - cohp_here = self.get_orbital_resolved_cohp(label, orbital_list[ilabel]) + for idx, label in enumerate(label_list[1:], start=1): + cohp_here = self.get_orbital_resolved_cohp(label, orbital_list[idx]) summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp_here.cohp.copy()[Spin.up]], axis=0) if Spin.down in summed_cohp: summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp_here.cohp.copy()[Spin.down]], axis=0) @@ -500,7 +507,6 @@ def get_summed_cohp_by_label_and_orbital_list( if summed_spin_channels and Spin.down in divided_cohp: final_cohp = {} final_icohp = {} - final_cohp[Spin.up] = np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0) final_icohp[Spin.up] = np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0) else: @@ -518,31 +524,30 @@ def get_summed_cohp_by_label_and_orbital_list( def get_orbital_resolved_cohp( self, - label, - orbitals, - summed_spin_channels=False, - ): + label: str | float, + orbitals: str | list | tuple, + summed_spin_channels: bool = False, + ) -> Cohp | None: """Get orbital-resolved COHP. Args: label: bond label (Lobster: labels as in ICOHPLIST/ICOOPLIST.lobster). - - orbitals: The orbitals as a label, or list or tuple of the form - [(n1, orbital1), (n2, orbital2)]. Orbitals can either be str, - int, or Orbital. - - summed_spin_channels: bool, will sum the spin channels and return the sum in Spin.up if true + orbitals: The orbitals as a label, or list/tuple of + [(n1, orbital1), (n2, orbital2), ...]. + Where each orbital can either be str, int, or Orbital. + summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: A Cohp object if CompleteCohp contains orbital-resolved cohp, or None if it doesn't. Note: It currently assumes that orbitals are str if they aren't the - other valid types. This is not ideal, but the easiest way to - avoid unicode issues between python 2 and python 3. + other valid types. This is not ideal, but is the easiest way to + avoid unicode issues between Python 2 and Python 3. """ if self.orb_res_cohp is None: return None + if isinstance(orbitals, (list, tuple)): cohp_orbs = [d["orbitals"] for d in self.orb_res_cohp[label].values()] orbs = [] @@ -561,6 +566,7 @@ def get_orbital_resolved_cohp( orb_label = orbitals else: raise TypeError("Orbitals must be str, list, or tuple.") + try: icohp = self.orb_res_cohp[label][orb_label]["ICOHP"] except KeyError: @@ -589,10 +595,10 @@ def get_orbital_resolved_cohp( ) @classmethod - def from_dict(cls, dct: dict) -> Self: + def from_dict(cls, dct: dict[str, Any]) -> Self: """Get CompleteCohp object from dict representation. - TODO: clean that up? + TODO: clean that up """ cohp_dict = {} efermi = dct["efermi"] @@ -614,6 +620,7 @@ def from_dict(cls, dct: dict) -> Self: } else: bonds = None + for label in dct["COHP"]: cohp = {Spin(int(spin)): np.array(dct["COHP"][label][spin]) for spin in dct["COHP"][label]} try: @@ -692,15 +699,13 @@ def from_dict(cls, dct: dict) -> Self: else: orb_cohp = {} - are_cobis = dct.get("are_cobis", False) - return cls( structure, avg_cohp, cohp_dict, bonds=bonds, are_coops=dct["are_coops"], - are_cobis=are_cobis, + are_cobis=dct.get("are_cobis", False), are_multi_center_cobis=are_multi_center_cobis, orb_res_cohp=orb_cohp, ) @@ -708,12 +713,12 @@ def from_dict(cls, dct: dict) -> Self: @classmethod def from_file( cls, - fmt, - filename=None, - structure_file=None, - are_coops=False, - are_cobis=False, - are_multi_center_cobis=False, + fmt: Literal["LMTO", "LOBSTER"], + filename: PathLike | None = None, + structure_file: PathLike | None = None, + are_coops: bool = False, + are_cobis: bool = False, + are_multi_center_cobis: bool = False, ) -> Self: """ Create a CompleteCohp object from an output file of a COHP @@ -721,9 +726,7 @@ def from_file( LMTO-ASA code) or LOBSTER (for the LOBSTER code). Args: - fmt: A string for the code that was used to calculate - the COHPs so that the output file can be handled - correctly. Can take the values "LMTO" or "LOBSTER". + fmt (Literal["LMTO", "LOBSTER"]): The code used to calculate COHPs. filename: Name of the COHP output file. Defaults to COPL for LMTO and COHPCAR.lobster/COOPCAR.lobster for LOBSTER. structure_file: Name of the file containing the structure. @@ -741,9 +744,10 @@ def from_file( """ if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") + fmt = fmt.upper() if fmt == "LMTO": - # LMTO COOPs and orbital-resolved COHP cannot be handled yet. + # LMTO COOPs and orbital-resolved COHP cannot be handled yet are_coops = False are_cobis = False orb_res_cohp = None @@ -752,6 +756,7 @@ def from_file( if filename is None: filename = "COPL" cohp_file: LMTOCopl | Cohpcar = LMTOCopl(filename=filename, to_eV=True) + elif fmt == "LOBSTER": if ( (are_coops and are_cobis) @@ -775,6 +780,7 @@ def from_file( are_multi_center_cobis=are_multi_center_cobis, ) orb_res_cohp = cohp_file.orb_res_cohp + else: raise ValueError(f"Unknown format {fmt}. Valid formats are LMTO and LOBSTER.") @@ -785,7 +791,6 @@ def from_file( # LOBSTER shifts the energies so that the Fermi energy is at zero. # Shifting should be done by the plotter object though. - spins = [Spin.up, Spin.down] if cohp_file.is_spin_polarized else [Spin.up] if fmt == "LOBSTER": energies += efermi @@ -795,6 +800,7 @@ def from_file( # COHPs from the single-orbital populations. Total COHPs # may not be present when the cohpgenerator keyword is used # in LOBSTER versions 2.2.0 and earlier. + # TODO: Test this more extensively for label in orb_res_cohp: @@ -816,16 +822,16 @@ def from_file( } if fmt == "LMTO": - # Calculate the average COHP for the LMTO file to be - # consistent with LOBSTER output. - avg_data: dict[str, dict] = {"COHP": {}, "ICOHP": {}} - for i in avg_data: + # Calculate the average COHP for the LMTO file to be consistent with LOBSTER + avg_data: dict[Literal["COHP", "ICOHP"], dict] = {"COHP": {}, "ICOHP": {}} + for dtype in avg_data: for spin in spins: - rows = np.array([v[i][spin] for v in cohp_data.values()]) + rows = np.array([v[dtype][spin] for v in cohp_data.values()]) avg = np.mean(rows, axis=0) - # LMTO COHPs have 5 significant figures - avg_data[i].update({spin: np.array([round_to_sigfigs(a, 5) for a in avg], dtype=float)}) + # LMTO COHPs have 5 significant digits + avg_data[dtype] |= {spin: np.array([round_to_sigfigs(a, 5) for a in avg], dtype=float)} avg_cohp = Cohp(efermi, energies, avg_data["COHP"], icohp=avg_data["ICOHP"]) + elif not are_multi_center_cobis: avg_cohp = Cohp( efermi, @@ -837,9 +843,9 @@ def from_file( are_multi_center_cobis=are_multi_center_cobis, ) del cohp_data["average"] + else: - # only include two-center cobis in average - # do this for both spin channels + # Only include two-center COBIs in average for both spin channels cohp = {} cohp[Spin.up] = np.array( [np.array(c["COHP"][Spin.up]) for c in cohp_file.cohp_data.values() if len(c["sites"]) <= 2] @@ -850,6 +856,7 @@ def from_file( ).mean(axis=0) except KeyError: pass + try: icohp = {} icohp[Spin.up] = np.array( @@ -863,6 +870,7 @@ def from_file( pass except KeyError: icohp = None + avg_cohp = Cohp( efermi, energies, @@ -906,7 +914,7 @@ def from_file( class IcohpValue(MSONable): - """Store information on an ICOHP or ICOOP value. + """Information for an ICOHP or ICOOP value. Attributes: energies (ndarray): Energy values for the COHP/ICOHP/COOP/ICOOP. @@ -921,28 +929,28 @@ class IcohpValue(MSONable): def __init__( self, - label, - atom1, - atom2, - length, + label: str | float, + atom1: str, + atom2: str, + length: float, translation, - num, + num: int, icohp, - are_coops=False, - are_cobis=False, - orbitals=None, + are_coops: bool = False, + are_cobis: bool = False, + orbitals: dict | None = None, # TODO: DanielYang: more specific type ) -> None: """ Args: - label: label for the icohp - atom1: str of atom that is contributing to the bond - atom2: str of second atom that is contributing to the bond - length: float of bond lengths - translation: translation list, e.g. [0,0,0] - num: integer describing how often the bond exists + label: label for the icohp. + atom1 (str): The first atom that contributes to the bond. + atom2 (str): The second atom that contributes to the bond. + length (float): Bond length. + translation: translation list, e.g. [0, 0, 0]. # TODO: DanielYang: use tuple3float? + num (int): How often the bond exists. icohp: dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down} - are_coops: if True, this are COOPs - are_cobis: if True, this are COBIs + are_coops (bool): If True, this are COOPs + are_cobis (bool): If True, this are COBIs orbitals: {[str(Orbital1)-str(Orbital2)]: {"icohp":{Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}, "orbitals":[Orbital1, Orbital2]}}. """ @@ -985,7 +993,7 @@ def __str__(self) -> str: f"{self._icohp[Spin.up]} eV (Spin up)" ) - if self._are_cobis and not self._are_coops: + if not self._are_coops and self._are_cobis: if self._is_spin_polarized: return ( f"ICOBI {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " @@ -999,89 +1007,90 @@ def __str__(self) -> str: return "" @property - def num_bonds(self): + def num_bonds(self) -> int: """The number of bonds for which the ICOHP value is an average. Returns: - Int. + int """ return self._num @property def are_coops(self) -> bool: - """Check if ICOOPs or not. + """Whether is ICOOPs. Returns: - Boolean. + bool """ return self._are_coops @property def are_cobis(self) -> bool: - """Check if ICOBIs or not. + """Whether is ICOBIs. Returns: - Boolean. + bool """ return self._are_cobis @property def is_spin_polarized(self) -> bool: - """Check if is spin polarized calculation. + """Whether is spin polarized calculation. Returns: - Boolean. + bool """ return self._is_spin_polarized - def icohpvalue(self, spin=Spin.up): + def icohpvalue(self, spin: Spin = Spin.up) -> float: """ Args: spin: Spin.up or Spin.down. Returns: - float: corresponding to chosen spin. + float: ICOHP value corresponding to chosen spin. """ if not self.is_spin_polarized and spin == Spin.down: raise ValueError("The calculation was not performed with spin polarization") return self._icohp[spin] - def icohpvalue_orbital(self, orbitals, spin=Spin.up) -> float: + def icohpvalue_orbital(self, orbitals: list[Orbital] | str, spin: Spin = Spin.up) -> float: """ Args: orbitals: List of Orbitals or "str(Orbital1)-str(Orbital2)" spin: Spin.up or Spin.down. Returns: - float: corresponding to chosen spin. + float: ICOHP value corresponding to chosen spin. """ if not self.is_spin_polarized and spin == Spin.down: raise ValueError("The calculation was not performed with spin polarization") - if isinstance(orbitals, list): + + if isinstance(orbitals, list): # TODO: DanielYang: use tuple of 2 orbitals = f"{orbitals[0]}-{orbitals[1]}" return self._orbitals[orbitals]["icohp"][spin] @property - def icohp(self): + def icohp(self) -> dict[Spin, IcohpValue]: """Dict with ICOHPs for spin up and spin down. Returns: - dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}. + dict: {Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}. """ return self._icohp @property - def summed_icohp(self): + def summed_icohp(self) -> float: """Sum ICOHPs of both spin channels for spin polarized compounds. Returns: - float: icohp value in eV. + float: ICOHP value in eV. """ return self._icohp[Spin.down] + self._icohp[Spin.up] if self._is_spin_polarized else self._icohp[Spin.up] @property - def summed_orbital_icohp(self): + def summed_orbital_icohp(self) -> dict[str, float]: """Sum orbital-resolved ICOHPs of both spin channels for spin-polarized compounds. Returns: @@ -1096,7 +1105,7 @@ def summed_orbital_icohp(self): class IcohpCollection(MSONable): - """Store IcohpValues. + """Collection of IcohpValues. Attributes: are_coops (bool): Boolean to indicate if these are ICOOPs. @@ -1106,33 +1115,32 @@ class IcohpCollection(MSONable): def __init__( self, - list_labels, - list_atom1, - list_atom2, - list_length, - list_translation, - list_num, - list_icohp, - is_spin_polarized, - list_orb_icohp=None, - are_coops=False, - are_cobis=False, + list_labels: list, + list_atom1: list[str], + list_atom2: list[str], + list_length: list[float], + list_translation: list[list], # TODO: (DanielYang) more specific type + list_num: list, # TODO: more specific type + list_icohp: list[dict[Spin, IcohpValue]], + is_spin_polarized: bool, + list_orb_icohp: list[dict] | None = None, # TODO: more specific type + are_coops: bool = False, + are_cobis: bool = False, ) -> None: """ Args: - list_labels: list of labels for ICOHP/ICOOP values - list_atom1: list of str of atomnames e.g. "O1" - list_atom2: list of str of atomnames e.g. "O1" - list_length: list of lengths of corresponding bonds in Angstrom - list_translation: list of translation list, e.g. [0,0,0] - list_num: list of equivalent bonds, usually 1 starting from Lobster 3.0.0 - list_icohp: list of dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down} - is_spin_polarized: Boolean to indicate if the Lobster calculation was done spin polarized or not Boolean to - indicate if the Lobster calculation was done spin polarized or not + list_labels: list of labels for ICOHP/ICOOP values. + list_atom1 (list[str]): atom names e.g. "O1". + list_atom2 (list[str]): atom names e.g. "O1". + list_length: list of lengths of corresponding bonds in Angstrom. + list_translation: list of translation list, e.g. [0, 0, 0]. + list_num: list of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. + list_icohp: list of dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}. + is_spin_polarized (bool): Whether the LOBSTER calculation was done spin polarized. list_orb_icohp: list of dict={[str(Orbital1)-str(Orbital2)]: {"icohp":{Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}, "orbitals":[Orbital1, Orbital2]}} - are_coops: Boolean to indicate whether ICOOPs are stored - are_cobis: Boolean to indicate whether ICOBIs are stored. + are_coops (bool): Whether ICOOPs are stored + are_cobis (bool): Whether ICOBIs are stored. """ if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") @@ -1169,79 +1177,78 @@ def __str__(self) -> str: def get_icohp_by_label( self, - label, - summed_spin_channels=True, - spin=Spin.up, - orbitals=None, + label: str, + summed_spin_channels: bool = True, + spin: Spin = Spin.up, + orbitals: str | list[Orbital] | None = None, # TODO: change to tuple of 2 ) -> float: - """Get an ICOHP value for a certain bond as indicated by the label (bond labels starting by "1" as in - ICOHPLIST/ICOOPLIST). + """Get an ICOHP value for a certain bond as indicated by the label + (bond labels starting from "1" as in ICOHPLIST/ICOOPLIST). Args: - label: label in str format (usually the bond number in Icohplist.lobster/Icooplist.lobster - summed_spin_channels: Boolean to indicate whether the ICOHPs/ICOOPs of both spin channels should be summed - spin: if summed_spin_channels is equal to False, this spin indicates which spin channel should be returned - orbitals: List of Orbital or "str(Orbital1)-str(Orbital2)" + label (str): usually the bond number in Icohplist.lobster/Icooplist.lobster. + summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. + spin: If summed_spin_channels is False, this spin indicates which spin channel should be returned. + orbitals: List of Orbital or "str(Orbital1)-str(Orbital2)". Returns: float: ICOHP/ICOOP value """ - icohp_here: IcohpValue = self._icohplist[label] + icohp: IcohpValue = self._icohplist[label] + if orbitals is None: - if summed_spin_channels: - return icohp_here.summed_icohp - return icohp_here.icohpvalue(spin) + return icohp.summed_icohp if summed_spin_channels else icohp.icohpvalue(spin) if isinstance(orbitals, list): orbitals = f"{orbitals[0]}-{orbitals[1]}" + if summed_spin_channels: - return icohp_here.summed_orbital_icohp[orbitals] + return icohp.summed_orbital_icohp[orbitals] - return icohp_here.icohpvalue_orbital(spin=spin, orbitals=orbitals) + return icohp.icohpvalue_orbital(spin=spin, orbitals=orbitals) def get_summed_icohp_by_label_list( self, - label_list, - divisor=1.0, - summed_spin_channels=True, - spin=Spin.up, + label_list: list[str | float], + divisor: float = 1.0, + summed_spin_channels: bool = True, + spin: Spin = Spin.up, ) -> float: """Get the sum of several ICOHP values that are indicated by a list of labels (labels of the bonds are the same as in ICOHPLIST/ICOOPLIST). Args: - label_list: list of labels of the ICOHPs/ICOOPs that should be summed - divisor: is used to divide the sum - summed_spin_channels: Boolean to indicate whether the ICOHPs/ICOOPs of both spin channels should be summed - spin: if summed_spin_channels is equal to False, this spin indicates which spin channel should be returned + label_list: list of labels of the ICOHPs/ICOOPs that should be summed. + divisor (float): used to divide the sum. + summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. + spin: if summed_spin_channels is equal to False, this spin indicates which spin channel should be returned. Returns: - float: sum of all ICOHPs/ICOOPs as indicated with label_list + float: sum of all ICOHPs/ICOOPs as indicated with label_list. """ sum_icohp = 0 for label in label_list: icohp_here = self._icohplist[label] if icohp_here.num_bonds != 1: warnings.warn("One of the ICOHP values is an average over bonds. This is currently not considered.") - if icohp_here._is_spin_polarized: - if summed_spin_channels: - sum_icohp = sum_icohp + icohp_here.summed_icohp - else: - sum_icohp = sum_icohp + icohp_here.icohpvalue(spin) + + if icohp_here._is_spin_polarized and summed_spin_channels: + sum_icohp = sum_icohp + icohp_here.summed_icohp else: sum_icohp = sum_icohp + icohp_here.icohpvalue(spin) + return sum_icohp / divisor def get_icohp_dict_by_bondlengths( self, - minbondlength=0.0, - maxbondlength=8.0, - ): + minbondlength: float = 0.0, + maxbondlength: float = 8.0, + ) -> dict[str | float, IcohpValue]: """Get a dict of IcohpValues corresponding to certain bond lengths. Args: - minbondlength: defines the minimum of the bond lengths of the bonds - maxbondlength: defines the maximum of the bond lengths of the bonds. + minbondlength (float): The minimum bond length. + maxbondlength (float): The maximum bond length. Returns: dict of IcohpValues, the keys correspond to the values from the initial list_labels. @@ -1254,34 +1261,34 @@ def get_icohp_dict_by_bondlengths( def get_icohp_dict_of_site( self, - site, - minsummedicohp=None, - maxsummedicohp=None, - minbondlength=0.0, - maxbondlength=8.0, - only_bonds_to=None, - ): + site: int, + minsummedicohp: float | None = None, + maxsummedicohp: float | None = None, + minbondlength: float = 0.0, + maxbondlength: float = 8.0, + only_bonds_to: list[str] | None = None, + ) -> dict[str | float, IcohpValue]: """Get a dict of IcohpValue for a certain site (indicated by integer). Args: - site: integer describing the site of interest, order as in Icohplist.lobster/Icooplist.lobster, starts at 0 - minsummedicohp: float, minimal icohp/icoop of the bonds that are considered. It is the summed ICOHP value + site (int): The site of interest, order as in Icohplist.lobster/Icooplist.lobster, starts at 0. + minsummedicohp (float): Minimal icohp/icoop of the bonds that are considered. It is the summed ICOHP value from both spin channels for spin polarized cases - maxsummedicohp: float, maximal icohp/icoop of the bonds that are considered. It is the summed ICOHP value + maxsummedicohp (float): Maximal icohp/icoop of the bonds that are considered. It is the summed ICOHP value from both spin channels for spin polarized cases - minbondlength: float, defines the minimum of the bond lengths of the bonds - maxbondlength: float, defines the maximum of the bond lengths of the bonds - only_bonds_to: list of strings describing the bonding partners that are allowed, e.g. ['O'] + minbondlength (float): The minimum bond length. + maxbondlength (float): The maximum bond length. + only_bonds_to (list[str]): the bonding partners that are allowed, e.g. ["O"]. Returns: - dict of IcohpValues, the keys correspond to the values from the initial list_labels + Dict of IcohpValues, the keys correspond to the values from the initial list_labels. """ new_icohp_dict = {} for key, value in self._icohplist.items(): atomnumber1 = int(re.split(r"(\d+)", value._atom1)[1]) - 1 atomnumber2 = int(re.split(r"(\d+)", value._atom2)[1]) - 1 if site in (atomnumber1, atomnumber2): - # manipulate order of atoms so that searched one is always atom1 + # Swap order of atoms so that searched one is always atom1 if site == atomnumber2: save = value._atom1 value._atom1 = value._atom2 @@ -1304,16 +1311,20 @@ def get_icohp_dict_of_site( return new_icohp_dict - def extremum_icohpvalue(self, summed_spin_channels=True, spin=Spin.up): - """Get ICOHP/ICOOP of strongest bond. + def extremum_icohpvalue( + self, + summed_spin_channels: bool = True, + spin: Spin = Spin.up, + ) -> float: + """Get ICOHP/ICOOP of the strongest bond. Args: - summed_spin_channels: Boolean to indicate whether the ICOHPs/ICOOPs of both spin channels should be summed. + summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. spin: if summed_spin_channels is equal to False, this spin indicates which spin channel should be returned Returns: - lowest ICOHP/largest ICOOP value (i.e. ICOHP/ICOOP value of strongest bond) + Lowest ICOHP/largest ICOOP value (i.e. ICOHP/ICOOP value of strongest bond). """ extremum = -sys.float_info.max if self._are_coops or self._are_cobis else sys.float_info.max @@ -1338,56 +1349,56 @@ def extremum_icohpvalue(self, summed_spin_channels=True, spin=Spin.up): @property def is_spin_polarized(self) -> bool: - """Whether it is spin polarized.""" + """Whether this is spin polarized.""" return self._is_spin_polarized @property def are_coops(self) -> bool: - """Whether this is a coop.""" + """Whether this is COOP.""" return self._are_coops @property def are_cobis(self) -> bool: - """Whether this a cobi.""" + """Whether this is COBI.""" return self._are_cobis def get_integrated_cohp_in_energy_range( - cohp, - label, - orbital=None, - energy_range=None, - relative_E_Fermi=True, - summed_spin_channels=True, -): - """Integrate CompleteCohp objects which include data on integrated COHPs + cohp: CompleteCohp, + label: str | float, + orbital: Orbital | None = None, + energy_range: float | list[float] | None = None, + relative_E_Fermi: bool = True, + summed_spin_channels: bool = True, +) -> float | dict[Spin, float]: + """Integrate CompleteCohps which include data of integrated COHPs (ICOHPs). + Args: - cohp: CompleteCohp object - label: label of the COHP data - orbital: If not None, a orbital resolved integrated COHP will be returned + cohp: CompleteCohp object. + label: label of the COHP data. + orbital: If not None, a orbital resolved integrated COHP will be returned. energy_range: If None, returns icohp value at Fermi level. If float, integrates from this float up to the Fermi level. - If [float,float], will integrate in between. - relative_E_Fermi: if True, energy scale with E_Fermi at 0 eV is chosen - summed_spin_channels: if True, Spin channels will be summed. + If [float, float], will integrate in between. + relative_E_Fermi (bool): if True, energy scale with E_Fermi at 0 eV is chosen + summed_spin_channels (bool): Whether Spin channels will be summed. Returns: - float indicating the integrated COHP if summed_spin_channels==True, otherwise dict of the following form { - Spin.up:float, Spin.down:float} + If summed_spin_channels: + float: the ICOHP. + else: + dict: {Spin.up: float, Spin.down: float} """ summedicohp = {} if orbital is None: icohps = cohp.all_cohps[label].get_icohp(spin=None) - if summed_spin_channels and Spin.down in icohps: - summedicohp[Spin.up] = icohps[Spin.up] + icohps[Spin.down] - else: - summedicohp = icohps else: icohps = cohp.get_orbital_resolved_cohp(label=label, orbitals=orbital).icohp - if summed_spin_channels and Spin.down in icohps: - summedicohp[Spin.up] = icohps[Spin.up] + icohps[Spin.down] - else: - summedicohp = icohps + + if summed_spin_channels and Spin.down in icohps: + summedicohp[Spin.up] = icohps[Spin.up] + icohps[Spin.down] + else: + summedicohp = icohps if energy_range is None: energies_corrected = cohp.energies - cohp.efermi @@ -1396,12 +1407,13 @@ def get_integrated_cohp_in_energy_range( if not summed_spin_channels and Spin.down in icohps: spl_spindown = InterpolatedUnivariateSpline(energies_corrected, summedicohp[Spin.down], ext=0) return {Spin.up: spl_spinup(0.0), Spin.down: spl_spindown(0.0)} + if summed_spin_channels: return spl_spinup(0.0) return {Spin.up: spl_spinup(0.0)} - # returns icohp value at the Fermi level! + # Return ICOHP value at the Fermi level if isinstance(energy_range, float): if relative_E_Fermi: energies_corrected = cohp.energies - cohp.efermi diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index e30308233db..6b18294f62c 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -35,6 +35,7 @@ from typing import Any from pymatgen.core.structure import IStructure + from pymatgen.util.typing import PathLike __author__ = "Janine George, Marco Esters" __copyright__ = "Copyright 2017, The Materials Project" @@ -80,7 +81,7 @@ def __init__( are_coops: bool = False, are_cobis: bool = False, are_multi_center_cobis: bool = False, - filename: str | None = None, + filename: PathLike | None = None, ) -> None: """ Args: From 319dfa86e715faf4bab44aff996de6b8c72e2868 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 11:50:06 +0800 Subject: [PATCH 07/39] fix collection generation --- pymatgen/electronic_structure/dos.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pymatgen/electronic_structure/dos.py b/pymatgen/electronic_structure/dos.py index fc7378290a2..cdb3074838d 100644 --- a/pymatgen/electronic_structure/dos.py +++ b/pymatgen/electronic_structure/dos.py @@ -1458,8 +1458,24 @@ def _get_orb_type_lobster(orb) -> OrbitalType | None: Returns: OrbitalType """ - orb_labs = ("s", "p_y", "p_z", "p_x", "d_xy", "d_yz", "d_z^2", "d_xz", "d_x^2-y^2") - orb_labs += ("f_y(3x^2-y^2)", "f_xyz", "f_yz^2", "f_z^3", "f_xz^2", "f_z(x^2-y^2)", "f_x(x^2-3y^2)") + orb_labs = ( + "s", + "p_y", + "p_z", + "p_x", + "d_xy", + "d_yz", + "d_z^2", + "d_xz", + "d_x^2-y^2", + "f_y(3x^2-y^2)", + "f_xyz", + "f_yz^2", + "f_z^3", + "f_xz^2", + "f_z(x^2-y^2)", + "f_x(x^2-3y^2)", + ) try: orbital = Orbital(orb_labs.index(orb[1:])) From a29ef989786e2cd811e26fc83b26dc006bdf0957 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 12:03:26 +0800 Subject: [PATCH 08/39] add type `SpinLike` and case tweaks --- pymatgen/electronic_structure/cohp.py | 40 +++++++++++++-------------- pymatgen/util/typing.py | 6 +++- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index ab9b3c0fa2c..a2edc2da928 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -37,7 +37,7 @@ from numpy.typing import NDArray from typing_extensions import Self - from pymatgen.util.typing import PathLike + from pymatgen.util.typing import PathLike, SpinLike __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" @@ -128,14 +128,13 @@ def as_dict(self) -> dict[str, Any]: def get_cohp( self, - spin: Spin | Literal[-1, 1, "up", "down"] | None = None, + spin: SpinLike | None = None, integrated: bool = False, ) -> dict[Spin, NDArray] | None: """Get the COHP or ICOHP for a particular spin. Args: - spin: Can be Spin object, integer (-1/1) - or str ("up"/"down"). + spin: Spin object, integer (-1/1) or str ("up"/"down"). integrated: Return ICOHP (True) or COHP (False). Returns: @@ -157,7 +156,7 @@ def get_cohp( def get_icohp( self, - spin: Spin | Literal[-1, 1, "up", "down"] | None = None, + spin: SpinLike | None = None, ) -> dict[Spin, NDArray] | None: """Convenient wrapper to get the ICOHP for a particular spin.""" return self.get_cohp(spin=spin, integrated=True) @@ -185,7 +184,7 @@ def get_interpolated_value( def has_antibnd_states_below_efermi( self, - spin: Spin | Literal[-1, 1, "up", "down"] | None = None, + spin: SpinLike | None = None, limit: float = 0.01, ) -> dict[Spin, bool] | None: """Get dict of antibonding states below the Fermi level for the spin. @@ -531,14 +530,14 @@ def get_orbital_resolved_cohp( """Get orbital-resolved COHP. Args: - label: bond label (Lobster: labels as in ICOHPLIST/ICOOPLIST.lobster). + label: bond labels as in ICOHPLIST/ICOOPLIST.lobster. orbitals: The orbitals as a label, or list/tuple of [(n1, orbital1), (n2, orbital2), ...]. Where each orbital can either be str, int, or Orbital. summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: - A Cohp object if CompleteCohp contains orbital-resolved cohp, + A Cohp object if CompleteCohp contains orbital-resolved COHP, or None if it doesn't. Note: It currently assumes that orbitals are str if they aren't the @@ -942,17 +941,17 @@ def __init__( ) -> None: """ Args: - label: label for the icohp. + label: label for the ICOHP. atom1 (str): The first atom that contributes to the bond. atom2 (str): The second atom that contributes to the bond. length (float): Bond length. translation: translation list, e.g. [0, 0, 0]. # TODO: DanielYang: use tuple3float? num (int): How often the bond exists. - icohp: dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down} + icohp: dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} are_coops (bool): If True, this are COOPs are_cobis (bool): If True, this are COBIs - orbitals: {[str(Orbital1)-str(Orbital2)]: {"icohp":{Spin.up: icohpvalue for spin.up, Spin.down: - icohpvalue for spin.down}, "orbitals":[Orbital1, Orbital2]}}. + orbitals: {[str(Orbital1)-str(Orbital2)]: {"icohp": {Spin.up: IcohpValue for spin.up, Spin.down: + IcohpValue for spin.down}, "orbitals": [Orbital1, Orbital2]}}. """ if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") @@ -1076,7 +1075,7 @@ def icohp(self) -> dict[Spin, IcohpValue]: """Dict with ICOHPs for spin up and spin down. Returns: - dict: {Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}. + dict: {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. """ return self._icohp @@ -1110,7 +1109,7 @@ class IcohpCollection(MSONable): Attributes: are_coops (bool): Boolean to indicate if these are ICOOPs. are_cobis (bool): Boolean to indicate if these are ICOOPs. - is_spin_polarized (bool): Boolean to indicate if the Lobster calculation was done spin polarized or not. + is_spin_polarized (bool): Boolean to indicate if the LOBSTER calculation was done spin polarized or not. """ def __init__( @@ -1135,10 +1134,10 @@ def __init__( list_length: list of lengths of corresponding bonds in Angstrom. list_translation: list of translation list, e.g. [0, 0, 0]. list_num: list of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. - list_icohp: list of dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}. + list_icohp: list of dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. is_spin_polarized (bool): Whether the LOBSTER calculation was done spin polarized. - list_orb_icohp: list of dict={[str(Orbital1)-str(Orbital2)]: {"icohp":{Spin.up: icohpvalue for spin.up, - Spin.down: icohpvalue for spin.down}, "orbitals":[Orbital1, Orbital2]}} + list_orb_icohp: list of dict={[str(Orbital1)-str(Orbital2)]: {"icohp": {Spin.up: IcohpValue for spin.up, + Spin.down: IcohpValue for spin.down}, "orbitals": [Orbital1, Orbital2]}} are_coops (bool): Whether ICOOPs are stored are_cobis (bool): Whether ICOBIs are stored. """ @@ -1272,9 +1271,9 @@ def get_icohp_dict_of_site( Args: site (int): The site of interest, order as in Icohplist.lobster/Icooplist.lobster, starts at 0. - minsummedicohp (float): Minimal icohp/icoop of the bonds that are considered. It is the summed ICOHP value + minsummedicohp (float): Minimal ICOHP/ICOOP of the bonds that are considered. It is the summed ICOHP value from both spin channels for spin polarized cases - maxsummedicohp (float): Maximal icohp/icoop of the bonds that are considered. It is the summed ICOHP value + maxsummedicohp (float): Maximal ICOHP/ICOOP of the bonds that are considered. It is the summed ICOHP value from both spin channels for spin polarized cases minbondlength (float): The minimum bond length. maxbondlength (float): The maximum bond length. @@ -1296,6 +1295,7 @@ def get_icohp_dict_of_site( second_test = True if only_bonds_to is None else re.split("(\\d+)", value._atom2)[0] in only_bonds_to if value._length >= minbondlength and value._length <= maxbondlength and second_test: + # TODO: DanielYang: merge the following condition blocks if minsummedicohp is not None: if value.summed_icohp >= minsummedicohp: if maxsummedicohp is not None: @@ -1377,7 +1377,7 @@ def get_integrated_cohp_in_energy_range( cohp: CompleteCohp object. label: label of the COHP data. orbital: If not None, a orbital resolved integrated COHP will be returned. - energy_range: If None, returns icohp value at Fermi level. + energy_range: If None, returns ICOHP value at Fermi level. If float, integrates from this float up to the Fermi level. If [float, float], will integrate in between. relative_E_Fermi (bool): if True, energy scale with E_Fermi at 0 eV is chosen diff --git a/pymatgen/util/typing.py b/pymatgen/util/typing.py index 7f498c5e93f..52246c384df 100644 --- a/pymatgen/util/typing.py +++ b/pymatgen/util/typing.py @@ -7,9 +7,10 @@ from collections.abc import Sequence from os import PathLike as OsPathLike -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Any, Literal, Union from pymatgen.core import Composition, DummySpecies, Element, Species +from pymatgen.electronic_structure.core import Spin if TYPE_CHECKING: # needed to avoid circular imports from pymatgen.analysis.cost import CostEntry # type: ignore[attr-defined] @@ -25,6 +26,9 @@ PathLike = Union[str, OsPathLike] PbcLike = tuple[bool, bool, bool] +# Things that can be cast to a Spin +SpinLike = Union[Spin, Literal[-1, 1, "up", "down"]] + # Things that can be cast to a Species-like object using get_el_sp SpeciesLike = Union[str, Element, Species, DummySpecies] From d47008cef318dbad75a9e2287169a5e9caf3ec2c Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 16:46:03 +0800 Subject: [PATCH 09/39] reduce repetition for `__str__` of `IcohpValue` --- pymatgen/electronic_structure/cohp.py | 98 +++++++++++---------------- 1 file changed, 41 insertions(+), 57 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index a2edc2da928..fcae982d172 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -32,13 +32,16 @@ if TYPE_CHECKING: from collections.abc import Sequence - from typing import Any, Literal + from typing import Any, Literal, Union from numpy.typing import NDArray from typing_extensions import Self from pymatgen.util.typing import PathLike, SpinLike + # TODO: double check and clarify this custom type + CohpLabel = Union[str, int] + __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" __version__ = "0.2" @@ -323,16 +326,16 @@ def as_dict(self) -> dict[str, Any]: dct["ICOHP"] = {"average": {str(spin): pops.tolist() for spin, pops in self.icohp.items()}} for label in self.all_cohps: - dct["COHP"].update({label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].cohp.items()}}) + dct["COHP"] |= {label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].cohp.items()}} if self.all_cohps[label].icohp is not None: if "ICOHP" not in dct: dct["ICOHP"] = { label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].icohp.items()} } else: - dct["ICOHP"].update( - {label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].icohp.items()}} - ) + dct["ICOHP"] |= { + label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].icohp.items()} + } if False in [bond_dict == {} for bond_dict in self.bonds.values()]: dct["bonds"] = { @@ -360,13 +363,13 @@ def as_dict(self) -> dict[str, Any]: def get_cohp_by_label( self, - label: str | float, # TODO (DanielYang): double-check type of label, perhaps add custom type + label: CohpLabel, summed_spin_channels: bool = False, ) -> Cohp: """Get specific Cohp object by label. Args: - label: string (for newer LOBSTER versions: a number) # TODO: check arg label type + label: string (for newer LOBSTER versions: int) summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: @@ -523,7 +526,7 @@ def get_summed_cohp_by_label_and_orbital_list( def get_orbital_resolved_cohp( self, - label: str | float, + label: CohpLabel, orbitals: str | list | tuple, summed_spin_channels: bool = False, ) -> Cohp | None: @@ -597,7 +600,7 @@ def get_orbital_resolved_cohp( def from_dict(cls, dct: dict[str, Any]) -> Self: """Get CompleteCohp object from dict representation. - TODO: clean that up + TODO: clean this up """ cohp_dict = {} efermi = dct["efermi"] @@ -719,10 +722,7 @@ def from_file( are_cobis: bool = False, are_multi_center_cobis: bool = False, ) -> Self: - """ - Create a CompleteCohp object from an output file of a COHP - calculation. Valid formats are either LMTO (for the Stuttgart - LMTO-ASA code) or LOBSTER (for the LOBSTER code). + """Create CompleteCohp from an output file of a COHP calculation. Args: fmt (Literal["LMTO", "LOBSTER"]): The code used to calculate COHPs. @@ -916,25 +916,25 @@ class IcohpValue(MSONable): """Information for an ICOHP or ICOOP value. Attributes: - energies (ndarray): Energy values for the COHP/ICOHP/COOP/ICOOP. - densities (ndarray): Density of states values for the COHP/ICOHP/COOP/ICOOP. - energies_are_cartesian (bool): Whether the energies are cartesian or not. - are_coops (bool): Whether the object is a COOP/ICOOP or not. - are_cobis (bool): Whether the object is a COBIS/ICOBIS or not. - icohp (dict): A dictionary of the ICOHP/COHP values. The keys are Spin.up and Spin.down. + energies (NDArray): Energy values for the COHP/ICOHP/COOP/ICOOP. + densities (NDArray): Density of states for the COHP/ICOHP/COOP/ICOOP. + energies_are_cartesian (bool): Whether the energies are cartesian. + are_coops (bool): Whether the object is COOP/ICOOP. + are_cobis (bool): Whether the object is COBIS/ICOBIS. + icohp (dict): The ICOHP/COHP values, whose keys are Spin.up and Spin.down. summed_icohp (float): The summed ICOHP/COHP values. - num_bonds (int): The number of bonds used for the average COHP (relevant for LOBSTER versions <3.0). + num_bonds (int): The number of bonds used for the average COHP (for LOBSTER versions <3.0). """ def __init__( self, - label: str | float, + label: CohpLabel, atom1: str, atom2: str, length: float, - translation, + translation, # TODO: DanielYang: use tuple3float? num: int, - icohp, + icohp: dict, # more specific type are_coops: bool = False, are_cobis: bool = False, orbitals: dict | None = None, # TODO: DanielYang: more specific type @@ -945,8 +945,8 @@ def __init__( atom1 (str): The first atom that contributes to the bond. atom2 (str): The second atom that contributes to the bond. length (float): Bond length. - translation: translation list, e.g. [0, 0, 0]. # TODO: DanielYang: use tuple3float? - num (int): How often the bond exists. + translation: translation list, e.g. [0, 0, 0]. + num (int): How often the bond exists. # TODO: DanielYang: clarify this description icohp: dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} are_coops (bool): If True, this are COOPs are_cobis (bool): If True, this are COBIs @@ -971,40 +971,24 @@ def __init__( def __str__(self) -> str: """String representation of the ICOHP/ICOOP.""" if not self._are_coops and not self._are_cobis: - if self._is_spin_polarized: - return ( - f"ICOHP {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " - f"{self._icohp[Spin.up]} eV (Spin up) and {self._icohp[Spin.down]} eV (Spin down)" - ) - return ( - f"ICOHP {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " - f"{self._icohp[Spin.up]} eV (Spin up)" - ) + header = "ICOHP" + elif self._are_coops and not self._are_cobis: + header = "ICOOP" + else: + header = "ICOBI" - if self._are_coops and not self._are_cobis: - if self._is_spin_polarized: - return ( - f"ICOOP {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " - f"{self._icohp[Spin.up]} eV (Spin up) and {self._icohp[Spin.down]} eV (Spin down)" - ) + if self._is_spin_polarized: return ( - f"ICOOP {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " - f"{self._icohp[Spin.up]} eV (Spin up)" + f"{header} {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " + f"{self._icohp[Spin.up]} eV (Spin up) and {self._icohp[Spin.down]} eV (Spin down)" ) - if not self._are_coops and self._are_cobis: - if self._is_spin_polarized: - return ( - f"ICOBI {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " - f"{self._icohp[Spin.up]} eV (Spin up) and {self._icohp[Spin.down]} eV (Spin down)" - ) + else: return ( - f"ICOBI {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " + f"{header} {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " f"{self._icohp[Spin.up]} eV (Spin up)" ) - return "" - @property def num_bonds(self) -> int: """The number of bonds for which the ICOHP value is an average. @@ -1016,7 +1000,7 @@ def num_bonds(self) -> int: @property def are_coops(self) -> bool: - """Whether is ICOOPs. + """Whether are ICOOPs. Returns: bool @@ -1025,7 +1009,7 @@ def are_coops(self) -> bool: @property def are_cobis(self) -> bool: - """Whether is ICOBIs. + """Whether are ICOBIs. Returns: bool @@ -1081,7 +1065,7 @@ def icohp(self) -> dict[Spin, IcohpValue]: @property def summed_icohp(self) -> float: - """Sum ICOHPs of both spin channels for spin polarized compounds. + """Summed ICOHPs of both spin channels if spin polarized. Returns: float: ICOHP value in eV. @@ -1090,7 +1074,7 @@ def summed_icohp(self) -> float: @property def summed_orbital_icohp(self) -> dict[str, float]: - """Sum orbital-resolved ICOHPs of both spin channels for spin-polarized compounds. + """Summed orbital-resolved ICOHPs of both spin channels if spin-polarized. Returns: dict[str, float]: "str(Orbital1)-str(Ortibal2)" mapped to ICOHP value in eV. @@ -1176,7 +1160,7 @@ def __str__(self) -> str: def get_icohp_by_label( self, - label: str, + label: CohpLabel, summed_spin_channels: bool = True, spin: Spin = Spin.up, orbitals: str | list[Orbital] | None = None, # TODO: change to tuple of 2 @@ -1365,7 +1349,7 @@ def are_cobis(self) -> bool: def get_integrated_cohp_in_energy_range( cohp: CompleteCohp, - label: str | float, + label: CohpLabel, orbital: Orbital | None = None, energy_range: float | list[float] | None = None, relative_E_Fermi: bool = True, From 0407f52a2aaac2da90a4855454a017179d18e33e Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 16:50:10 +0800 Subject: [PATCH 10/39] simplify condition --- pymatgen/electronic_structure/cohp.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index fcae982d172..85310c720f2 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -970,12 +970,13 @@ def __init__( def __str__(self) -> str: """String representation of the ICOHP/ICOOP.""" - if not self._are_coops and not self._are_cobis: - header = "ICOHP" - elif self._are_coops and not self._are_cobis: + # (are_coops and are_cobis) is never True + if self._are_coops: header = "ICOOP" - else: + elif self._are_cobis: header = "ICOBI" + else: + header = "ICOHP" if self._is_spin_polarized: return ( From 3dd5d7c37a55d8ccbb1df3904505cadef92decad Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 16:55:28 +0800 Subject: [PATCH 11/39] reduce indentation level --- pymatgen/electronic_structure/cohp.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 85310c720f2..46df286edb5 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -984,11 +984,10 @@ def __str__(self) -> str: f"{self._icohp[Spin.up]} eV (Spin up) and {self._icohp[Spin.down]} eV (Spin down)" ) - else: - return ( - f"{header} {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " - f"{self._icohp[Spin.up]} eV (Spin up)" - ) + return ( + f"{header} {self._label} between {self._atom1} and {self._atom2} ({self._translation}): " + f"{self._icohp[Spin.up]} eV (Spin up)" + ) @property def num_bonds(self) -> int: From 0e75e0d6529d1c01b1a7eabc13d008a7f95ced21 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 17:13:48 +0800 Subject: [PATCH 12/39] clarify `translation` --- pymatgen/electronic_structure/cohp.py | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 46df286edb5..121ea226481 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -37,7 +37,7 @@ from numpy.typing import NDArray from typing_extensions import Self - from pymatgen.util.typing import PathLike, SpinLike + from pymatgen.util.typing import PathLike, SpinLike, Vector3D # TODO: double check and clarify this custom type CohpLabel = Union[str, int] @@ -932,7 +932,7 @@ def __init__( atom1: str, atom2: str, length: float, - translation, # TODO: DanielYang: use tuple3float? + translation: Vector3D, num: int, icohp: dict, # more specific type are_coops: bool = False, @@ -945,7 +945,7 @@ def __init__( atom1 (str): The first atom that contributes to the bond. atom2 (str): The second atom that contributes to the bond. length (float): Bond length. - translation: translation list, e.g. [0, 0, 0]. + translation: cell translation vector, e.g. (0, 0, 0). num (int): How often the bond exists. # TODO: DanielYang: clarify this description icohp: dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} are_coops (bool): If True, this are COOPs @@ -1102,7 +1102,7 @@ def __init__( list_atom1: list[str], list_atom2: list[str], list_length: list[float], - list_translation: list[list], # TODO: (DanielYang) more specific type + list_translation: list[Vector3D], list_num: list, # TODO: more specific type list_icohp: list[dict[Spin, IcohpValue]], is_spin_polarized: bool, @@ -1116,7 +1116,7 @@ def __init__( list_atom1 (list[str]): atom names e.g. "O1". list_atom2 (list[str]): atom names e.g. "O1". list_length: list of lengths of corresponding bonds in Angstrom. - list_translation: list of translation list, e.g. [0, 0, 0]. + list_translation: list of cell translation vectors. list_num: list of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. list_icohp: list of dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. is_spin_polarized (bool): Whether the LOBSTER calculation was done spin polarized. @@ -1141,18 +1141,18 @@ def __init__( self._list_icohp = list_icohp self._list_orb_icohp = list_orb_icohp - for ilist, listel in enumerate(list_labels): - self._icohplist[listel] = IcohpValue( - label=listel, - atom1=list_atom1[ilist], - atom2=list_atom2[ilist], - length=list_length[ilist], - translation=list_translation[ilist], - num=list_num[ilist], - icohp=list_icohp[ilist], + for idx, label in enumerate(list_labels): + self._icohplist[label] = IcohpValue( + label=label, + atom1=list_atom1[idx], + atom2=list_atom2[idx], + length=list_length[idx], + translation=list_translation[idx], + num=list_num[idx], + icohp=list_icohp[idx], are_coops=are_coops, are_cobis=are_cobis, - orbitals=None if list_orb_icohp is None else list_orb_icohp[ilist], + orbitals=None if list_orb_icohp is None else list_orb_icohp[idx], ) def __str__(self) -> str: From b7c03dac019ba9f895e89966ad3b796ec4303444 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 17:55:56 +0800 Subject: [PATCH 13/39] clarify `list_num` and other docstrings --- pymatgen/electronic_structure/cohp.py | 123 +++++++++++++------------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 121ea226481..2ff424e27b5 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -70,7 +70,7 @@ def __init__( ) -> None: """ Args: - efermi: Fermi energy. + efermi: The Fermi level. energies: A sequence of energies. cohp ({Spin: np.array}): representing the COHP for each spin. are_coops: Indicates whether this object describes COOPs. @@ -137,13 +137,12 @@ def get_cohp( """Get the COHP or ICOHP for a particular spin. Args: - spin: Spin object, integer (-1/1) or str ("up"/"down"). + spin: Spin, integer (-1/1) or str ("up"/"down"). If is None + and both spins are present, both will be returned. integrated: Return ICOHP (True) or COHP (False). Returns: - The CHOP or ICOHP for the selected spin. If Spin is - None and both spins are present, both spins will be returned - as a dictionary. + dict: The COHP or ICOHP for the selected spin. """ populations = self.icohp if integrated else self.cohp @@ -193,8 +192,8 @@ def has_antibnd_states_below_efermi( """Get dict of antibonding states below the Fermi level for the spin. Args: - spin: Spin. - limit: only COHP higher than this value will be considered. + spin (SpinLike): selected spin. + limit (float): Only COHP higher than this value will be considered. """ populations = self.cohp n_energies_below_efermi = len([energy for energy in self.energies if energy <= self.efermi]) @@ -233,17 +232,17 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: class CompleteCohp(Cohp): - """A wrapper class that defines an average COHP, and individual COHPs. + """A wrapper that defines an average COHP, and individual COHPs. Attributes: - are_coops (bool): Indicates whether the object is consisting of COOPs. - are_cobis (bool): Indicates whether the object is consisting of COBIs. - efermi (float): Fermi energy. + are_coops (bool): Whether the object is consisting of COOPs. + are_cobis (bool): Whether the object is consisting of COBIs. + efermi (float): The Fermi level. energies (Sequence[float]): Sequence of energies. - structure (pymatgen.Structure): Structure associated with the COHPs. + structure (Structure): Structure associated with the COHPs. cohp (Sequence[float]): The average COHP. icohp (Sequence[float]): The average ICOHP. - all_cohps (dict[str, Sequence[float]]): A dict of COHPs for individual bonds of the form {label: COHP}. + all_cohps (dict[str, Sequence[float]]): COHPs for individual bonds of the form {label: COHP}. orb_res_cohp (dict[str, Dict[str, Sequence[float]]]): Orbital-resolved COHPs. """ @@ -252,7 +251,7 @@ def __init__( structure: Structure, avg_cohp: Cohp, cohp_dict: dict[str, Cohp], - bonds: dict | None = None, + bonds: dict | None = None, # TODO: DanielYang: more specific type are_coops: bool = False, are_cobis: bool = False, are_multi_center_cobis: bool = False, @@ -262,8 +261,8 @@ def __init__( Args: structure: Structure associated with this COHP. avg_cohp: The average COHP as a Cohp object. - cohp_dict: A dict of COHP objects for individual bonds of the form - {label: COHP} + cohp_dict (dict): COHP objects for individual bonds of the form + {label: COHP}. bonds: A dict containing information on the bonds of the form {label: {key: val}}. The key-val pair can be any information the user wants to put in, but typically contains the sites, @@ -366,14 +365,14 @@ def get_cohp_by_label( label: CohpLabel, summed_spin_channels: bool = False, ) -> Cohp: - """Get specific Cohp object by label. + """Get specific Cohp by label, to simplify plotting. Args: label: string (for newer LOBSTER versions: int) - summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. + summed_spin_channels (bool): Sum the spin channels and return the sum as Spin.up. Returns: - Returns the COHP object to simplify plotting + The Cohp. """ if label.lower() == "average": divided_cohp: dict[Spin, Any] | None = self.cohp @@ -788,7 +787,7 @@ def from_file( cohp_data = cohp_file.cohp_data energies = cohp_file.energies - # LOBSTER shifts the energies so that the Fermi energy is at zero. + # LOBSTER shifts the energies so that the Fermi level is at zero. # Shifting should be done by the plotter object though. spins = [Spin.up, Spin.down] if cohp_file.is_spin_polarized else [Spin.up] if fmt == "LOBSTER": @@ -1000,7 +999,7 @@ def num_bonds(self) -> int: @property def are_coops(self) -> bool: - """Whether are ICOOPs. + """Whether these are ICOOPs. Returns: bool @@ -1009,7 +1008,7 @@ def are_coops(self) -> bool: @property def are_cobis(self) -> bool: - """Whether are ICOBIs. + """Whether these are ICOBIs. Returns: bool @@ -1018,7 +1017,7 @@ def are_cobis(self) -> bool: @property def is_spin_polarized(self) -> bool: - """Whether is spin polarized calculation. + """Whether this is spin polarized calculation. Returns: bool @@ -1041,7 +1040,7 @@ def icohpvalue(self, spin: Spin = Spin.up) -> float: def icohpvalue_orbital(self, orbitals: list[Orbital] | str, spin: Spin = Spin.up) -> float: """ Args: - orbitals: List of Orbitals or "str(Orbital1)-str(Orbital2)" + orbitals (list[Orbitals]): List of Orbitals or "str(Orbital1)-str(Orbital2)". spin: Spin.up or Spin.down. Returns: @@ -1091,9 +1090,9 @@ class IcohpCollection(MSONable): """Collection of IcohpValues. Attributes: - are_coops (bool): Boolean to indicate if these are ICOOPs. - are_cobis (bool): Boolean to indicate if these are ICOOPs. - is_spin_polarized (bool): Boolean to indicate if the LOBSTER calculation was done spin polarized or not. + are_coops (bool): Whether these are ICOOPs. + are_cobis (bool): Whether these are ICOOPs. + is_spin_polarized (bool): Whether the calculation is spin polarized. """ def __init__( @@ -1103,7 +1102,7 @@ def __init__( list_atom2: list[str], list_length: list[float], list_translation: list[Vector3D], - list_num: list, # TODO: more specific type + list_num: list[int], list_icohp: list[dict[Spin, IcohpValue]], is_spin_polarized: bool, list_orb_icohp: list[dict] | None = None, # TODO: more specific type @@ -1112,17 +1111,18 @@ def __init__( ) -> None: """ Args: - list_labels: list of labels for ICOHP/ICOOP values. - list_atom1 (list[str]): atom names e.g. "O1". - list_atom2 (list[str]): atom names e.g. "O1". - list_length: list of lengths of corresponding bonds in Angstrom. - list_translation: list of cell translation vectors. - list_num: list of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. - list_icohp: list of dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. - is_spin_polarized (bool): Whether the LOBSTER calculation was done spin polarized. - list_orb_icohp: list of dict={[str(Orbital1)-str(Orbital2)]: {"icohp": {Spin.up: IcohpValue for spin.up, - Spin.down: IcohpValue for spin.down}, "orbitals": [Orbital1, Orbital2]}} - are_coops (bool): Whether ICOOPs are stored + list_labels (list): Labels for ICOHP/ICOOP values. + list_atom1 (list[str]): Atom names, e.g. "O1". + list_atom2 (list[str]): Atom names, e.g. "O1". + list_length (list[float]): Bond lengths in Angstrom. + list_translation (list[Vector3D]): Cell translation vectors. + list_num (list[int]): Numbers of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. + list_icohp (list[dict]): Dicts as {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. + is_spin_polarized (bool): Whether the calculation is spin polarized. + list_orb_icohp (list[dict]): Dicts as {[str(Orbital1)-str(Orbital2)]: + {"icohp": {Spin.up: IcohpValue for spin.up, + Spin.down: IcohpValue for spin.down}, "orbitals": [Orbital1, Orbital2]}}. + are_coops (bool): Whether ICOOPs are stored. are_cobis (bool): Whether ICOBIs are stored. """ if are_coops and are_cobis: @@ -1201,10 +1201,10 @@ def get_summed_icohp_by_label_list( (labels of the bonds are the same as in ICOHPLIST/ICOOPLIST). Args: - label_list: list of labels of the ICOHPs/ICOOPs that should be summed. - divisor (float): used to divide the sum. + label_list (list): Labels of the ICOHPs/ICOOPs that should be summed. + divisor (float): Divisor used to divide the sum. summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. - spin: if summed_spin_channels is equal to False, this spin indicates which spin channel should be returned. + spin: If not summed_spin_channels, indicate which spin channel should be returned. Returns: float: sum of all ICOHPs/ICOOPs as indicated with label_list. @@ -1251,17 +1251,17 @@ def get_icohp_dict_of_site( maxbondlength: float = 8.0, only_bonds_to: list[str] | None = None, ) -> dict[str | float, IcohpValue]: - """Get a dict of IcohpValue for a certain site (indicated by integer). + """Get a dict of IcohpValue for a certain site. Args: - site (int): The site of interest, order as in Icohplist.lobster/Icooplist.lobster, starts at 0. - minsummedicohp (float): Minimal ICOHP/ICOOP of the bonds that are considered. It is the summed ICOHP value - from both spin channels for spin polarized cases - maxsummedicohp (float): Maximal ICOHP/ICOOP of the bonds that are considered. It is the summed ICOHP value - from both spin channels for spin polarized cases + site (int): The site of interest, ordered as in Icohplist.lobster/Icooplist.lobster, starts from 0. + minsummedicohp (float): Minimal ICOHP/ICOOP of the bonds that are considered. + It is the summed ICOHP value from both spin channels for spin polarized cases + maxsummedicohp (float): Maximal ICOHP/ICOOP of the bonds that are considered. + It is the summed ICOHP value from both spin channels for spin polarized cases minbondlength (float): The minimum bond length. maxbondlength (float): The maximum bond length. - only_bonds_to (list[str]): the bonding partners that are allowed, e.g. ["O"]. + only_bonds_to (list[str]): The bonding partners that are allowed, e.g. ["O"]. Returns: Dict of IcohpValues, the keys correspond to the values from the initial list_labels. @@ -1303,9 +1303,10 @@ def extremum_icohpvalue( """Get ICOHP/ICOOP of the strongest bond. Args: - summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. - - spin: if summed_spin_channels is equal to False, this spin indicates which spin channel should be returned + summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both + spin channels should be summed. + spin: If not summed_spin_channels, this indicates which + spin channel should be returned. Returns: Lowest ICOHP/largest ICOOP value (i.e. ICOHP/ICOOP value of strongest bond). @@ -1324,11 +1325,14 @@ def extremum_icohpvalue( extremum = value.icohpvalue(spin) elif value.icohpvalue(spin) > extremum: extremum = value.icohpvalue(spin) + elif not self._are_coops and not self._are_cobis: if value.summed_icohp < extremum: extremum = value.summed_icohp + elif value.summed_icohp > extremum: extremum = value.summed_icohp + return extremum @property @@ -1351,7 +1355,7 @@ def get_integrated_cohp_in_energy_range( cohp: CompleteCohp, label: CohpLabel, orbital: Orbital | None = None, - energy_range: float | list[float] | None = None, + energy_range: float | tuple[float, float] | None = None, relative_E_Fermi: bool = True, summed_spin_channels: bool = True, ) -> float | dict[Spin, float]: @@ -1361,9 +1365,9 @@ def get_integrated_cohp_in_energy_range( cohp: CompleteCohp object. label: label of the COHP data. orbital: If not None, a orbital resolved integrated COHP will be returned. - energy_range: If None, returns ICOHP value at Fermi level. - If float, integrates from this float up to the Fermi level. - If [float, float], will integrate in between. + energy_range: If None, return the ICOHP value at Fermi level. + If float, integrate from this value up to Fermi level. + If (float, float), integrate in between. relative_E_Fermi (bool): if True, energy scale with E_Fermi at 0 eV is chosen summed_spin_channels (bool): Whether Spin channels will be summed. @@ -1373,12 +1377,12 @@ def get_integrated_cohp_in_energy_range( else: dict: {Spin.up: float, Spin.down: float} """ - summedicohp = {} if orbital is None: icohps = cohp.all_cohps[label].get_icohp(spin=None) else: icohps = cohp.get_orbital_resolved_cohp(label=label, orbitals=orbital).icohp + summedicohp = {} if summed_spin_channels and Spin.down in icohps: summedicohp[Spin.up] = icohps[Spin.up] + icohps[Spin.down] else: @@ -1392,10 +1396,7 @@ def get_integrated_cohp_in_energy_range( spl_spindown = InterpolatedUnivariateSpline(energies_corrected, summedicohp[Spin.down], ext=0) return {Spin.up: spl_spinup(0.0), Spin.down: spl_spindown(0.0)} - if summed_spin_channels: - return spl_spinup(0.0) - - return {Spin.up: spl_spinup(0.0)} + return spl_spinup(0.0) if summed_spin_channels else {Spin.up: spl_spinup(0.0)} # Return ICOHP value at the Fermi level if isinstance(energy_range, float): From 773273684d9ed3c9f821b12629e94d189f8c1e85 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 18:18:58 +0800 Subject: [PATCH 14/39] clarify `label` as str --- pymatgen/electronic_structure/cohp.py | 31 ++++++++++++--------------- pymatgen/electronic_structure/core.py | 1 - 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 2ff424e27b5..34a02b58d15 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -32,16 +32,13 @@ if TYPE_CHECKING: from collections.abc import Sequence - from typing import Any, Literal, Union + from typing import Any, Literal from numpy.typing import NDArray from typing_extensions import Self from pymatgen.util.typing import PathLike, SpinLike, Vector3D - # TODO: double check and clarify this custom type - CohpLabel = Union[str, int] - __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" __version__ = "0.2" @@ -192,7 +189,7 @@ def has_antibnd_states_below_efermi( """Get dict of antibonding states below the Fermi level for the spin. Args: - spin (SpinLike): selected spin. + spin (SpinLike): Selected spin. limit (float): Only COHP higher than this value will be considered. """ populations = self.cohp @@ -362,13 +359,13 @@ def as_dict(self) -> dict[str, Any]: def get_cohp_by_label( self, - label: CohpLabel, + label: str, summed_spin_channels: bool = False, ) -> Cohp: """Get specific Cohp by label, to simplify plotting. Args: - label: string (for newer LOBSTER versions: int) + label (str): Label for the interaction. summed_spin_channels (bool): Sum the spin channels and return the sum as Spin.up. Returns: @@ -525,14 +522,14 @@ def get_summed_cohp_by_label_and_orbital_list( def get_orbital_resolved_cohp( self, - label: CohpLabel, + label: str, orbitals: str | list | tuple, summed_spin_channels: bool = False, ) -> Cohp | None: """Get orbital-resolved COHP. Args: - label: bond labels as in ICOHPLIST/ICOOPLIST.lobster. + label (str): bond labels as in ICOHPLIST/ICOOPLIST.lobster. orbitals: The orbitals as a label, or list/tuple of [(n1, orbital1), (n2, orbital2), ...]. Where each orbital can either be str, int, or Orbital. @@ -927,7 +924,7 @@ class IcohpValue(MSONable): def __init__( self, - label: CohpLabel, + label: str, atom1: str, atom2: str, length: float, @@ -940,7 +937,7 @@ def __init__( ) -> None: """ Args: - label: label for the ICOHP. + label (str): Label for the ICOHP. atom1 (str): The first atom that contributes to the bond. atom2 (str): The second atom that contributes to the bond. length (float): Bond length. @@ -1160,7 +1157,7 @@ def __str__(self) -> str: def get_icohp_by_label( self, - label: CohpLabel, + label: str, summed_spin_channels: bool = True, spin: Spin = Spin.up, orbitals: str | list[Orbital] | None = None, # TODO: change to tuple of 2 @@ -1169,7 +1166,7 @@ def get_icohp_by_label( (bond labels starting from "1" as in ICOHPLIST/ICOOPLIST). Args: - label (str): usually the bond number in Icohplist.lobster/Icooplist.lobster. + label (str): The bond number in Icohplist.lobster/Icooplist.lobster. summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. spin: If summed_spin_channels is False, this spin indicates which spin channel should be returned. orbitals: List of Orbital or "str(Orbital1)-str(Orbital2)". @@ -1353,7 +1350,7 @@ def are_cobis(self) -> bool: def get_integrated_cohp_in_energy_range( cohp: CompleteCohp, - label: CohpLabel, + label: str, orbital: Orbital | None = None, energy_range: float | tuple[float, float] | None = None, relative_E_Fermi: bool = True, @@ -1362,13 +1359,13 @@ def get_integrated_cohp_in_energy_range( """Integrate CompleteCohps which include data of integrated COHPs (ICOHPs). Args: - cohp: CompleteCohp object. - label: label of the COHP data. + cohp (CompleteCohp): CompleteCohp object. + label (str): Label of the COHP data. orbital: If not None, a orbital resolved integrated COHP will be returned. energy_range: If None, return the ICOHP value at Fermi level. If float, integrate from this value up to Fermi level. If (float, float), integrate in between. - relative_E_Fermi (bool): if True, energy scale with E_Fermi at 0 eV is chosen + relative_E_Fermi (bool): If True, energy scale with E_Fermi at 0 eV is chosen. summed_spin_channels (bool): Whether Spin channels will be summed. Returns: diff --git a/pymatgen/electronic_structure/core.py b/pymatgen/electronic_structure/core.py index bd887f64a25..cfac056dd42 100644 --- a/pymatgen/electronic_structure/core.py +++ b/pymatgen/electronic_structure/core.py @@ -473,7 +473,6 @@ def get_moment_relative_to_crystal_axes(self, lattice: Lattice): """ # Get matrix representing unit lattice vectors unit_m = lattice.matrix / np.linalg.norm(lattice.matrix, axis=1)[:, None] - # Note np.matmul() requires numpy version >= 1.10 moment = np.matmul(self.global_moment, np.linalg.inv(unit_m)) # Round small values to zero moment[np.abs(moment) < 1e-8] = 0 From aaf9f298c6c8a9506a6865c4095f692fdcc314e7 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 19:03:56 +0800 Subject: [PATCH 15/39] more type and docstring improvements --- pymatgen/electronic_structure/cohp.py | 250 ++++++++++++++------------ 1 file changed, 131 insertions(+), 119 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 34a02b58d15..0e7b3345a21 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -67,13 +67,13 @@ def __init__( ) -> None: """ Args: - efermi: The Fermi level. - energies: A sequence of energies. - cohp ({Spin: np.array}): representing the COHP for each spin. - are_coops: Indicates whether this object describes COOPs. - are_cobis: Indicates whether this object describes COBIs. - are_multi_center_cobis: Indicates whether this object describes multi-center COBIs - icohp ({Spin: np.array}): representing the ICOHP for each spin. + efermi (float): The Fermi level. + energies (Sequence[float]): Energies. + cohp ({Spin: NDArrary}): The COHP for each spin. + are_coops (bool): Whether this object describes COOPs. + are_cobis (bool): Whether this object describes COBIs. + are_multi_center_cobis (bool): Whether this object describes multi-center COBIs. + icohp ({Spin: NDArrary}): The ICOHP for each spin. """ self.are_coops = are_coops self.are_cobis = are_cobis @@ -84,7 +84,7 @@ def __init__( self.icohp = icohp def __repr__(self) -> str: - """Get a string that can be easily plotted (e.g. using gnuplot).""" + """A string that can be easily plotted (e.g. using gnuplot).""" if self.are_coops: cohp_str = "COOP" elif self.are_cobis or self.are_multi_center_cobis: @@ -134,8 +134,8 @@ def get_cohp( """Get the COHP or ICOHP for a particular spin. Args: - spin: Spin, integer (-1/1) or str ("up"/"down"). If is None - and both spins are present, both will be returned. + spin (SpinLike): Selected spin. If is None and both + spins are present, both will be returned. integrated: Return ICOHP (True) or COHP (False). Returns: @@ -168,8 +168,8 @@ def get_interpolated_value( """Get the interpolated COHP for a particular energy. Args: - energy: Energy to return the COHP value for. - integrated: Return ICOHP (True) or COHP (False). + energy (float): Energy to get the COHP value for. + integrated (bool): Return ICOHP (True) or COHP (False). """ inters = {} for spin in self.cohp: @@ -248,30 +248,30 @@ def __init__( structure: Structure, avg_cohp: Cohp, cohp_dict: dict[str, Cohp], - bonds: dict | None = None, # TODO: DanielYang: more specific type + bonds: dict[str, Any] | None = None, are_coops: bool = False, are_cobis: bool = False, are_multi_center_cobis: bool = False, - orb_res_cohp: Cohp | None = None, + orb_res_cohp: Cohp | None = None, # TODO: DanielYang: double check type ) -> None: """ Args: - structure: Structure associated with this COHP. - avg_cohp: The average COHP as a Cohp object. - cohp_dict (dict): COHP objects for individual bonds of the form + structure (Structure): Structure associated with this COHP. + avg_cohp (Cohp): The average COHP. + cohp_dict (dict[str, Cohp]): COHP for individual bonds of the form {label: COHP}. - bonds: A dict containing information on the bonds of the form - {label: {key: val}}. The key-val pair can be any information - the user wants to put in, but typically contains the sites, - the bond length, and the number of bonds. If nothing is + bonds (dict[str, Any]): Information on the bonds of the form + {label: {key: val}}. The value can be any information, + but typically contains the sites, the bond length, + and the number of bonds. If nothing is supplied, it will default to an empty dict. - are_coops: indicates whether the Cohp objects are COOPs. + are_coops (bool): Whether the Cohp objects are COOPs. Defaults to False for COHPs. - are_cobis: indicates whether the Cohp objects are COBIs. + are_cobis (bool): Whether the Cohp objects are COBIs. Defaults to False for COHPs. - are_multi_center_cobis: indicates whether the Cohp objects are multi-center COBIs. + are_multi_center_cobis (bool): Whether the Cohp objects are multi-center COBIs. Defaults to False for COHPs. - orb_res_cohp: Orbital-resolved COHPs. + orb_res_cohp (Cohp): Orbital-resolved COHPs. """ if ( (are_coops and are_cobis) @@ -299,10 +299,13 @@ def __init__( def __str__(self) -> str: if self.are_coops: - return f"Complete COOPs for {self.structure}" - if self.are_cobis: - return f"Complete COBIs for {self.structure}" - return f"Complete COHPs for {self.structure}" + header = "COOPs" + elif self.are_cobis: + header = "COBIs" + else: + header = "COHPs" + + return f"Complete {header} for {self.structure}" def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of CompleteCohp.""" @@ -362,7 +365,7 @@ def get_cohp_by_label( label: str, summed_spin_channels: bool = False, ) -> Cohp: - """Get specific Cohp by label, to simplify plotting. + """Get specific Cohp by the label, to simplify plotting. Args: label (str): Label for the interaction. @@ -382,9 +385,9 @@ def get_cohp_by_label( assert divided_cohp is not None if summed_spin_channels and Spin.down in self.cohp: - assert divided_icohp is not None final_cohp = {} final_icohp = {} + assert divided_icohp is not None final_cohp[Spin.up] = np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0) final_icohp[Spin.up] = np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0) else: @@ -402,35 +405,35 @@ def get_cohp_by_label( def get_summed_cohp_by_label_list( self, - label_list: list[str | float], + label_list: list[str], divisor: float = 1, summed_spin_channels: bool = False, ) -> Cohp: - """Get a COHP object that includes a summed COHP divided by divisor. + """Get a Cohp object that includes a summed COHP divided by divisor. Args: - label_list: list of labels for the COHP that should be included in the sum. + label_list (list[str]): Labels for the COHP to include. divisor (float): The summed COHP will be divided by this divisor. summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: - A Cohp object including a summed COHP. + A Cohp object for the summed COHP. """ # Check if COHPs are spin polarized first_cohpobject = self.get_cohp_by_label(label_list[0]) summed_cohp = first_cohpobject.cohp.copy() summed_icohp = first_cohpobject.icohp.copy() for label in label_list[1:]: - cohp_here = self.get_cohp_by_label(label) - summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp_here.cohp[Spin.up]], axis=0) + cohp = self.get_cohp_by_label(label) + summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp.cohp[Spin.up]], axis=0) if Spin.down in summed_cohp: - summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp_here.cohp[Spin.down]], axis=0) + summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp.cohp[Spin.down]], axis=0) - summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], cohp_here.icohp[Spin.up]], axis=0) + summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], cohp.icohp[Spin.up]], axis=0) if Spin.down in summed_icohp: - summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], cohp_here.icohp[Spin.down]], axis=0) + summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], cohp.icohp[Spin.down]], axis=0) divided_cohp = {} divided_icohp = {} @@ -460,7 +463,7 @@ def get_summed_cohp_by_label_list( def get_summed_cohp_by_label_and_orbital_list( self, - label_list: list[str | float], + label_list: list[str], orbital_list: list, # TODO (DanielYang): what is the type of orbital? Add custom type for it divisor: float = 1, summed_spin_channels: bool = False, @@ -468,14 +471,14 @@ def get_summed_cohp_by_label_and_orbital_list( """Get a Cohp object that includes a summed COHP divided by divisor. Args: - label_list: list of labels for the COHP that should be included. - orbital_list: list of orbitals for the COHPs that should be included - (same order as label_list) - divisor (float): the summed COHP will be divided by this divisor. + label_list (list[str]): Labels for the COHP that should be included. + orbital_list (list): Orbitals for the COHPs that should be included + (same order as label_list). + divisor (float): The summed COHP will be divided by this divisor. summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. Returns: - A Cohp object including a summed COHP + A Cohp object including the summed COHP. """ # Check length of label_list and orbital_list if not len(label_list) == len(orbital_list): @@ -486,13 +489,13 @@ def get_summed_cohp_by_label_and_orbital_list( summed_cohp = first_cohpobject.cohp.copy() summed_icohp = first_cohpobject.icohp.copy() for idx, label in enumerate(label_list[1:], start=1): - cohp_here = self.get_orbital_resolved_cohp(label, orbital_list[idx]) - summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp_here.cohp.copy()[Spin.up]], axis=0) + cohp = self.get_orbital_resolved_cohp(label, orbital_list[idx]) + summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp.cohp.copy()[Spin.up]], axis=0) if Spin.down in summed_cohp: - summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp_here.cohp.copy()[Spin.down]], axis=0) - summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], cohp_here.icohp.copy()[Spin.up]], axis=0) + summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp.cohp.copy()[Spin.down]], axis=0) + summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], cohp.icohp.copy()[Spin.up]], axis=0) if Spin.down in summed_icohp: - summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], cohp_here.icohp.copy()[Spin.down]], axis=0) + summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], cohp.icohp.copy()[Spin.down]], axis=0) divided_cohp = {} divided_icohp = {} @@ -529,15 +532,16 @@ def get_orbital_resolved_cohp( """Get orbital-resolved COHP. Args: - label (str): bond labels as in ICOHPLIST/ICOOPLIST.lobster. + label (str): Bond labels as in ICOHPLIST/ICOOPLIST.lobster. orbitals: The orbitals as a label, or list/tuple of [(n1, orbital1), (n2, orbital2), ...]. Where each orbital can either be str, int, or Orbital. - summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. + # TODO: DanielYang: clarify type + summed_spin_channels (bool): Sum the spin channels and return the sum as Spin.up. Returns: A Cohp object if CompleteCohp contains orbital-resolved COHP, - or None if it doesn't. + or None if it doesn't. Note: It currently assumes that orbitals are str if they aren't the other valid types. This is not ideal, but is the easiest way to @@ -594,9 +598,9 @@ def get_orbital_resolved_cohp( @classmethod def from_dict(cls, dct: dict[str, Any]) -> Self: - """Get CompleteCohp object from dict representation. + """Get CompleteCohp object from a dict representation. - TODO: clean this up + TODO: Clean this up. """ cohp_dict = {} efermi = dct["efermi"] @@ -661,9 +665,9 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: "orbitals": orbitals, } # If no total COHPs are present, calculate the total - # COHPs from the single-orbital populations. Total COHPs - # may not be present when the COHP generator keyword is used - # in LOBSTER versions 2.2.0 and earlier. + # COHPs from the single-orbital populations. + # Total COHPs may not be present when the COHP generator keyword + # is used in LOBSTER versions 2.2.0 and earlier. if label not in dct["COHP"] or dct["COHP"][label] is None: cohp = { Spin.up: np.sum( @@ -722,17 +726,16 @@ def from_file( Args: fmt (Literal["LMTO", "LOBSTER"]): The code used to calculate COHPs. - filename: Name of the COHP output file. Defaults to COPL - for LMTO and COHPCAR.lobster/COOPCAR.lobster for LOBSTER. - structure_file: Name of the file containing the structure. - If no file name is given, use CTRL for LMTO and POSCAR - for LOBSTER. - are_coops: Indicates whether the populations are COOPs or - COHPs. Defaults to False for COHPs. - are_cobis: Indicates whether the populations are COBIs or - COHPs. Defaults to False for COHPs. - are_multi_center_cobis: Indicates whether this file - includes information on multi-center COBIs + filename (PathLike): The COHP output file. Defaults to "COPL" + for LMTO and "COHPCAR.lobster/COOPCAR.lobster" for LOBSTER. + structure_file (PathLike): The file containing the structure. + If None, use "CTRL" for LMTO and "POSCAR" for LOBSTER. + are_coops (bool): Whether the populations are COOPs or COHPs. + Defaults to False for COHPs. + are_cobis (bool): Whether the populations are COBIs or COHPs. + Defaults to False for COHPs. + are_multi_center_cobis (bool): Whether this file + includes information on multi-center COBIs. Returns: A CompleteCohp object. @@ -740,9 +743,8 @@ def from_file( if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") - fmt = fmt.upper() - if fmt == "LMTO": - # LMTO COOPs and orbital-resolved COHP cannot be handled yet + if fmt.upper() == "LMTO": + # TODO: LMTO COOPs and orbital-resolved COHP cannot be handled yet are_coops = False are_cobis = False orb_res_cohp = None @@ -750,9 +752,10 @@ def from_file( structure_file = "CTRL" if filename is None: filename = "COPL" + cohp_file: LMTOCopl | Cohpcar = LMTOCopl(filename=filename, to_eV=True) - elif fmt == "LOBSTER": + elif fmt.upper() == "LOBSTER": if ( (are_coops and are_cobis) or (are_coops and are_multi_center_cobis) @@ -930,10 +933,10 @@ def __init__( length: float, translation: Vector3D, num: int, - icohp: dict, # more specific type + icohp: dict[Spin, IcohpValue], are_coops: bool = False, are_cobis: bool = False, - orbitals: dict | None = None, # TODO: DanielYang: more specific type + orbitals: dict[str, dict[Literal["icohp", "orbitals"], Any]] | None = None, ) -> None: """ Args: @@ -941,13 +944,17 @@ def __init__( atom1 (str): The first atom that contributes to the bond. atom2 (str): The second atom that contributes to the bond. length (float): Bond length. - translation: cell translation vector, e.g. (0, 0, 0). + translation (Vector3D): cell translation vector, e.g. (0, 0, 0). num (int): How often the bond exists. # TODO: DanielYang: clarify this description - icohp: dict={Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} - are_coops (bool): If True, this are COOPs - are_cobis (bool): If True, this are COBIs - orbitals: {[str(Orbital1)-str(Orbital2)]: {"icohp": {Spin.up: IcohpValue for spin.up, Spin.down: - IcohpValue for spin.down}, "orbitals": [Orbital1, Orbital2]}}. + icohp (dict): {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} + are_coops (bool): Whether these are COOPs. + are_cobis (bool): Whether these are COBIs. + orbitals (dict): {[str(Orbital1)-str(Orbital2)]: { + "icohp": { + Spin.up: IcohpValue for spin.up, + Spin.down: IcohpValue for spin.down + }, + "orbitals": [Orbital1, Orbital2, ...]}. """ if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") @@ -1014,7 +1021,7 @@ def are_cobis(self) -> bool: @property def is_spin_polarized(self) -> bool: - """Whether this is spin polarized calculation. + """Whether this is a spin polarized calculation. Returns: bool @@ -1038,7 +1045,7 @@ def icohpvalue_orbital(self, orbitals: list[Orbital] | str, spin: Spin = Spin.up """ Args: orbitals (list[Orbitals]): List of Orbitals or "str(Orbital1)-str(Orbital2)". - spin: Spin.up or Spin.down. + spin (Spin): Spin.up or Spin.down. Returns: float: ICOHP value corresponding to chosen spin. @@ -1073,7 +1080,7 @@ def summed_orbital_icohp(self) -> dict[str, float]: """Summed orbital-resolved ICOHPs of both spin channels if spin-polarized. Returns: - dict[str, float]: "str(Orbital1)-str(Ortibal2)" mapped to ICOHP value in eV. + dict[str, float]: "str(Orbital1)-str(Ortibal2)": ICOHP value in eV. """ orbital_icohp = {} for orb, item in self._orbitals.items(): @@ -1094,7 +1101,7 @@ class IcohpCollection(MSONable): def __init__( self, - list_labels: list, + list_labels: list[str], list_atom1: list[str], list_atom2: list[str], list_length: list[float], @@ -1102,13 +1109,13 @@ def __init__( list_num: list[int], list_icohp: list[dict[Spin, IcohpValue]], is_spin_polarized: bool, - list_orb_icohp: list[dict] | None = None, # TODO: more specific type + list_orb_icohp: list[dict[str, dict[Literal["icohp", "orbitals"], Any]]] | None = None, are_coops: bool = False, are_cobis: bool = False, ) -> None: """ Args: - list_labels (list): Labels for ICOHP/ICOOP values. + list_labels (list[str]): Labels for ICOHP/ICOOP values. list_atom1 (list[str]): Atom names, e.g. "O1". list_atom2 (list[str]): Atom names, e.g. "O1". list_length (list[float]): Bond lengths in Angstrom. @@ -1116,9 +1123,9 @@ def __init__( list_num (list[int]): Numbers of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. list_icohp (list[dict]): Dicts as {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. is_spin_polarized (bool): Whether the calculation is spin polarized. - list_orb_icohp (list[dict]): Dicts as {[str(Orbital1)-str(Orbital2)]: - {"icohp": {Spin.up: IcohpValue for spin.up, - Spin.down: IcohpValue for spin.down}, "orbitals": [Orbital1, Orbital2]}}. + list_orb_icohp (list[dict]): Dicts as {[str(Orbital1)-str(Orbital2)]: { + "icohp": {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}, + "orbitals": [Orbital1, Orbital2]}. are_coops (bool): Whether ICOOPs are stored. are_cobis (bool): Whether ICOBIs are stored. """ @@ -1160,19 +1167,21 @@ def get_icohp_by_label( label: str, summed_spin_channels: bool = True, spin: Spin = Spin.up, - orbitals: str | list[Orbital] | None = None, # TODO: change to tuple of 2 + orbitals: str | list[Orbital] | None = None, # TODO: DanielYang: use tuple of 2 ) -> float: - """Get an ICOHP value for a certain bond as indicated by the label - (bond labels starting from "1" as in ICOHPLIST/ICOOPLIST). + """Get an ICOHP value for a certain bond indicated by the label. Args: - label (str): The bond number in Icohplist.lobster/Icooplist.lobster. - summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. - spin: If summed_spin_channels is False, this spin indicates which spin channel should be returned. + label (str): The bond number in Icohplist.lobster/Icooplist.lobster, + starting from "1". + summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both + spin channels should be summed. + spin (Spin): If not summed_spin_channels, indicate + which spin channel should be returned. orbitals: List of Orbital or "str(Orbital1)-str(Orbital2)". Returns: - float: ICOHP/ICOOP value + float: ICOHP/ICOOP value. """ icohp: IcohpValue = self._icohplist[label] @@ -1189,33 +1198,35 @@ def get_icohp_by_label( def get_summed_icohp_by_label_list( self, - label_list: list[str | float], + label_list: list[str], divisor: float = 1.0, summed_spin_channels: bool = True, spin: Spin = Spin.up, ) -> float: - """Get the sum of several ICOHP values that are indicated by a list of labels - (labels of the bonds are the same as in ICOHPLIST/ICOOPLIST). + """Get the sum of ICOHP values. Args: - label_list (list): Labels of the ICOHPs/ICOOPs that should be summed. + label_list (list[str]): Labels of the ICOHPs/ICOOPs that should be summed, + the same as in ICOHPLIST/ICOOPLIST. divisor (float): Divisor used to divide the sum. - summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. - spin: If not summed_spin_channels, indicate which spin channel should be returned. + summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both + spin channels should be summed. + spin (Spin): If not summed_spin_channels, indicate + which spin channel should be returned. Returns: - float: sum of all ICOHPs/ICOOPs as indicated with label_list. + float: Sum of ICOHPs selected with label_list. """ sum_icohp = 0 for label in label_list: - icohp_here = self._icohplist[label] - if icohp_here.num_bonds != 1: + icohp = self._icohplist[label] + if icohp.num_bonds != 1: warnings.warn("One of the ICOHP values is an average over bonds. This is currently not considered.") - if icohp_here._is_spin_polarized and summed_spin_channels: - sum_icohp = sum_icohp + icohp_here.summed_icohp + if icohp._is_spin_polarized and summed_spin_channels: + sum_icohp = sum_icohp + icohp.summed_icohp else: - sum_icohp = sum_icohp + icohp_here.icohpvalue(spin) + sum_icohp = sum_icohp + icohp.icohpvalue(spin) return sum_icohp / divisor @@ -1223,19 +1234,19 @@ def get_icohp_dict_by_bondlengths( self, minbondlength: float = 0.0, maxbondlength: float = 8.0, - ) -> dict[str | float, IcohpValue]: - """Get a dict of IcohpValues corresponding to certain bond lengths. + ) -> dict[str, IcohpValue]: + """Get IcohpValues within certain bond length range. Args: minbondlength (float): The minimum bond length. maxbondlength (float): The maximum bond length. Returns: - dict of IcohpValues, the keys correspond to the values from the initial list_labels. + dict[str, IcohpValue]: Keys are the labels from the initial list_labels. """ new_icohp_dict = {} for value in self._icohplist.values(): - if value._length >= minbondlength and value._length <= maxbondlength: + if minbondlength <= value._length <= maxbondlength: new_icohp_dict[value._label] = value return new_icohp_dict @@ -1248,10 +1259,11 @@ def get_icohp_dict_of_site( maxbondlength: float = 8.0, only_bonds_to: list[str] | None = None, ) -> dict[str | float, IcohpValue]: - """Get a dict of IcohpValue for a certain site. + """Get IcohpValues for a certain site. Args: - site (int): The site of interest, ordered as in Icohplist.lobster/Icooplist.lobster, starts from 0. + site (int): The site of interest, ordered as in Icohplist.lobster/Icooplist.lobster, + starts from 0. minsummedicohp (float): Minimal ICOHP/ICOOP of the bonds that are considered. It is the summed ICOHP value from both spin channels for spin polarized cases maxsummedicohp (float): Maximal ICOHP/ICOOP of the bonds that are considered. @@ -1275,7 +1287,7 @@ def get_icohp_dict_of_site( value._atom2 = save second_test = True if only_bonds_to is None else re.split("(\\d+)", value._atom2)[0] in only_bonds_to - if value._length >= minbondlength and value._length <= maxbondlength and second_test: + if minbondlength <= value._length <= maxbondlength and second_test: # TODO: DanielYang: merge the following condition blocks if minsummedicohp is not None: if value.summed_icohp >= minsummedicohp: @@ -1302,7 +1314,7 @@ def extremum_icohpvalue( Args: summed_spin_channels (bool): Whether the ICOHPs/ICOOPs of both spin channels should be summed. - spin: If not summed_spin_channels, this indicates which + spin (Spin): If not summed_spin_channels, this indicates which spin channel should be returned. Returns: @@ -1365,7 +1377,7 @@ def get_integrated_cohp_in_energy_range( energy_range: If None, return the ICOHP value at Fermi level. If float, integrate from this value up to Fermi level. If (float, float), integrate in between. - relative_E_Fermi (bool): If True, energy scale with E_Fermi at 0 eV is chosen. + relative_E_Fermi (bool): Whether energy scale with Fermi level at 0 eV is chosen. summed_spin_channels (bool): Whether Spin channels will be summed. Returns: From 950eb1066f2fcd136915f7560a34ec091b57e6ae Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 19:18:24 +0800 Subject: [PATCH 16/39] fix unit test --- pymatgen/electronic_structure/cohp.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 0e7b3345a21..65b49a164ed 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -743,7 +743,8 @@ def from_file( if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") - if fmt.upper() == "LMTO": + fmt = fmt.upper() + if fmt == "LMTO": # TODO: LMTO COOPs and orbital-resolved COHP cannot be handled yet are_coops = False are_cobis = False @@ -755,7 +756,7 @@ def from_file( cohp_file: LMTOCopl | Cohpcar = LMTOCopl(filename=filename, to_eV=True) - elif fmt.upper() == "LOBSTER": + elif fmt == "LOBSTER": if ( (are_coops and are_cobis) or (are_coops and are_multi_center_cobis) From 5a2ec9f83f70e9fd8537508ba3fd46fb4abb6847 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 21:05:30 +0800 Subject: [PATCH 17/39] fix most mypy errors --- pymatgen/electronic_structure/cohp.py | 109 ++++++++++++++------------ 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 65b49a164ed..7b977becf96 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -39,6 +39,9 @@ from pymatgen.util.typing import PathLike, SpinLike, Vector3D + # TODO: use more specific type + CohpOrbital = str | int | Orbital + __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" __version__ = "0.2" @@ -252,7 +255,7 @@ def __init__( are_coops: bool = False, are_cobis: bool = False, are_multi_center_cobis: bool = False, - orb_res_cohp: Cohp | None = None, # TODO: DanielYang: double check type + orb_res_cohp: dict[str, dict] | None = None, ) -> None: """ Args: @@ -271,7 +274,7 @@ def __init__( Defaults to False for COHPs. are_multi_center_cobis (bool): Whether the Cohp objects are multi-center COBIs. Defaults to False for COHPs. - orb_res_cohp (Cohp): Orbital-resolved COHPs. + orb_res_cohp (dict): Orbital-resolved COHPs. """ if ( (are_coops and are_cobis) @@ -326,15 +329,13 @@ def as_dict(self) -> dict[str, Any]: for label in self.all_cohps: dct["COHP"] |= {label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].cohp.items()}} - if self.all_cohps[label].icohp is not None: + icohp = self.all_cohps[label].icohp + if icohp is not None: + # TODO: DanielYang59: merge two condition branches with "|=" operator? if "ICOHP" not in dct: - dct["ICOHP"] = { - label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].icohp.items()} - } + dct["ICOHP"] = {label: {str(spin): pops.tolist() for spin, pops in icohp.items()}} else: - dct["ICOHP"] |= { - label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].icohp.items()} - } + dct["ICOHP"] |= {label: {str(spin): pops.tolist() for spin, pops in icohp.items()}} if False in [bond_dict == {} for bond_dict in self.bonds.values()]: dct["bonds"] = { @@ -350,12 +351,16 @@ def as_dict(self) -> dict[str, Any]: for label in self.orb_res_cohp: orb_dict[label] = {} for orbs in self.orb_res_cohp[label]: - cohp = {str(spin): pops.tolist() for spin, pops in self.orb_res_cohp[label][orbs]["COHP"].items()} - orb_dict[label][orbs] = {"COHP": cohp} - icohp = {str(spin): pops.tolist() for spin, pops in self.orb_res_cohp[label][orbs]["ICOHP"].items()} - orb_dict[label][orbs]["ICOHP"] = icohp - orbitals = [[orb[0], orb[1].name] for orb in self.orb_res_cohp[label][orbs]["orbitals"]] - orb_dict[label][orbs]["orbitals"] = orbitals + orb_dict[label][orbs] = { + "COHP": { + str(spin): pops.tolist() for spin, pops in self.orb_res_cohp[label][orbs]["COHP"].items() + }, + "ICOHP": { + str(spin): pops.tolist() for spin, pops in self.orb_res_cohp[label][orbs]["ICOHP"].items() + }, + "orbitals": [[orb[0], orb[1].name] for orb in self.orb_res_cohp[label][orbs]["orbitals"]], + } + dct["orb_res_cohp"] = orb_dict return dct @@ -377,7 +382,6 @@ def get_cohp_by_label( if label.lower() == "average": divided_cohp: dict[Spin, Any] | None = self.cohp divided_icohp: dict[Spin, Any] | None = self.icohp - else: divided_cohp = self.all_cohps[label].get_cohp(spin=None, integrated=False) divided_icohp = self.all_cohps[label].get_icohp(spin=None) @@ -385,11 +389,11 @@ def get_cohp_by_label( assert divided_cohp is not None if summed_spin_channels and Spin.down in self.cohp: - final_cohp = {} - final_icohp = {} assert divided_icohp is not None - final_cohp[Spin.up] = np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0) - final_icohp[Spin.up] = np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0) + final_cohp: dict[Spin, Any] = {Spin.up: np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0)} + final_icohp: dict[Spin, Any] | None = { + Spin.up: np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0) + } else: final_cohp = divided_cohp final_icohp = divided_icohp @@ -422,32 +426,31 @@ def get_summed_cohp_by_label_list( # Check if COHPs are spin polarized first_cohpobject = self.get_cohp_by_label(label_list[0]) summed_cohp = first_cohpobject.cohp.copy() + assert first_cohpobject.icohp is not None summed_icohp = first_cohpobject.icohp.copy() for label in label_list[1:]: cohp = self.get_cohp_by_label(label) + icohp = cohp.icohp + assert icohp is not None summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp.cohp[Spin.up]], axis=0) if Spin.down in summed_cohp: summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp.cohp[Spin.down]], axis=0) - summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], cohp.icohp[Spin.up]], axis=0) + summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], icohp[Spin.up]], axis=0) if Spin.down in summed_icohp: - summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], cohp.icohp[Spin.down]], axis=0) + summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], icohp[Spin.down]], axis=0) - divided_cohp = {} - divided_icohp = {} - divided_cohp[Spin.up] = np.divide(summed_cohp[Spin.up], divisor) - divided_icohp[Spin.up] = np.divide(summed_icohp[Spin.up], divisor) + divided_cohp = {Spin.up: np.divide(summed_cohp[Spin.up], divisor)} + divided_icohp = {Spin.up: np.divide(summed_icohp[Spin.up], divisor)} if Spin.down in summed_cohp: divided_cohp[Spin.down] = np.divide(summed_cohp[Spin.down], divisor) divided_icohp[Spin.down] = np.divide(summed_icohp[Spin.down], divisor) if summed_spin_channels and Spin.down in summed_cohp: - final_cohp = {} - final_icohp = {} - final_cohp[Spin.up] = np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0) - final_icohp[Spin.up] = np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0) + final_cohp = {Spin.up: np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0)} + final_icohp = {Spin.up: np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0)} else: final_cohp = divided_cohp final_icohp = divided_icohp @@ -464,7 +467,7 @@ def get_summed_cohp_by_label_list( def get_summed_cohp_by_label_and_orbital_list( self, label_list: list[str], - orbital_list: list, # TODO (DanielYang): what is the type of orbital? Add custom type for it + orbital_list: list, # TODO (DanielYang): what is its type? Add custom type for it? divisor: float = 1, summed_spin_channels: bool = False, ) -> Cohp: @@ -486,30 +489,32 @@ def get_summed_cohp_by_label_and_orbital_list( # Check if COHPs are spin polarized first_cohpobject = self.get_orbital_resolved_cohp(label_list[0], orbital_list[0]) + assert first_cohpobject is not None + assert first_cohpobject.icohp is not None summed_cohp = first_cohpobject.cohp.copy() summed_icohp = first_cohpobject.icohp.copy() + for idx, label in enumerate(label_list[1:], start=1): cohp = self.get_orbital_resolved_cohp(label, orbital_list[idx]) + assert cohp is not None + assert cohp.icohp is not None summed_cohp[Spin.up] = np.sum([summed_cohp[Spin.up], cohp.cohp.copy()[Spin.up]], axis=0) if Spin.down in summed_cohp: summed_cohp[Spin.down] = np.sum([summed_cohp[Spin.down], cohp.cohp.copy()[Spin.down]], axis=0) + summed_icohp[Spin.up] = np.sum([summed_icohp[Spin.up], cohp.icohp.copy()[Spin.up]], axis=0) if Spin.down in summed_icohp: summed_icohp[Spin.down] = np.sum([summed_icohp[Spin.down], cohp.icohp.copy()[Spin.down]], axis=0) - divided_cohp = {} - divided_icohp = {} - divided_cohp[Spin.up] = np.divide(summed_cohp[Spin.up], divisor) - divided_icohp[Spin.up] = np.divide(summed_icohp[Spin.up], divisor) + divided_cohp = {Spin.up: np.divide(summed_cohp[Spin.up], divisor)} + divided_icohp = {Spin.up: np.divide(summed_icohp[Spin.up], divisor)} if Spin.down in summed_cohp: divided_cohp[Spin.down] = np.divide(summed_cohp[Spin.down], divisor) divided_icohp[Spin.down] = np.divide(summed_icohp[Spin.down], divisor) if summed_spin_channels and Spin.down in divided_cohp: - final_cohp = {} - final_icohp = {} - final_cohp[Spin.up] = np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0) - final_icohp[Spin.up] = np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0) + final_cohp = {Spin.up: np.sum([divided_cohp[Spin.up], divided_cohp[Spin.down]], axis=0)} + final_icohp = {Spin.up: np.sum([divided_icohp[Spin.up], divided_icohp[Spin.down]], axis=0)} else: final_cohp = divided_cohp final_icohp = divided_icohp @@ -526,7 +531,7 @@ def get_summed_cohp_by_label_and_orbital_list( def get_orbital_resolved_cohp( self, label: str, - orbitals: str | list | tuple, + orbitals: str | list[Orbital] | tuple[Orbital, ...], summed_spin_channels: bool = False, ) -> Cohp | None: """Get orbital-resolved COHP. @@ -551,7 +556,7 @@ def get_orbital_resolved_cohp( return None if isinstance(orbitals, (list, tuple)): - cohp_orbs = [d["orbitals"] for d in self.orb_res_cohp[label].values()] + cohp_orbs = [val["orbitals"] for val in self.orb_res_cohp[label].values()] orbs = [] for orbital in orbitals: if isinstance(orbital[1], int): @@ -564,6 +569,7 @@ def get_orbital_resolved_cohp( raise TypeError("Orbital must be str, int, or Orbital.") orb_index = cohp_orbs.index(orbs) orb_label = list(self.orb_res_cohp[label])[orb_index] + elif isinstance(orbitals, str): orb_label = orbitals else: @@ -643,7 +649,7 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: cohp_dict[label] = Cohp(efermi, energies, cohp, icohp=icohp) if "orb_res_cohp" in dct: - orb_cohp: dict[str, dict] = {} + orb_cohp: dict[str, dict[Orbital, dict[str, Any]]] = {} for label in dct["orb_res_cohp"]: orb_cohp[label] = {} for orb in dct["orb_res_cohp"][label]: @@ -701,6 +707,7 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: else: orb_cohp = {} + assert avg_cohp is not None return cls( structure, avg_cohp, @@ -743,7 +750,7 @@ def from_file( if are_coops and are_cobis: raise ValueError("You cannot have info about COOPs and COBIs in the same file.") - fmt = fmt.upper() + fmt = fmt.upper() # type: ignore[assignment] if fmt == "LMTO": # TODO: LMTO COOPs and orbital-resolved COHP cannot be handled yet are_coops = False @@ -1056,6 +1063,8 @@ def icohpvalue_orbital(self, orbitals: list[Orbital] | str, spin: Spin = Spin.up if isinstance(orbitals, list): # TODO: DanielYang: use tuple of 2 orbitals = f"{orbitals[0]}-{orbitals[1]}" + + assert self._orbitals is not None return self._orbitals[orbitals]["icohp"][spin] @property @@ -1084,6 +1093,7 @@ def summed_orbital_icohp(self) -> dict[str, float]: dict[str, float]: "str(Orbital1)-str(Ortibal2)": ICOHP value in eV. """ orbital_icohp = {} + assert self._orbitals is not None for orb, item in self._orbitals.items(): orbital_icohp[orb] = ( item["icohp"][Spin.up] + item["icohp"][Spin.down] if self._is_spin_polarized else item["icohp"][Spin.up] @@ -1135,7 +1145,6 @@ def __init__( self._are_coops = are_coops self._are_cobis = are_cobis - self._icohplist = {} self._is_spin_polarized = is_spin_polarized self._list_labels = list_labels self._list_atom1 = list_atom1 @@ -1146,6 +1155,7 @@ def __init__( self._list_icohp = list_icohp self._list_orb_icohp = list_orb_icohp + self._icohplist: dict[str, IcohpValue] = {} for idx, label in enumerate(list_labels): self._icohplist[label] = IcohpValue( label=label, @@ -1218,7 +1228,7 @@ def get_summed_icohp_by_label_list( Returns: float: Sum of ICOHPs selected with label_list. """ - sum_icohp = 0 + sum_icohp: float = 0 for label in label_list: icohp = self._icohplist[label] if icohp.num_bonds != 1: @@ -1259,7 +1269,7 @@ def get_icohp_dict_of_site( minbondlength: float = 0.0, maxbondlength: float = 8.0, only_bonds_to: list[str] | None = None, - ) -> dict[str | float, IcohpValue]: + ) -> dict[str, IcohpValue]: """Get IcohpValues for a certain site. Args: @@ -1364,7 +1374,7 @@ def are_cobis(self) -> bool: def get_integrated_cohp_in_energy_range( cohp: CompleteCohp, label: str, - orbital: Orbital | None = None, + orbital: CohpOrbital | None = None, # TODO: DanielYang: fix type energy_range: float | tuple[float, float] | None = None, relative_E_Fermi: bool = True, summed_spin_channels: bool = True, @@ -1390,8 +1400,11 @@ def get_integrated_cohp_in_energy_range( if orbital is None: icohps = cohp.all_cohps[label].get_icohp(spin=None) else: - icohps = cohp.get_orbital_resolved_cohp(label=label, orbitals=orbital).icohp + _icohps = cohp.get_orbital_resolved_cohp(label=label, orbitals=orbital) + assert _icohps is not None + icohps = _icohps.icohp + assert icohps is not None summedicohp = {} if summed_spin_channels and Spin.down in icohps: summedicohp[Spin.up] = icohps[Spin.up] + icohps[Spin.down] From 0afb7c26ff537b714c347cb10c58e51aac7f721a Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 21:11:07 +0800 Subject: [PATCH 18/39] fix remaining mypy errors --- pymatgen/electronic_structure/cohp.py | 7 +++---- pymatgen/io/lobster/outputs.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 7b977becf96..4904ccfc4b9 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -531,7 +531,7 @@ def get_summed_cohp_by_label_and_orbital_list( def get_orbital_resolved_cohp( self, label: str, - orbitals: str | list[Orbital] | tuple[Orbital, ...], + orbitals: str | list[tuple[str, Orbital]] | tuple[tuple[str, Orbital], ...], summed_spin_channels: bool = False, ) -> Cohp | None: """Get orbital-resolved COHP. @@ -541,7 +541,6 @@ def get_orbital_resolved_cohp( orbitals: The orbitals as a label, or list/tuple of [(n1, orbital1), (n2, orbital2), ...]. Where each orbital can either be str, int, or Orbital. - # TODO: DanielYang: clarify type summed_spin_channels (bool): Sum the spin channels and return the sum as Spin.up. Returns: @@ -1374,7 +1373,7 @@ def are_cobis(self) -> bool: def get_integrated_cohp_in_energy_range( cohp: CompleteCohp, label: str, - orbital: CohpOrbital | None = None, # TODO: DanielYang: fix type + orbital: str | None = None, energy_range: float | tuple[float, float] | None = None, relative_E_Fermi: bool = True, summed_spin_channels: bool = True, @@ -1384,7 +1383,7 @@ def get_integrated_cohp_in_energy_range( Args: cohp (CompleteCohp): CompleteCohp object. label (str): Label of the COHP data. - orbital: If not None, a orbital resolved integrated COHP will be returned. + orbital (str): If not None, a orbital resolved integrated COHP will be returned. energy_range: If None, return the ICOHP value at Fermi level. If float, integrate from this value up to Fermi level. If (float, float), integrate in between. diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 6b18294f62c..bad5952e107 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -429,7 +429,7 @@ def __init__( labels, atoms1, atoms2, lens, translations, nums, icohps = [], [], [], [], [], [], [] - # initialize static variables + # Initialize static variables label = "" atom1 = "" atom2 = "" From 5fcf23619fa804e31b0f7e2d521821a2552c6474 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 21:23:17 +0800 Subject: [PATCH 19/39] add DEBUG tag --- pymatgen/io/lobster/outputs.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index bad5952e107..ec4d7d7ae83 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -35,7 +35,7 @@ from typing import Any from pymatgen.core.structure import IStructure - from pymatgen.util.typing import PathLike + from pymatgen.util.typing import PathLike, Vector3D __author__ = "Janine George, Marco Esters" __copyright__ = "Copyright 2017, The Materials Project" @@ -388,14 +388,14 @@ def __init__( if len(data) == 0: raise RuntimeError("ICOHPLIST file contains no data.") - # Which Lobster version? + # Determine LOBSTER version if len(data[0].split()) == 8: version = "3.1.1" elif len(data[0].split()) == 6: version = "2.2.1" - warnings.warn("Please consider using the new Lobster version. See www.cohp.de.") + warnings.warn("Please consider using the new LOBSTER version. See www.cohp.de.") else: - raise ValueError + raise ValueError("Unsupported LOBSTER version.") # If the calculation is spin polarized, the line in the middle # of the file will be another header line. @@ -435,7 +435,7 @@ def __init__( atom2 = "" length = None num = None - translation = [] + translation: Vector3D | None = None for bond in range(n_bonds): line = data_without_orbitals[bond].split() @@ -447,16 +447,16 @@ def __init__( length = float(line[3]) icohp[Spin.up] = float(line[4]) num = int(line[5]) - translation = [0, 0, 0] + translation = (0, 0, 0) if self.is_spin_polarized: icohp[Spin.down] = float(data_without_orbitals[bond + n_bonds + 1].split()[4]) - elif version == "3.1.1": + else: # version == "3.1.1" label = f"{line[0]}" atom1 = str(line[1]) atom2 = str(line[2]) length = float(line[3]) - translation = [int(line[4]), int(line[5]), int(line[6])] + translation = (int(line[4]), int(line[5]), int(line[6])) icohp[Spin.up] = float(line[7]) num = 1 @@ -500,9 +500,9 @@ def __init__( list_atom1=atoms1, list_atom2=atoms2, list_length=lens, - list_translation=translations, + list_translation=translations, # type: ignore[arg-type] list_num=nums, - list_icohp=icohps, + list_icohp=icohps, # DEBUG: DanielYang: inconsistent type is_spin_polarized=self.is_spin_polarized, list_orb_icohp=list_orb_icohp, ) From 796c379d22fe3e173fcc50a0e9ed97136ae5c742 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 21:26:59 +0800 Subject: [PATCH 20/39] reduce code repetition --- pymatgen/io/lobster/outputs.py | 38 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index ec4d7d7ae83..e27a040b9e1 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -410,11 +410,11 @@ def __init__( if self.orbitalwise: data_without_orbitals = [] data_orbitals = [] - for line in data: - if "_" not in line.split()[1]: - data_without_orbitals += [line] + for line_parts in data: + if "_" not in line_parts.split()[1]: + data_without_orbitals += [line_parts] else: - data_orbitals += [line] + data_orbitals += [line_parts] else: data_without_orbitals = data @@ -438,26 +438,24 @@ def __init__( translation: Vector3D | None = None for bond in range(n_bonds): - line = data_without_orbitals[bond].split() + line_parts = data_without_orbitals[bond].split() + + label = f"{line_parts[0]}" + atom1 = str(line_parts[1]) + atom2 = str(line_parts[2]) + length = float(line_parts[3]) + icohp = {} if version == "2.2.1": - label = f"{line[0]}" - atom1 = str(line[1]) - atom2 = str(line[2]) - length = float(line[3]) - icohp[Spin.up] = float(line[4]) - num = int(line[5]) + icohp[Spin.up] = float(line_parts[4]) + num = int(line_parts[5]) translation = (0, 0, 0) if self.is_spin_polarized: icohp[Spin.down] = float(data_without_orbitals[bond + n_bonds + 1].split()[4]) else: # version == "3.1.1" - label = f"{line[0]}" - atom1 = str(line[1]) - atom2 = str(line[2]) - length = float(line[3]) - translation = (int(line[4]), int(line[5]), int(line[6])) - icohp[Spin.up] = float(line[7]) + translation = (int(line_parts[4]), int(line_parts[5]), int(line_parts[6])) + icohp[Spin.up] = float(line_parts[7]) num = 1 if self.is_spin_polarized: @@ -479,11 +477,11 @@ def __init__( for i_data_orb in range(n_orbs): data_orb = data_orbitals[i_data_orb] icohp = {} - line = data_orb.split() - label = f"{line[0]}" + line_parts = data_orb.split() + label = f"{line_parts[0]}" orbs = re.findall(r"_(.*?)(?=\s)", data_orb) orb_label, orbitals = get_orb_from_str(orbs) - icohp[Spin.up] = float(line[7]) + icohp[Spin.up] = float(line_parts[7]) if self.is_spin_polarized: icohp[Spin.down] = float(data_orbitals[n_orbs + i_data_orb].split()[7]) From 9551db207ce444119b35b5881e71e26c38527d74 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 21:38:33 +0800 Subject: [PATCH 21/39] Need Confirm: set `translation` as tuple --- pymatgen/io/lobster/outputs.py | 8 ------ tests/io/lobster/test_inputs.py | 48 ++++++++++++++++----------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index e27a040b9e1..cc93d889328 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -429,14 +429,6 @@ def __init__( labels, atoms1, atoms2, lens, translations, nums, icohps = [], [], [], [], [], [], [] - # Initialize static variables - label = "" - atom1 = "" - atom2 = "" - length = None - num = None - translation: Vector3D | None = None - for bond in range(n_bonds): line_parts = data_without_orbitals[bond].split() diff --git a/tests/io/lobster/test_inputs.py b/tests/io/lobster/test_inputs.py index 574676952d6..ee29a8ba0d4 100644 --- a/tests/io/lobster/test_inputs.py +++ b/tests/io/lobster/test_inputs.py @@ -444,77 +444,77 @@ def test_values(self): "length": 2.88231, "number_of_bonds": 3, "icohp": {Spin.up: -2.18042}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "2": { "length": 3.10144, "number_of_bonds": 3, "icohp": {Spin.up: -1.14347}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "3": { "length": 2.88231, "number_of_bonds": 3, "icohp": {Spin.up: -2.18042}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "4": { "length": 3.10144, "number_of_bonds": 3, "icohp": {Spin.up: -1.14348}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "5": { "length": 3.05001, "number_of_bonds": 3, "icohp": {Spin.up: -1.30006}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "6": { "length": 2.91676, "number_of_bonds": 3, "icohp": {Spin.up: -1.96843}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "7": { "length": 3.05001, "number_of_bonds": 3, "icohp": {Spin.up: -1.30006}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "8": { "length": 2.91676, "number_of_bonds": 3, "icohp": {Spin.up: -1.96843}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "9": { "length": 3.37522, "number_of_bonds": 3, "icohp": {Spin.up: -0.47531}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "10": { "length": 3.07294, "number_of_bonds": 3, "icohp": {Spin.up: -2.38796}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "11": { "length": 3.37522, "number_of_bonds": 3, "icohp": {Spin.up: -0.47531}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, } @@ -523,77 +523,77 @@ def test_values(self): "length": 2.88231, "number_of_bonds": 3, "icohp": {Spin.up: 0.14245}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "2": { "length": 3.10144, "number_of_bonds": 3, "icohp": {Spin.up: -0.04118}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "3": { "length": 2.88231, "number_of_bonds": 3, "icohp": {Spin.up: 0.14245}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "4": { "length": 3.10144, "number_of_bonds": 3, "icohp": {Spin.up: -0.04118}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "5": { "length": 3.05001, "number_of_bonds": 3, "icohp": {Spin.up: -0.03516}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "6": { "length": 2.91676, "number_of_bonds": 3, "icohp": {Spin.up: 0.10745}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "7": { "length": 3.05001, "number_of_bonds": 3, "icohp": {Spin.up: -0.03516}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "8": { "length": 2.91676, "number_of_bonds": 3, "icohp": {Spin.up: 0.10745}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "9": { "length": 3.37522, "number_of_bonds": 3, "icohp": {Spin.up: -0.12395}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "10": { "length": 3.07294, "number_of_bonds": 3, "icohp": {Spin.up: 0.24714}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "11": { "length": 3.37522, "number_of_bonds": 3, "icohp": {Spin.up: -0.12395}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, } @@ -602,14 +602,14 @@ def test_values(self): "length": 2.83189, "number_of_bonds": 2, "icohp": {Spin.up: -0.10218, Spin.down: -0.19701}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, "2": { "length": 2.45249, "number_of_bonds": 1, "icohp": {Spin.up: -0.28485, Spin.down: -0.58279}, - "translation": [0, 0, 0], + "translation": (0, 0, 0), "orbitals": None, }, } From c188030470ecee8101665b9581f878dd8b4c1b33 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:39:02 +0000 Subject: [PATCH 22/39] pre-commit auto-fixes --- pymatgen/io/lobster/outputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index cc93d889328..0d855bc60ef 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -35,7 +35,7 @@ from typing import Any from pymatgen.core.structure import IStructure - from pymatgen.util.typing import PathLike, Vector3D + from pymatgen.util.typing import PathLike __author__ = "Janine George, Marco Esters" __copyright__ = "Copyright 2017, The Materials Project" From e21c5ed367e343e7ca5c111d71e82c8dbf3aaa57 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Wed, 12 Jun 2024 22:01:05 +0800 Subject: [PATCH 23/39] more type clarify --- pymatgen/electronic_structure/cohp.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 4904ccfc4b9..559a022224b 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -331,7 +331,6 @@ def as_dict(self) -> dict[str, Any]: dct["COHP"] |= {label: {str(spin): pops.tolist() for spin, pops in self.all_cohps[label].cohp.items()}} icohp = self.all_cohps[label].icohp if icohp is not None: - # TODO: DanielYang59: merge two condition branches with "|=" operator? if "ICOHP" not in dct: dct["ICOHP"] = {label: {str(spin): pops.tolist() for spin, pops in icohp.items()}} else: @@ -467,7 +466,7 @@ def get_summed_cohp_by_label_list( def get_summed_cohp_by_label_and_orbital_list( self, label_list: list[str], - orbital_list: list, # TODO (DanielYang): what is its type? Add custom type for it? + orbital_list: list[str], divisor: float = 1, summed_spin_channels: bool = False, ) -> Cohp: @@ -475,7 +474,7 @@ def get_summed_cohp_by_label_and_orbital_list( Args: label_list (list[str]): Labels for the COHP that should be included. - orbital_list (list): Orbitals for the COHPs that should be included + orbital_list (list[str]): Orbitals for the COHPs that should be included (same order as label_list). divisor (float): The summed COHP will be divided by this divisor. summed_spin_channels (bool): Sum the spin channels and return the sum in Spin.up. @@ -1048,10 +1047,14 @@ def icohpvalue(self, spin: Spin = Spin.up) -> float: return self._icohp[spin] - def icohpvalue_orbital(self, orbitals: list[Orbital] | str, spin: Spin = Spin.up) -> float: + def icohpvalue_orbital( + self, + orbitals: tuple[Orbital, Orbital] | str, + spin: Spin = Spin.up, + ) -> float: """ Args: - orbitals (list[Orbitals]): List of Orbitals or "str(Orbital1)-str(Orbital2)". + orbitals: tuple[Orbital, Orbital] or "str(Orbital0)-str(Orbital1)". spin (Spin): Spin.up or Spin.down. Returns: @@ -1060,7 +1063,7 @@ def icohpvalue_orbital(self, orbitals: list[Orbital] | str, spin: Spin = Spin.up if not self.is_spin_polarized and spin == Spin.down: raise ValueError("The calculation was not performed with spin polarization") - if isinstance(orbitals, list): # TODO: DanielYang: use tuple of 2 + if isinstance(orbitals, (tuple, list)): orbitals = f"{orbitals[0]}-{orbitals[1]}" assert self._orbitals is not None @@ -1177,7 +1180,7 @@ def get_icohp_by_label( label: str, summed_spin_channels: bool = True, spin: Spin = Spin.up, - orbitals: str | list[Orbital] | None = None, # TODO: DanielYang: use tuple of 2 + orbitals: str | tuple[Orbital, Orbital] | None = None, ) -> float: """Get an ICOHP value for a certain bond indicated by the label. @@ -1198,7 +1201,7 @@ def get_icohp_by_label( if orbitals is None: return icohp.summed_icohp if summed_spin_channels else icohp.icohpvalue(spin) - if isinstance(orbitals, list): + if isinstance(orbitals, (tuple, list)): orbitals = f"{orbitals[0]}-{orbitals[1]}" if summed_spin_channels: From 5b81360cb6563e9d38d246554035695ef029ac46 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 09:57:23 +0800 Subject: [PATCH 24/39] clarify `num` argument --- pymatgen/electronic_structure/cohp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 559a022224b..72f15d5a5d8 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -951,7 +951,7 @@ def __init__( atom2 (str): The second atom that contributes to the bond. length (float): Bond length. translation (Vector3D): cell translation vector, e.g. (0, 0, 0). - num (int): How often the bond exists. # TODO: DanielYang: clarify this description + num (int): The number of equivalent bonds. icohp (dict): {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} are_coops (bool): Whether these are COOPs. are_cobis (bool): Whether these are COBIs. From 5f760df63244aab0759edf296c626edf3b6fe915 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 12:37:52 +0800 Subject: [PATCH 25/39] clarify docstring of `bandstructure` --- .../electronic_structure/bandstructure.py | 364 +++++++++--------- 1 file changed, 176 insertions(+), 188 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index 2b466543270..d77314899d2 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -33,9 +33,8 @@ class Kpoint(MSONable): - """Store kpoint objects. A kpoint is defined with a lattice and frac - or Cartesian coordinates syntax similar than the site object in - pymatgen.core.structure. + """A kpoint is defined with a lattice and frac or Cartesian coordinates, + syntax similar than the Site object in pymatgen.core.structure. """ def __init__( @@ -48,15 +47,14 @@ def __init__( ) -> None: """ Args: - coords: coordinate of the kpoint as a numpy array - lattice: A pymatgen.core.Lattice object representing - the reciprocal lattice of the kpoint - to_unit_cell: Translates fractional coordinate to the basic unit + coords (NDArray): Coordinate of the Kpoint. + lattice (Lattice): The reciprocal lattice of the kpoint. + to_unit_cell (bool): Translate fractional coordinate to the basic unit cell, i.e., all fractional coordinates satisfy 0 <= a < 1. Defaults to False. - coords_are_cartesian: Boolean indicating if the coordinates given are - in Cartesian or fractional coordinates (by default fractional) - label: the label of the kpoint if any (None by default). + coords_are_cartesian (bool): Whether the coordinates given are + in Cartesian (True) or fractional coordinates (by default fractional). + label (str): The label of the Kpoint if any (None by default). """ self._lattice = lattice self._frac_coords = lattice.get_fractional_coords(coords) if coords_are_cartesian else coords @@ -73,9 +71,10 @@ def __str__(self) -> str: return f"{self.frac_coords} {self.cart_coords} {self.label}" def __eq__(self, other: object) -> bool: - """Check if two Kpoints are equal.""" + """Whether two Kpoints are equal.""" if not isinstance(other, type(self)): return NotImplemented + return ( np.allclose(self.frac_coords, other.frac_coords) and self.lattice == other.lattice @@ -84,9 +83,7 @@ def __eq__(self, other: object) -> bool: @property def lattice(self) -> Lattice: - """The lattice associated with the kpoint. It's a - pymatgen.core.Lattice object. - """ + """The lattice associated with the kpoint, as a Lattice object.""" return self._lattice @property @@ -101,12 +98,12 @@ def label(self, label: str | None) -> None: @property def frac_coords(self) -> NDArray: - """The fractional coordinates of the kpoint as a numpy array.""" + """The fractional coordinates of the kpoint as a NumPy array.""" return np.copy(self._frac_coords) @property def cart_coords(self) -> NDArray: - """The Cartesian coordinates of the kpoint as a numpy array.""" + """The Cartesian coordinates of the kpoint as a NumPy array.""" return np.copy(self._cart_coords) @property @@ -125,7 +122,7 @@ def c(self) -> float: return self._frac_coords[2] def as_dict(self) -> dict[str, Any]: - """JSON-serializable dict representation of a kpoint.""" + """JSON-serializable dict representation of the kpoint.""" return { "lattice": self.lattice.as_dict(), "fcoords": self.frac_coords.tolist(), @@ -137,7 +134,7 @@ def as_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls, dct: dict) -> Self: - """Create from dict. + """Create from a dict. Args: dct (dict): A dict with all data for a kpoint object. @@ -150,23 +147,23 @@ def from_dict(cls, dct: dict) -> Self: class BandStructure: - """This is the most generic band structure data possible - it's defined by a list of Kpoints and energies for each of them. + """Generic band structure data, defined by a list of Kpoints + and corresponding energies for each of them. Attributes: - kpoints (list): The list of kpoints (as Kpoint objects) in the band structure. + kpoints (list[Kpoint]): Kpoints in the band structure. lattice_rec (Lattice): The reciprocal lattice of the band structure. - efermi (float): The Fermi energy. - is_spin_polarized (bool): True if the band structure is spin-polarized. - bands (dict): The energy eigenvalues as a {spin: array}. Note that the use of an - array is necessary for computational as well as memory efficiency due to the large - amount of numerical data. The indices of the array are [band_index, kpoint_index]. - nb_bands (int): Returns the number of bands in the band structure. - structure (Structure): Returns the structure. - projections (dict): The projections as a {spin: array}. Note that the use of an - array is necessary for computational as well as memory efficiency due to the large - amount of numerical data. The indices of the array are [band_index, kpoint_index, - orbital_index, ion_index]. + efermi (float): The Fermi level. + is_spin_polarized (bool): Whether the band structure is spin-polarized. + bands (dict[Spin, NDArray]): The energy eigenvalues. Note that the use of an + array is necessary for computational and memory efficiency due to the large + amount of numerical data. The indices of the array are (band_index, kpoint_index). + nb_bands (int): The number of bands in the band structure. + structure (Structure): The structure. + projections (dict[Spin, NDArray]): The projections. Note that the use of an + array is necessary for computational and memory efficiency due to the large + amount of numerical data. The indices of the array are (band_index, kpoint_index, + orbital_index, ion_index). """ def __init__( @@ -175,34 +172,32 @@ def __init__( eigenvals: dict[Spin, NDArray], lattice: Lattice, efermi: float, - labels_dict=None, + labels_dict: dict[Kpoint, str] | None = None, coords_are_cartesian: bool = False, structure: Structure | None = None, projections: dict[Spin, NDArray] | None = None, ) -> None: """ Args: - kpoints: list of Kpoint as numpy arrays, in frac_coords of the - given lattice by default - eigenvals: dict of energies for spin up and spin down - {Spin.up:[][],Spin.down:[][]}, the first index of the array - [][] refers to the band and the second to the index of the - kpoint. The kpoints are ordered according to the order of the - kpoints array. If the band structure is not spin polarized, we - only store one data set under Spin.up - lattice: The reciprocal lattice as a pymatgen Lattice object. - Pymatgen uses the physics convention of reciprocal lattice vectors - WITH a 2*pi coefficient - efermi (float): Fermi energy - labels_dict: (dict) of {} this links a kpoint (in frac coords or - Cartesian coordinates depending on the coords) to a label. - coords_are_cartesian: Whether coordinates are cartesian. - structure: The crystal structure (as a pymatgen Structure object) + kpoints (NDArray): Kpoint as NumPy array, in frac_coords of the + given lattice by default. + eigenvals (dict): Energies for spin up and spin down as + {Spin.up:[][], Spin.down:[][]}, the first index of the array + [][] refers to the band and the second to the index of the kpoint. + The kpoints are ordered according to the kpoints array. + If the band structure is not spin polarized, we + only store one data set under Spin.up. + lattice (Lattice): The reciprocal lattice. Pymatgen uses the physics + convention of reciprocal lattice vectors with a 2*pi coefficient. + efermi (float): The Fermi level. + labels_dict (dict[Kpoint, str]): Dict mapping kpoint to label. + coords_are_cartesian (bool): Whether coordinates are cartesian. + structure (Structure): The crystal structure associated with the band structure. This is needed if we - provide projections to the band structure - projections: dict of orbital projections as {spin: array}. The - indices of the array are [band_index, kpoint_index, orbital_index, - ion_index].If the band structure is not spin polarized, we only + provide projections to the band structure. + projections (dict[Spin, NDArray]): Orbital projections. The + indices of the array are (band_index, kpoint_index, orbital_index, + ion_index). If the band structure is not spin polarized, we only store one data set under Spin.up. """ self.efermi = efermi @@ -237,13 +232,12 @@ def __init__( self.is_spin_polarized = len(self.bands) == 2 def get_projection_on_elements(self) -> dict[Spin, NDArray]: - """Get a dictionary of projections on elements. + """Get projections on elements. Returns: - a dictionary in the {Spin.up:[][{Element: [values]}], - Spin.down:[][{Element: [values]}]} format - if there is no projections in the band structure - returns an empty dict + dict[Spin, NDArray]: Dict in {Spin.up:[][{Element: [values]}], + Spin.down: [][{Element: [values]}]} format. + If there is no projections in the band structure, return {}. """ assert self.structure is not None result: dict[Spin, NDArray] = {} @@ -258,17 +252,17 @@ def get_projection_on_elements(self) -> dict[Spin, NDArray]: return result def get_projections_on_elements_and_orbitals(self, el_orb_spec: dict[str, list[str]]): - """Get a dictionary of projections on elements and specific orbitals. + """Get projections on elements and specific orbitals. Args: - el_orb_spec (dict[str, list[str]]): A dictionary of elements and orbitals which - to project onto. Format is {Element: [orbitals]}, e.g. {'Cu':['d','s']}. + el_orb_spec (dict[str, list[str]]): Elements and orbitals to project onto. + Format is {Element: [orbitals]}, e.g. {"Cu": ["d", "s"]}. Returns: - A dictionary of projections on elements in the - {Spin.up:[][{Element:{orb:values}}], - Spin.down:[][{Element:{orb:values}}]} format - if there is no projections in the band structure returns an empty dict. + dict[str, list[str]: Projections on elements in the + {Spin.up: [][{Element: {orb: values}}], + Spin.down: [][{Element: {orb: values}}]} format. + If there is no projections in the band structure, return {}. """ if self.structure is None: raise ValueError("Structure is required for this method") @@ -297,7 +291,7 @@ def is_metal(self, efermi_tol: float = 1e-4) -> bool: level crosses a band. Returns: - bool: True if a metal. + bool: True if is metal. """ for vals in self.bands.values(): for idx in range(self.nb_bands): @@ -306,25 +300,25 @@ def is_metal(self, efermi_tol: float = 1e-4) -> bool: return False def get_vbm(self) -> dict[str, Any]: - """Get data about the VBM. + """Get data about the valence band maximum (VBM). Returns: - dict: With keys "band_index", "kpoint_index", "kpoint", "energy" - - "band_index": A dict with spin keys pointing to a list of the - indices of the band containing the VBM (please note that you - can have several bands sharing the VBM) {Spin.up:[], - Spin.down:[]} - - "kpoint_index": The list of indices in self.kpoints for the - kpoint VBM. Please note that there can be several - kpoint_indices relating to the same kpoint (e.g., Gamma can - occur at different spots in the band structure line plot) - - "kpoint": The kpoint (as a kpoint object) - - "energy": The energy of the VBM - - "projections": The projections along sites and orbitals of the - VBM if any projection data is available (else it is an empty - dictionary). The format is similar to the projections field in - BandStructure: {spin:{'Orbital': [proj]}} where the array - [proj] is ordered according to the sites in structure + dict of keys "band_index", "kpoint_index", "kpoint", "energy": + - "band_index" (dict): A dict with spin keys pointing to a list of the + indices of the band containing the VBM (please note that you + can have several bands sharing the VBM) {Spin.up:[], + Spin.down:[]}. + - "kpoint_index": The list of indices in self.kpoints for the + kpoint VBM. Please note that there can be several + kpoint_indices relating to the same kpoint (e.g., Gamma can + occur at different spots in the band structure line plot). + - "kpoint" (Kpoint): The kpoint. + - "energy" (float): The energy of the VBM. + - "projections": The projections along sites and orbitals of the + VBM if any projection data is available (else it is an empty + dictionary). The format is similar to the projections field in + BandStructure: {spin:{'Orbital': [proj]}} where the array + [proj] is ordered according to the sites in structure. """ if self.is_metal(): return { @@ -334,6 +328,7 @@ def get_vbm(self) -> dict[str, Any]: "energy": None, "projections": {}, } + max_tmp = -float("inf") index = kpoint_vbm = None for value in self.bands.values(): @@ -351,7 +346,7 @@ def get_vbm(self) -> dict[str, Any]: else: list_ind_kpts.append(index) - # Get all other bands sharing the vbm + # Get all other bands sharing the VBM list_ind_band = defaultdict(list) for spin in self.bands: for idx in range(self.nb_bands): @@ -362,6 +357,7 @@ def get_vbm(self) -> dict[str, Any]: if len(list_ind_band[spin]) == 0: continue proj[spin] = value[list_ind_band[spin][0]][list_ind_kpts[0]] + return { "band_index": list_ind_band, "kpoint_index": list_ind_kpts, @@ -371,24 +367,24 @@ def get_vbm(self) -> dict[str, Any]: } def get_cbm(self) -> dict[str, Any]: - """Get data about the CBM. + """Get data about the conduction band minimum (CBM). Returns: - dict[str, Any]: with keys band_index, kpoint_index, kpoint, energy. - - "band_index": A dict with spin keys pointing to a list of the + dict of keys "band_index", "kpoint_index", "kpoint", "energy": + - "band_index" (dict): A dict with spin keys pointing to a list of the indices of the band containing the CBM (please note that you - can have several bands sharing the CBM) {Spin.up:[], Spin.down:[]} + can have several bands sharing the CBM) {Spin.up:[], Spin.down:[]}. - "kpoint_index": The list of indices in self.kpoints for the kpoint CBM. Please note that there can be several kpoint_indices relating to the same kpoint (e.g., Gamma can - occur at different spots in the band structure line plot) - - "kpoint": The kpoint (as a kpoint object) - - "energy": The energy of the CBM + occur at different spots in the band structure line plot). + - "kpoint" (Kpoint): The kpoint. + - "energy" (float): The energy of the CBM. - "projections": The projections along sites and orbitals of the CBM if any projection data is available (else it is an empty dictionary). The format is similar to the projections field in BandStructure: {spin:{'Orbital': [proj]}} where the array - [proj] is ordered according to the sites in structure + [proj] is ordered according to the sites in structure. """ if self.is_metal(): return { @@ -398,8 +394,8 @@ def get_cbm(self) -> dict[str, Any]: "energy": None, "projections": {}, } - max_tmp = float("inf") + max_tmp = float("inf") index = kpoint_cbm = None for value in self.bands.values(): for idx, j in zip(*np.where(value >= self.efermi)): @@ -416,7 +412,7 @@ def get_cbm(self) -> dict[str, Any]: else: list_index_kpoints.append(index) - # get all other bands sharing the cbm + # Get all other bands sharing the CBM list_index_band = defaultdict(list) for spin in self.bands: for idx in range(self.nb_bands): @@ -437,13 +433,13 @@ def get_cbm(self) -> dict[str, Any]: } def get_band_gap(self) -> dict[str, Any]: - r"""Get band gap data. + r"""Get band gap. Returns: - A dict {"energy", "direct", "transition"}: - "energy": band gap energy - "direct": A boolean telling if the gap is direct or not - "transition": kpoint labels of the transition (e.g., "\\Gamma-X") + dict of keys "energy", "direct", "transition": + "energy" (float): Band gap energy. + "direct" (bool): Whether the gap is direct. + "transition" (str): Kpoint labels of the transition (e.g., "\\Gamma-X"). """ if self.is_metal(): return {"energy": 0.0, "direct": False, "transition": None} @@ -471,15 +467,15 @@ def get_band_gap(self) -> dict[str, Any]: return result def get_direct_band_gap_dict(self) -> dict[Spin, dict[str, Any]]: - """Get a dictionary of information about the direct - band gap. + """Get information about the direct band gap. Returns: - a dictionary of the band gaps indexed by spin - along with their band indices and k-point index + dict[Spin, dict[str, Any]]: The band gaps indexed by spin + along with their band indices and kpoint index. """ if self.is_metal(): raise ValueError("get_direct_band_gap_dict should only be used with non-metals") + direct_gap_dict = {} for spin, v in self.bands.items(): above = v[np.all(v > self.efermi, axis=1)] @@ -503,7 +499,7 @@ def get_direct_band_gap(self) -> float: """Get the direct band gap. Returns: - the value of the direct band gap + float: The direct band gap value. """ if self.is_metal(): return 0.0 @@ -517,15 +513,15 @@ def get_sym_eq_kpoints( cartesian: bool = False, tol: float = 1e-2, ) -> NDArray: - """Get a list of unique symmetrically equivalent Kpoints. + """Get unique symmetrically equivalent Kpoints. Args: - kpoint (1x3 array): coordinate of the Kpoint - cartesian (bool): kpoint is in Cartesian or fractional coordinates - tol (float): tolerance below which coordinates are considered equal + kpoint (1x3 array): Coordinate of the Kpoint. + cartesian (bool): Whether kpoint is in Cartesian or fractional coordinates. + tol (float): Tolerance below which coordinates are considered equal. Returns: - list[1x3 array] | None: if structure is not available returns None + (1x3 NDArray) | None: None if structure is not available. """ if not self.structure: return None @@ -534,7 +530,7 @@ def get_sym_eq_kpoints( symm_ops = sg.get_point_group_operations(cartesian=cartesian) points = np.dot(kpoint, [m.rotation_matrix for m in symm_ops]) rm_list = [] - # Identify and remove duplicates from the list of equivalent k-points: + # Identify and remove duplicates from equivalent k-points for i in range(len(points) - 1): for j in range(i + 1, len(points)): if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol): @@ -548,15 +544,15 @@ def get_kpoint_degeneracy( cartesian: bool = False, tol: float = 1e-2, ) -> NDArray | None: - """Get degeneracy of a given k-point based on structure symmetry. + """Get degeneracy of a given kpoint based on structure symmetry. Args: - kpoint (1x3 array): coordinate of the k-point - cartesian (bool): kpoint is in Cartesian or fractional coordinates - tol (float): tolerance below which coordinates are considered equal. + kpoint (1x3 NDArray): Coordinate of the k-point. + cartesian (bool): Whether kpoint is in Cartesian or fractional coordinates. + tol (float): Tolerance below which coordinates are considered equal. Returns: - int | None: degeneracy or None if structure is not available + int | None: Degeneracy, or None if structure is not available. """ all_kpts = self.get_sym_eq_kpoints(kpoint, cartesian, tol=tol) return len(all_kpts) if all_kpts is not None else None @@ -571,7 +567,7 @@ def as_dict(self) -> dict[str, Any]: "kpoints": [], } # kpoints are not kpoint objects dicts but are frac coords (this makes - # the dict smaller and avoids the repetition of the lattice + # the dict smaller and avoids the repetition of the lattice). for k in self.kpoints: dct["kpoints"].append(k.as_dict()["fcoords"]) @@ -595,7 +591,7 @@ def as_dict(self) -> dict[str, Any]: dct["labels_dict"] = {} dct["is_spin_polarized"] = self.is_spin_polarized - # MongoDB does not accept keys starting with $. Add a blank space to fix the problem + # MongoDB does not accept keys starting with "$", add a space to fix this. for c, label in self.labels_dict.items(): mongo_key = f" {c}" if c.startswith("$") else c dct["labels_dict"][mongo_key] = label.as_dict()["fcoords"] @@ -607,27 +603,28 @@ def as_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls, dct: dict[str, Any]) -> Self: - """Create from dict. + """Create from a dict. Args: - dct: A dict with all data for a band structure object. + dct: A dict with all data for a BandStructure. Returns: - A BandStructure object + A BandStructure object. """ # Strip the label to recover initial string - # (see trick used in as_dict to handle $ chars) + # (see trick used in as_dict to handle "$"" chars) labels_dict = {k.strip(): v for k, v in dct["labels_dict"].items()} - projections = {} - structure = None + if isinstance(next(iter(dct["bands"].values())), dict): eigenvals = {Spin(int(k)): np.array(dct["bands"][k]["data"]) for k in dct["bands"]} else: eigenvals = {Spin(int(k)): dct["bands"][k] for k in dct["bands"]} + structure = None if "structure" in dct: structure = Structure.from_dict(dct["structure"]) + projections = {} try: if dct.get("projections"): if isinstance(dct["projections"]["1"][0][0], dict): @@ -657,12 +654,13 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: def from_old_dict(cls, dct: dict[str, Any]) -> Self: """ Args: - dct (dict): A dict with all data for a band structure symmetry line object. + dct (dict): A dict with all data for a BandStructure object. Returns: - A BandStructureSymmLine object + A BandStructure object. """ - # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) + # Strip the label to recover initial string + # (see trick used in as_dict to handle "$" chars) labels_dict = {k.strip(): v for k, v in dct["labels_dict"].items()} projections: dict = {} structure = None @@ -707,33 +705,31 @@ def __init__( eigenvals: dict[Spin, list], lattice: Lattice, efermi: float, - labels_dict: dict, + labels_dict: dict[Kpoint, str], coords_are_cartesian: bool = False, structure: Structure | None = None, projections: dict[Spin, NDArray] | None = None, ) -> None: """ Args: - kpoints: list of kpoint as numpy arrays, in frac_coords of the + kpoints (NDArray): Array of kpoint, in frac_coords of the given lattice by default - eigenvals: dict of energies for spin up and spin down + eigenvals (dict[Spin, list]): Energies for spin up and spin down {Spin.up:[][],Spin.down:[][]}, the first index of the array [][] refers to the band and the second to the index of the kpoint. The kpoints are ordered according to the order of the kpoints array. If the band structure is not spin polarized, we only store one data set under Spin.up. - lattice: The reciprocal lattice. - Pymatgen uses the physics convention of reciprocal lattice vectors - WITH a 2*pi coefficient - efermi: fermi energy - labels_dict: (dict) of {} this link a kpoint (in frac coords or - Cartesian coordinates depending on the coords). - coords_are_cartesian: Whether coordinates are cartesian. - structure: The crystal structure (as a pymatgen Structure object) - associated with the band structure. This is needed if we - provide projections to the band structure. - projections: dict of orbital projections as {spin: array}. The - indices of the array are [band_index, kpoint_index, orbital_index, + lattice (Lattice): The reciprocal lattice. Pymatgen uses the physics + convention of reciprocal lattice vectors with a 2*pi coefficient. + efermi (float): The Fermi level. + labels_dict (dict[Kpoint, str]): Dict mapping kpoint to label. + coords_are_cartesian (bool): Whether coordinates are cartesian. + structure (Structure): The crystal structure associated with the + band structure. This is needed if we provide projections to + the band structure. + projections (dict[Spin, NDArray]): Orbital projections as {spin: array}. + The indices of the array are [band_index, kpoint_index, orbital_index, ion_index].If the band structure is not spin polarized, we only store one data set under Spin.up. """ @@ -787,21 +783,19 @@ def __init__( self.is_spin_polarized = True def get_equivalent_kpoints(self, index: int) -> list[int]: - """Get the list of kpoint indices equivalent (meaning they are the - same frac coords) to the given one. + """Get kpoint indices equivalent (having the same coords) to the given one. Args: - index: the kpoint index + index (int): The kpoint index Returns: - a list of equivalent indices + list[int]: Equivalent indices. - TODO: now it uses the label we might want to use coordinates instead + TODO: now it uses the label, we might want to use coordinates instead. (in case there was a mislabel) """ # If the kpoint has no label it can't have a repetition # along the BandStructureSymmLine object - if self.kpoints[index].label is None: return [index] @@ -813,17 +807,15 @@ def get_equivalent_kpoints(self, index: int) -> list[int]: return list_index_kpoints def get_branch(self, index: int) -> list[dict[str, Any]]: - r"""Get in what branch(es) is the kpoint. There can be several - branches. + """Get what branch(es) is the kpoint. It takes into account the + fact that one kpoint (e.g., Gamma) can be in several branches. Args: - index: the kpoint index + index (int): The kpoint index. Returns: - A list of dictionaries [{"name","start_index","end_index","index"}] - indicating all branches in which the k_point is. It takes into - account the fact that one kpoint (e.g., \\Gamma) can be in several - branches + A list of dicts [{"name", "start_index", "end_index", "index"}] + indicating all branches in which the k_point is. """ to_return = [] for idx in self.get_equivalent_kpoints(index): @@ -845,10 +837,10 @@ def apply_scissor(self, new_band_gap: float) -> Self: and shift this one up. This will not work all the time for metals! Args: - new_band_gap: the band gap the scissor band structure need to have. + new_band_gap (float): The band gap the scissor band structure need to have. Returns: - BandStructureSymmLine: with the applied scissor shift + BandStructureSymmLine: With the applied scissor shift. """ if self.is_metal(): # Move then the highest index band crossing the Fermi level find this band... @@ -904,7 +896,7 @@ def as_dict(self) -> dict[str, Any]: class LobsterBandStructureSymmLine(BandStructureSymmLine): - """Lobster subclass of BandStructure with customized functions.""" + """LOBSTER subclass of BandStructure with customized functions.""" def as_dict(self) -> dict[str, Any]: """JSON-serializable dict representation of BandStructureSymmLine.""" @@ -940,9 +932,9 @@ def as_dict(self) -> dict[str, Any]: dct["labels_dict"] = {} dct["is_spin_polarized"] = self.is_spin_polarized - # MongoDB does not accept keys starting with $. Add a blank space to fix the problem + # MongoDB does not accept keys starting with "$", add a space to fix this. for c, label in self.labels_dict.items(): - mongo_key = c if not c.startswith("$") else " " + c + mongo_key = f" {c}" if c.startswith("$") else c dct["labels_dict"][mongo_key] = label.as_dict()["fcoords"] if len(self.projections) != 0 and self.structure is not None: dct["structure"] = self.structure.as_dict() @@ -953,14 +945,14 @@ def as_dict(self) -> dict[str, Any]: def from_dict(cls, dct: dict[str, Any]) -> Self: """ Args: - dct (dict): A dict with all data for a band structure symmetry line - object. + dct (dict): All data for a LobsterBandStructureSymmLine object. Returns: - A BandStructureSymmLine object + A LobsterBandStructureSymmLine object. """ try: - # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) + # Strip the label to recover initial string + # (see trick used in as_dict to handle "$" chars) labels_dict = {k.strip(): v for k, v in dct["labels_dict"].items()} projections = {} structure = None @@ -992,13 +984,13 @@ def from_dict(cls, dct: dict[str, Any]) -> Self: def from_old_dict(cls, dct: dict[str, Any]) -> Self: """ Args: - dct (dict): A dict with all data for a band structure symmetry line - object. + dct (dict): All data for a LobsterBandStructureSymmLine object. Returns: - A BandStructureSymmLine object + A LobsterBandStructureSymmLine object """ - # Strip the label to recover initial string (see trick used in as_dict to handle $ chars) + # Strip the label to recover initial string + # (see trick used in as_dict to handle "$" chars) labels_dict = {k.strip(): v for k, v in dct["labels_dict"].items()} projections: dict = {} structure = None @@ -1025,14 +1017,13 @@ def from_old_dict(cls, dct: dict[str, Any]) -> Self: ) def get_projection_on_elements(self) -> dict[Spin, list]: - """Get a dictionary of projections on elements. It sums over all available orbitals + """Get projections on elements. It sums over all available orbitals for each element. Returns: - a dictionary in the {Spin.up:[][{Element:values}], - Spin.down:[][{Element:values}]} format - if there is no projections in the band structure - returns an empty dict + dict[Spin, list]: dict in the {Spin.up:[][{Element:values}], + Spin.down:[][{Element:values}]} format. + If there is no projections in the band structure, return {}. """ result: dict[Spin, list] = {} for spin, v in self.projections.items(): @@ -1048,12 +1039,13 @@ def get_projections_on_elements_and_orbitals( self, el_orb_spec: dict[Element, list], # type: ignore[override] ) -> dict[Spin, list]: - """Return a dictionary of projections on elements and specific orbitals. + """Get projections on elements and specific orbitals. Args: - el_orb_spec: A dictionary of Elements and Orbitals for which we want - to have projections on. It is given as: {Element:[orbitals]}, - e.g. {'Si':['3s','3p']} or {'Si':['3s','3p_x', '3p_y', '3p_z']} depending on input files + el_orb_spec (dict): Elements and Orbitals for which we want + to project on. It is given as {Element: [orbitals]}, + e.g. {"Si": ["3s", "3p"]} or {"Si": ["3s", "3p_x", "3p_y", "3p_z']} + depending on input files. Returns: A dictionary of projections on elements in the @@ -1099,22 +1091,20 @@ def get_reconstructed_band_structure( list_bs: list[BandStructure] | list[BandStructureSymmLine], efermi: float | None = None, ) -> BandStructure | BandStructureSymmLine: - """Take a list of band structures and reconstructs - one band structure object from all of them. + """Merge band structures to one single BandStructure(SymmLine) object. - This is typically very useful when you split non self consistent - band structure runs in several independent jobs and want to merge back - the results + This is typically useful when you split non self-consistent band + structure runs to several independent jobs and want to merge the results. Args: - list_bs: A list of BandStructure or BandStructureSymmLine objects. - efermi: The Fermi energy of the reconstructed band structure. If - None is assigned an average of all the Fermi energy in each + list_bs (list): BandStructure or BandStructureSymmLine objects. + efermi (float): The Fermi level of the reconstructed band structure. + If None, an average of all the Fermi levels in each object in the list_bs is used. Returns: A BandStructure or BandStructureSymmLine object (depending on - the type of the list_bs objects) + the type of the list_bs objects). """ if efermi is None: efermi = sum(b.efermi for b in list_bs) / len(list_bs) @@ -1128,9 +1118,7 @@ def get_reconstructed_band_structure( dicts = [bs.labels_dict for bs in list_bs] labels_dict = {k: v.frac_coords for d in dicts for k, v in d.items()} - eigenvals = {} - eigenvals[Spin.up] = np.concatenate([bs.bands[Spin.up][:nb_bands] for bs in list_bs], axis=1) - + eigenvals = {Spin.up: np.concatenate([bs.bands[Spin.up][:nb_bands] for bs in list_bs], axis=1)} if list_bs[0].is_spin_polarized: eigenvals[Spin.down] = np.concatenate([bs.bands[Spin.down][:nb_bands] for bs in list_bs], axis=1) From e558ac7d99869af913dc68c0b3029f8df9a21629 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 12:42:07 +0800 Subject: [PATCH 26/39] more minor tweaks --- pymatgen/electronic_structure/bandstructure.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index d77314899d2..9ee8616b8a0 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -1,4 +1,4 @@ -"""This module provides classes to define everything related to band structures.""" +"""This module provides classes to define things related to band structures.""" from __future__ import annotations @@ -33,8 +33,8 @@ class Kpoint(MSONable): - """A kpoint is defined with a lattice and frac or Cartesian coordinates, - syntax similar than the Site object in pymatgen.core.structure. + """A kpoint defined with a lattice and frac or Cartesian coordinates, + similar to the Site object in pymatgen.core.structure. """ def __init__( @@ -287,8 +287,8 @@ def get_projections_on_elements_and_orbitals(self, el_orb_spec: dict[str, list[s return result def is_metal(self, efermi_tol: float = 1e-4) -> bool: - """Check if the band structure indicates a metal by looking if the fermi - level crosses a band. + """Check if the band structure indicates a metal, + by looking at if the fermi level crosses a band. Returns: bool: True if is metal. @@ -1109,8 +1109,6 @@ def get_reconstructed_band_structure( if efermi is None: efermi = sum(b.efermi for b in list_bs) / len(list_bs) - kpoints = [] - labels_dict = {} rec_lattice = list_bs[0].lattice_rec nb_bands = min(list_bs[i].nb_bands for i in range(len(list_bs))) From 4c0bb04e749af1370d0cadb19a48410617f30777 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 16:59:07 +0800 Subject: [PATCH 27/39] clarify type of labels_dict --- .../electronic_structure/bandstructure.py | 20 +++++++++---------- pymatgen/io/vasp/outputs.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pymatgen/electronic_structure/bandstructure.py b/pymatgen/electronic_structure/bandstructure.py index 9ee8616b8a0..73378506807 100644 --- a/pymatgen/electronic_structure/bandstructure.py +++ b/pymatgen/electronic_structure/bandstructure.py @@ -172,7 +172,7 @@ def __init__( eigenvals: dict[Spin, NDArray], lattice: Lattice, efermi: float, - labels_dict: dict[Kpoint, str] | None = None, + labels_dict: dict[str, Kpoint] | None = None, coords_are_cartesian: bool = False, structure: Structure | None = None, projections: dict[Spin, NDArray] | None = None, @@ -190,7 +190,7 @@ def __init__( lattice (Lattice): The reciprocal lattice. Pymatgen uses the physics convention of reciprocal lattice vectors with a 2*pi coefficient. efermi (float): The Fermi level. - labels_dict (dict[Kpoint, str]): Dict mapping kpoint to label. + labels_dict (dict[str, Kpoint]): Dict mapping label to Kpoint. coords_are_cartesian (bool): Whether coordinates are cartesian. structure (Structure): The crystal structure associated with the band structure. This is needed if we @@ -705,7 +705,7 @@ def __init__( eigenvals: dict[Spin, list], lattice: Lattice, efermi: float, - labels_dict: dict[Kpoint, str], + labels_dict: dict[str, Kpoint], coords_are_cartesian: bool = False, structure: Structure | None = None, projections: dict[Spin, NDArray] | None = None, @@ -723,7 +723,7 @@ def __init__( lattice (Lattice): The reciprocal lattice. Pymatgen uses the physics convention of reciprocal lattice vectors with a 2*pi coefficient. efermi (float): The Fermi level. - labels_dict (dict[Kpoint, str]): Dict mapping kpoint to label. + labels_dict (dict[str, Kpoint]): Dict mapping label to Kpoint. coords_are_cartesian (bool): Whether coordinates are cartesian. structure (Structure): The crystal structure associated with the band structure. This is needed if we provide projections to @@ -791,8 +791,8 @@ def get_equivalent_kpoints(self, index: int) -> list[int]: Returns: list[int]: Equivalent indices. - TODO: now it uses the label, we might want to use coordinates instead. - (in case there was a mislabel) + TODO: now it uses the label, we might want to use coordinates + instead in case there was a mislabel. """ # If the kpoint has no label it can't have a repetition # along the BandStructureSymmLine object @@ -1091,7 +1091,7 @@ def get_reconstructed_band_structure( list_bs: list[BandStructure] | list[BandStructureSymmLine], efermi: float | None = None, ) -> BandStructure | BandStructureSymmLine: - """Merge band structures to one single BandStructure(SymmLine) object. + """Merge multiple BandStructure(SymmLine) objects to a single one. This is typically useful when you split non self-consistent band structure runs to several independent jobs and want to merge the results. @@ -1104,7 +1104,7 @@ def get_reconstructed_band_structure( Returns: A BandStructure or BandStructureSymmLine object (depending on - the type of the list_bs objects). + the type of the objects in list_bs). """ if efermi is None: efermi = sum(b.efermi for b in list_bs) / len(list_bs) @@ -1112,9 +1112,9 @@ def get_reconstructed_band_structure( rec_lattice = list_bs[0].lattice_rec nb_bands = min(list_bs[i].nb_bands for i in range(len(list_bs))) - kpoints = np.concatenate([[k.frac_coords for k in bs.kpoints] for bs in list_bs]) + kpoints = np.concatenate([[kpt.frac_coords for kpt in bs.kpoints] for bs in list_bs]) dicts = [bs.labels_dict for bs in list_bs] - labels_dict = {k: v.frac_coords for d in dicts for k, v in d.items()} + labels_dict = {key: val.frac_coords for dct in dicts for key, val in dct.items()} eigenvals = {Spin.up: np.concatenate([bs.bands[Spin.up][:nb_bands] for bs in list_bs], axis=1)} if list_bs[0].is_spin_polarized: diff --git a/pymatgen/io/vasp/outputs.py b/pymatgen/io/vasp/outputs.py index 7eccca20661..ed9a6fe7aee 100644 --- a/pymatgen/io/vasp/outputs.py +++ b/pymatgen/io/vasp/outputs.py @@ -1034,7 +1034,7 @@ def get_band_structure( eigenvals, lattice_new, e_fermi, - labels_dict, + labels_dict, # type: ignore[arg-type] structure=self.final_structure, projections=p_eig_vals, ) From 8e252388e718415140142a69605896342a5e16c3 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 17:20:22 +0800 Subject: [PATCH 28/39] replace unnecessary single-item list extend with append --- pymatgen/io/lobster/outputs.py | 108 +++++++++++++++++---------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 0d855bc60ef..3a1b00a39cd 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -427,7 +427,13 @@ def __init__( else: n_bonds = len(data_without_orbitals) - labels, atoms1, atoms2, lens, translations, nums, icohps = [], [], [], [], [], [], [] + labels: list[str] = [] + atoms1: list[str] = [] + atoms2: list[str] = [] + lens: list[float] = [] + translations: list[tuple[int, int, int]] = [] + nums: list[int] = [] + icohps: list[dict[Spin, float]] = [] for bond in range(n_bonds): line_parts = data_without_orbitals[bond].split() @@ -437,7 +443,7 @@ def __init__( atom2 = str(line_parts[2]) length = float(line_parts[3]) - icohp = {} + icohp: dict[Spin, float] = {} if version == "2.2.1": icohp[Spin.up] = float(line_parts[4]) num = int(line_parts[5]) @@ -453,13 +459,13 @@ def __init__( if self.is_spin_polarized: icohp[Spin.down] = float(data_without_orbitals[bond + n_bonds + 1].split()[7]) - labels += [label] - atoms1 += [atom1] - atoms2 += [atom2] - lens += [length] - translations += [translation] - nums += [num] - icohps += [icohp] + labels.append(label) + atoms1.append(atom1) + atoms2.append(atom2) + lens.append(length) + translations.append(translation) + nums.append(num) + icohps.append(icohp) list_orb_icohp: list[dict] | None = None if self.orbitalwise: @@ -479,7 +485,7 @@ def __init__( icohp[Spin.down] = float(data_orbitals[n_orbs + i_data_orb].split()[7]) if len(list_orb_icohp) < int(label): - list_orb_icohp += [{orb_label: {"icohp": icohp, "orbitals": orbitals}}] + list_orb_icohp.append({orb_label: {"icohp": icohp, "orbitals": orbitals}}) else: list_orb_icohp[int(label) - 1][orb_label] = {"icohp": icohp, "orbitals": orbitals} @@ -561,7 +567,7 @@ def __init__(self, filename: str | None = "NcICOBILIST.lobster"): # LOBSTER < 4 data_without_orbitals = [] for line in data: if "_" not in str(line.split()[3:]) and "s]" not in str(line.split()[3:]): - data_without_orbitals += [line] + data_without_orbitals.append(line) else: data_without_orbitals = data @@ -592,11 +598,11 @@ def __init__(self, filename: str | None = "NcICOBILIST.lobster"): # LOBSTER < 4 if self.is_spin_polarized: ncicobi[Spin.down] = float(data_without_orbitals[bond + n_bonds + 1].split()[2]) - self.list_labels += [label] - self.list_n_atoms += [n_atoms] - self.list_ncicobi += [ncicobi] - self.list_interaction_type += [interaction_type] - self.list_num += [num] + self.list_labels.append(label) + self.list_n_atoms.append(n_atoms) + self.list_ncicobi.append(ncicobi) + self.list_interaction_type.append(interaction_type) + self.list_num.append(num) # TODO: add functions to get orbital resolved NcICOBIs @@ -677,7 +683,7 @@ def _parse_doscar(self): for nd in range(1, ndos): line = file.readline().split() cdos[nd] = np.array(line) - dos += [cdos] + dos.append(cdos) doshere = np.array(dos[0]) if len(doshere[0, :]) == 5: self._is_spin_polarized = True @@ -699,7 +705,7 @@ def _parse_doscar(self): for orb_num, j in enumerate(range(1, ncol)): orb = orbitals[atom + 1][orb_num] pdos[orb][spin] = data[:, j] - pdoss += [pdos] + pdoss.append(pdos) else: tdensities[Spin.up] = doshere[:, 1] tdensities[Spin.down] = doshere[:, 2] @@ -717,7 +723,7 @@ def _parse_doscar(self): pdos[orb][spin] = data[:, j] if j % 2 == 0: orb_num += 1 - pdoss += [pdos] + pdoss.append(pdos) self._efermi = efermi self._pdos = pdoss @@ -814,8 +820,8 @@ def __init__( line = data[atom].split() self.atomlist += [line[1] + line[0]] self.types += [line[1]] - self.mulliken += [float(line[2])] - self.loewdin += [float(line[3])] + self.mulliken.append(float(line[2])) + self.loewdin.append(float(line[3])) def get_structure_with_charges(self, structure_filename): """Get a Structure with Mulliken and Loewdin charges as site properties @@ -1087,9 +1093,9 @@ def _get_spillings(data, number_of_spins): splitrow = row.split() if len(splitrow) > 2 and splitrow[2] == "spilling:": if splitrow[1] == "charge": - charge_spilling += [np.float64(splitrow[3].replace("%", "")) / 100.0] + charge_spilling.append(np.float64(splitrow[3].replace("%", "")) / 100.0) if splitrow[1] == "total": - total_spilling += [np.float64(splitrow[3].replace("%", "")) / 100.0] + total_spilling.append(np.float64(splitrow[3].replace("%", "")) / 100.0) if len(charge_spilling) == number_of_spins and len(total_spilling) == number_of_spins: break @@ -1156,7 +1162,7 @@ def _get_warning_orthonormalization(data): for row in data: splitrow = row.split() if "orthonormalized" in splitrow: - orthowarning += [" ".join(splitrow[1:])] + orthowarning.append(" ".join(splitrow[1:])) return orthowarning @staticmethod @@ -1165,7 +1171,7 @@ def _get_all_warning_lines(data): for row in data: splitrow = row.split() if len(splitrow) > 0 and splitrow[0] == "WARNING:": - ws += [" ".join(splitrow[1:])] + ws.append(" ".join(splitrow[1:])) return ws @staticmethod @@ -1174,7 +1180,7 @@ def _get_all_info_lines(data): for row in data: splitrow = row.split() if len(splitrow) > 0 and splitrow[0] == "INFO:": - infos += [" ".join(splitrow[1:])] + infos.append(" ".join(splitrow[1:])) return infos @@ -1257,7 +1263,7 @@ def __init__( filenames = "." for name in os.listdir(filenames): if fnmatch.fnmatch(name, "FATBAND_*.lobster"): - filenames_new += [os.path.join(filenames, name)] + filenames_new.append(os.path.join(filenames, name)) filenames = filenames_new if len(filenames) == 0: raise ValueError("No FATBAND files in folder or given") @@ -1310,7 +1316,7 @@ def __init__( linenumbers = [] for iline, line in enumerate(contents[1 : self.nbands * 2 + 4]): if line.split()[0] == "#": - linenumbers += [iline] + linenumbers.append(iline) if ifilename == 0: self.is_spinpolarized = len(linenumbers) == 2 @@ -1360,7 +1366,7 @@ def __init__( ] ) if ifilename == 0: - kpoints_array += [KPOINT] + kpoints_array.append(KPOINT) linenumber = 0 iband = 0 @@ -1471,7 +1477,7 @@ def _read(self, contents: list, spin_numbers: list): kpoint_array = [] for kpointel in kpoint: if kpointel not in {"at", "k-point", ""}: - kpoint_array += [float(kpointel)] + kpoint_array.append(float(kpointel)) elif "maxDeviation" in line: if spin not in self.band_overlaps_dict: @@ -1483,16 +1489,16 @@ def _read(self, contents: list, spin_numbers: list): if "matrices" not in self.band_overlaps_dict[spin]: self.band_overlaps_dict[spin]["matrices"] = [] maxdev = line.split(" ")[2] - self.band_overlaps_dict[spin]["max_deviations"] += [float(maxdev)] + self.band_overlaps_dict[spin]["max_deviations"].append(float(maxdev)) self.band_overlaps_dict[spin]["k_points"] += [kpoint_array] - self.max_deviation += [float(maxdev)] + self.max_deviation.append(float(maxdev)) overlaps = [] else: rows = [] for el in line.split(" "): if el != "": - rows += [float(el)] + rows.append(float(el)) overlaps += [rows] if len(overlaps) == len(rows): self.band_overlaps_dict[spin]["matrices"] += [np.matrix(overlaps)] @@ -1603,7 +1609,7 @@ def __init__(self, filename: str = "GROSSPOP.lobster", list_dict_grosspop: list[ small_dict["Mulliken GP"][cleanline[0]] = float(cleanline[1]) small_dict["Loewdin GP"][cleanline[0]] = float(cleanline[2]) if "total" in cleanline[0]: - self.list_dict_grosspop += [small_dict] + self.list_dict_grosspop.append(small_dict) def get_structure_with_total_grosspop(self, structure_filename: str) -> Structure: """Get a Structure with Mulliken and Loewdin total grosspopulations as site properties @@ -1664,9 +1670,9 @@ def _parse_file(filename): splitline = line.split() if len(splitline) >= 6: points += [[float(splitline[0]), float(splitline[1]), float(splitline[2])]] - distance += [float(splitline[3])] - real += [float(splitline[4])] - imaginary += [float(splitline[5])] + distance.append(float(splitline[3])) + real.append(float(splitline[4])) + imaginary.append(float(splitline[5])) if len(real) != grid[0] * grid[1] * grid[2] or len(imaginary) != grid[0] * grid[1] * grid[2]: raise ValueError("Something went wrong while reading the file") @@ -1712,9 +1718,9 @@ def set_volumetric_data(self, grid, structure): "coordinates 0.0 0.0 0.0 coordinates 1.0 1.0 1.0 box bandlist 1 " ) - new_x += [x_here] - new_y += [y_here] - new_z += [z_here] + new_x.append(x_here) + new_y.append(y_here) + new_z.append(z_here) new_real += [self.real[runner]] new_imaginary += [self.imaginary[runner]] @@ -1909,8 +1915,8 @@ def __init__( line = data[atom].split() self.atomlist += [line[1] + str(line[0])] self.types += [line[1]] - self.sitepotentials_mulliken += [float(line[2])] - self.sitepotentials_loewdin += [float(line[3])] + self.sitepotentials_mulliken.append(float(line[2])) + self.sitepotentials_loewdin.append(float(line[3])) self.madelungenergies_mulliken = float(data[self.num_atoms + 1].split()[3]) self.madelungenergies_loewdin = float(data[self.num_atoms + 1].split()[4]) @@ -2098,21 +2104,21 @@ def _parse_matrix(file_data, pattern, e_fermi): for idx, line in enumerate(file_data): line = line.strip() if "Real parts" in line: - start_inxs_real += [idx + 1] + start_inxs_real.append(idx + 1) if idx == 1: # ignore the first occurrence as files start with real matrices pass else: - end_inxs_imag += [idx - 1] + end_inxs_imag.append(idx - 10) matches = re.search(pattern, file_data[idx - 1]) if matches and len(matches.groups()) == 2: k_point = matches.group(2) complex_matrices[k_point] = {} if "Imag parts" in line: - end_inxs_real += [idx - 1] - start_inxs_imag += [idx + 1] + end_inxs_real.append(idx - 1) + start_inxs_imag.append(idx + 1) # explicitly add the last line as files end with imaginary matrix if idx == len(file_data) - 1: - end_inxs_imag += [len(file_data)] + end_inxs_imag.append(len(file_data)) # extract matrix data and store diagonal elements matrix_real = [] @@ -2133,13 +2139,13 @@ def _parse_matrix(file_data, pattern, e_fermi): matches = re.search(pattern, file_data[start_inx_real - 2]) if matches and len(matches.groups()) == 2: - spin = Spin.up if matches.group(1) == "1" else Spin.down - k_point = matches.group(2) + spin = Spin.up if matches[1] == "1" else Spin.down + k_point = matches[2] complex_matrices[k_point].update({spin: comp_matrix}) elif matches and len(matches.groups()) == 1: - k_point = matches.group(1) + k_point = matches[1] complex_matrices.update({k_point: comp_matrix}) - matrix_diagonal_values += [comp_matrix.real.diagonal() - e_fermi] + matrix_diagonal_values.append(comp_matrix.real.diagonal() - e_fermi) # extract elements basis functions as list elements_basis_functions = [ From 00b7134352548d2fdb4852fa7aaa32096d4276ab Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 17:22:34 +0800 Subject: [PATCH 29/39] fix typo --- pymatgen/io/lobster/outputs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 3a1b00a39cd..003c43e8554 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -2108,7 +2108,7 @@ def _parse_matrix(file_data, pattern, e_fermi): if idx == 1: # ignore the first occurrence as files start with real matrices pass else: - end_inxs_imag.append(idx - 10) + end_inxs_imag.append(idx - 1) matches = re.search(pattern, file_data[idx - 1]) if matches and len(matches.groups()) == 2: k_point = matches.group(2) From 952e2060755a53455f38311334fa41fac6e3b2b2 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 17:32:09 +0800 Subject: [PATCH 30/39] relocate magic method --- pymatgen/electronic_structure/dos.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pymatgen/electronic_structure/dos.py b/pymatgen/electronic_structure/dos.py index cdb3074838d..68f62faf701 100644 --- a/pymatgen/electronic_structure/dos.py +++ b/pymatgen/electronic_structure/dos.py @@ -52,6 +52,18 @@ def __init__(self, energies: ArrayLike, densities: ArrayLike, efermi: float) -> super().__init__(energies, densities, efermi) self.efermi = efermi + def __str__(self) -> str: + """Get a string which can be easily plotted (using gnuplot).""" + if Spin.down in self.densities: + str_arr = [f"#{'Energy':30s} {'DensityUp':30s} {'DensityDown':30s}"] + for idx, energy in enumerate(self.energies): + str_arr.append(f"{energy:.5f} {self.densities[Spin.up][idx]:.5f} {self.densities[Spin.down][idx]:.5f}") + else: + str_arr = [f"#{'Energy':30s} {'DensityUp':30s}"] + for idx, energy in enumerate(self.energies): + str_arr.append(f"{energy:.5f} {self.densities[Spin.up][idx]:.5f}") + return "\n".join(str_arr) + def get_interpolated_gap(self, tol: float = 0.001, abs_tol: bool = False, spin: Spin | None = None): """Expects a DOS object and finds the gap. @@ -148,18 +160,6 @@ def get_gap(self, tol: float = 0.001, abs_tol: bool = False, spin: Spin | None = cbm, vbm = self.get_cbm_vbm(tol, abs_tol, spin) return max(cbm - vbm, 0.0) - def __str__(self) -> str: - """Get a string which can be easily plotted (using gnuplot).""" - if Spin.down in self.densities: - str_arr = [f"#{'Energy':30s} {'DensityUp':30s} {'DensityDown':30s}"] - for i, energy in enumerate(self.energies): - str_arr.append(f"{energy:.5f} {self.densities[Spin.up][i]:.5f} {self.densities[Spin.down][i]:.5f}") - else: - str_arr = [f"#{'Energy':30s} {'DensityUp':30s}"] - for i, energy in enumerate(self.energies): - str_arr.append(f"{energy:.5f} {self.densities[Spin.up][i]:.5f}") - return "\n".join(str_arr) - class Dos(MSONable): """Basic DOS object. All other DOS objects are extended versions of this From 4ee908d745f5d39f9b7a856ef9afd26af0ccdba4 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 18:05:33 +0800 Subject: [PATCH 31/39] clarify type of `list_icohp` --- pymatgen/electronic_structure/cohp.py | 14 ++++++++------ pymatgen/io/lobster/outputs.py | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 72f15d5a5d8..f7061a1e951 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -939,7 +939,7 @@ def __init__( length: float, translation: Vector3D, num: int, - icohp: dict[Spin, IcohpValue], + icohp: dict[Spin, float], are_coops: bool = False, are_cobis: bool = False, orbitals: dict[str, dict[Literal["icohp", "orbitals"], Any]] | None = None, @@ -952,7 +952,7 @@ def __init__( length (float): Bond length. translation (Vector3D): cell translation vector, e.g. (0, 0, 0). num (int): The number of equivalent bonds. - icohp (dict): {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down} + icohp (dict[Spin, float]): {Spin.up: ICOHP_up, Spin.down: ICOHP_down} are_coops (bool): Whether these are COOPs. are_cobis (bool): Whether these are COBIs. orbitals (dict): {[str(Orbital1)-str(Orbital2)]: { @@ -1070,11 +1070,11 @@ def icohpvalue_orbital( return self._orbitals[orbitals]["icohp"][spin] @property - def icohp(self) -> dict[Spin, IcohpValue]: + def icohp(self) -> dict[Spin, float]: """Dict with ICOHPs for spin up and spin down. Returns: - dict: {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. + dict[Spin, float]: {Spin.up: ICOHP_up, Spin.down: ICOHP_down}. """ return self._icohp @@ -1120,7 +1120,7 @@ def __init__( list_length: list[float], list_translation: list[Vector3D], list_num: list[int], - list_icohp: list[dict[Spin, IcohpValue]], + list_icohp: list[dict[Spin, float]], is_spin_polarized: bool, list_orb_icohp: list[dict[str, dict[Literal["icohp", "orbitals"], Any]]] | None = None, are_coops: bool = False, @@ -1134,7 +1134,7 @@ def __init__( list_length (list[float]): Bond lengths in Angstrom. list_translation (list[Vector3D]): Cell translation vectors. list_num (list[int]): Numbers of equivalent bonds, usually 1 starting from LOBSTER 3.0.0. - list_icohp (list[dict]): Dicts as {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}. + list_icohp (list[dict]): Dicts as {Spin.up: ICOHP_up, Spin.down: ICOHP_down}. is_spin_polarized (bool): Whether the calculation is spin polarized. list_orb_icohp (list[dict]): Dicts as {[str(Orbital1)-str(Orbital2)]: { "icohp": {Spin.up: IcohpValue for spin.up, Spin.down: IcohpValue for spin.down}, @@ -1157,6 +1157,8 @@ def __init__( self._list_icohp = list_icohp self._list_orb_icohp = list_orb_icohp + # TODO: DanielYang: self._icohplist name is misleading + # (not list), and confuses with self._list_icohp self._icohplist: dict[str, IcohpValue] = {} for idx, label in enumerate(list_labels): self._icohplist[label] = IcohpValue( diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 003c43e8554..6931e44060c 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -498,7 +498,7 @@ def __init__( list_length=lens, list_translation=translations, # type: ignore[arg-type] list_num=nums, - list_icohp=icohps, # DEBUG: DanielYang: inconsistent type + list_icohp=icohps, is_spin_polarized=self.is_spin_polarized, list_orb_icohp=list_orb_icohp, ) From 3aef21135aa5faa45da8227dc7e099d11ec93ad3 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Thu, 13 Jun 2024 18:13:16 +0800 Subject: [PATCH 32/39] remove unused type alias --- pymatgen/electronic_structure/cohp.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index f7061a1e951..94069a55c22 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -39,9 +39,6 @@ from pymatgen.util.typing import PathLike, SpinLike, Vector3D - # TODO: use more specific type - CohpOrbital = str | int | Orbital - __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" __version__ = "0.2" From 61379c1d859b3d9b70b7d61a5498721673acb436 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Fri, 14 Jun 2024 10:29:59 +0800 Subject: [PATCH 33/39] revert undesired rename --- pymatgen/io/lobster/outputs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 6931e44060c..967b6ce7ebb 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -410,11 +410,11 @@ def __init__( if self.orbitalwise: data_without_orbitals = [] data_orbitals = [] - for line_parts in data: - if "_" not in line_parts.split()[1]: - data_without_orbitals += [line_parts] + for line in data: + if "_" not in line.split()[1]: + data_without_orbitals.append(line) else: - data_orbitals += [line_parts] + data_orbitals.append(line) else: data_without_orbitals = data From 92e7df85374955317b75976abf9d6bafb4bb5e51 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Fri, 14 Jun 2024 10:46:42 +0800 Subject: [PATCH 34/39] replace more single item extend with append --- pymatgen/io/lobster/outputs.py | 49 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 967b6ce7ebb..48eb950467b 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -816,12 +816,12 @@ def __init__( raise RuntimeError("CHARGES file contains no data.") self.num_atoms = len(data) - for atom in range(self.num_atoms): - line = data[atom].split() - self.atomlist += [line[1] + line[0]] - self.types += [line[1]] - self.mulliken.append(float(line[2])) - self.loewdin.append(float(line[3])) + for atom_idx in range(self.num_atoms): + line_parts = data[atom_idx].split() + self.atomlist.append(line_parts[1] + line_parts[0]) + self.types.append(line_parts[1]) + self.mulliken.append(float(line_parts[2])) + self.loewdin.append(float(line_parts[3])) def get_structure_with_charges(self, structure_filename): """Get a Structure with Mulliken and Loewdin charges as site properties @@ -1111,8 +1111,8 @@ def _get_elements_basistype_basisfunctions(data): basisfunctions = [] for row in data: if begin and not end: - splitrow = row.split() - if splitrow[0] not in [ + row_parts = row.split() + if row_parts[0] not in { "INFO:", "WARNING:", "setting", @@ -1121,11 +1121,11 @@ def _get_elements_basistype_basisfunctions(data): "saving", "spillings", "writing", - ]: - elements += [splitrow[0]] - basistype += [splitrow[1].replace("(", "").replace(")", "")] + }: + elements.append(row_parts[0]) + basistype.append(row_parts[1].replace("(", "").replace(")", "")) # last sign is a '' - basisfunctions += [splitrow[2:]] + basisfunctions += [row_parts[2:]] else: end = True if "setting up local basis functions..." in row: @@ -1271,17 +1271,17 @@ def __init__( with zopen(name, mode="rt") as file: contents = file.read().split("\n") - atom_names += [os.path.split(name)[1].split("_")[1].capitalize()] + atom_names.append(os.path.split(name)[1].split("_")[1].capitalize()) parameters = contents[0].split() - atom_type += [re.split(r"[0-9]+", parameters[3])[0].capitalize()] - orbital_names += [parameters[4]] + atom_type.append(re.split(r"[0-9]+", parameters[3])[0].capitalize()) + orbital_names.append(parameters[4]) # get atomtype orbital dict atom_orbital_dict = {} # type: dict - for iatom, atom in enumerate(atom_names): + for idx, atom in enumerate(atom_names): if atom not in atom_orbital_dict: atom_orbital_dict[atom] = [] - atom_orbital_dict[atom] += [orbital_names[iatom]] + atom_orbital_dict[atom].append(orbital_names[idx]) # test if there are the same orbitals twice or if two different formats were used or if all necessary orbitals # are there for items in atom_orbital_dict.values(): @@ -1289,9 +1289,9 @@ def __init__( raise ValueError("The are two FATBAND files for the same atom and orbital. The program will stop.") split = [] for item in items: - split += [item.split("_")[0]] + split.append(item.split("_")[0]) for number in collections.Counter(split).values(): - if number not in (1, 3, 5, 7): + if number not in {1, 3, 5, 7}: raise ValueError( "Make sure all relevant orbitals were generated and that no duplicates (2p and 2p_x) are " "present" @@ -1621,7 +1621,6 @@ def get_structure_with_total_grosspop(self, structure_filename: str) -> Structur Structure Object with Mulliken and Loewdin total grosspopulations as site properties. """ struct = Structure.from_file(structure_filename) - # site_properties: dict[str, Any] = {} mullikengp = [] loewdingp = [] for grosspop in self.list_dict_grosspop: @@ -1912,11 +1911,11 @@ def __init__( data = data[5:-1] self.num_atoms = len(data) - 2 for atom in range(self.num_atoms): - line = data[atom].split() - self.atomlist += [line[1] + str(line[0])] - self.types += [line[1]] - self.sitepotentials_mulliken.append(float(line[2])) - self.sitepotentials_loewdin.append(float(line[3])) + line_parts = data[atom].split() + self.atomlist.append(line_parts[1] + str(line_parts[0])) + self.types.append(line_parts[1]) + self.sitepotentials_mulliken.append(float(line_parts[2])) + self.sitepotentials_loewdin.append(float(line_parts[3])) self.madelungenergies_mulliken = float(data[self.num_atoms + 1].split()[3]) self.madelungenergies_loewdin = float(data[self.num_atoms + 1].split()[4]) From c397e1cb5b710ddf2a6a58c7c53ae83106bc23aa Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Fri, 14 Jun 2024 10:56:40 +0800 Subject: [PATCH 35/39] simplify dict generation --- pymatgen/io/lobster/outputs.py | 77 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index 48eb950467b..81d65241a1b 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -1003,43 +1003,42 @@ def __init__(self, filename: str | None, **kwargs) -> None: raise ValueError("must provide either filename or kwargs to initialize Lobsterout") def get_doc(self) -> dict[str, Any]: - """Get the LobsterDict with all the information stored in lobsterout.""" - lobster_dict: dict[str, Any] = {} - # check if Lobster starts from a projection - lobster_dict["restart_from_projection"] = self.is_restart_from_projection - lobster_dict["lobster_version"] = self.lobster_version - lobster_dict["threads"] = self.number_of_threads - lobster_dict["dft_program"] = self.dft_program - - lobster_dict["charge_spilling"] = self.charge_spilling - lobster_dict["total_spilling"] = self.total_spilling - - lobster_dict["elements"] = self.elements - lobster_dict["basis_type"] = self.basis_type - lobster_dict["basis_functions"] = self.basis_functions - - lobster_dict["timing"] = self.timing - - lobster_dict["warning_lines"] = self.warning_lines - - lobster_dict["info_orthonormalization"] = self.info_orthonormalization - - lobster_dict["info_lines"] = self.info_lines - - lobster_dict["has_doscar"] = self.has_doscar - lobster_dict["has_doscar_lso"] = self.has_doscar_lso - lobster_dict["has_cohpcar"] = self.has_cohpcar - lobster_dict["has_coopcar"] = self.has_coopcar - lobster_dict["has_cobicar"] = self.has_cobicar - lobster_dict["has_charge"] = self.has_charge - lobster_dict["has_madelung"] = self.has_madelung - lobster_dict["has_projection"] = self.has_projection - lobster_dict["has_bandoverlaps"] = self.has_bandoverlaps - lobster_dict["has_fatbands"] = self.has_fatbands - lobster_dict["has_grosspopulation"] = self.has_grosspopulation - lobster_dict["has_density_of_energies"] = self.has_density_of_energies - - return lobster_dict + """Get a dict with all the information in lobsterout.""" + return { + # Check if LOBSTER starts from a projection + "restart_from_projection": self.is_restart_from_projection, + "lobster_version": self.lobster_version, + "threads": self.number_of_threads, + "dft_program": self.dft_program, + # + "charge_spilling": self.charge_spilling, + "total_spilling": self.total_spilling, + # + "elements": self.elements, + "basis_type": self.basis_type, + "basis_functions": self.basis_functions, + # + "timing": self.timing, + # + "warning_lines": self.warning_lines, + # + "info_orthonormalization": self.info_orthonormalization, + # + "info_lines": self.info_lines, + # + "has_doscar": self.has_doscar, + "has_doscar_lso": self.has_doscar_lso, + "has_cohpcar": self.has_cohpcar, + "has_coopcar": self.has_coopcar, + "has_cobicar": self.has_cobicar, + "has_charge": self.has_charge, + "has_madelung": self.has_madelung, + "has_projection": self.has_projection, + "has_bandoverlaps": self.has_bandoverlaps, + "has_fatbands": self.has_fatbands, + "has_grosspopulation": self.has_grosspopulation, + "has_density_of_energies": self.has_density_of_energies, + } def as_dict(self): """MSONable dict""" @@ -1984,7 +1983,7 @@ def get_orb_from_str(orbs): list of tw Orbital objects """ # TODO: also useful for plotting of DOS - orb_labs = [ + orb_labs = ( "s", "p_y", "p_z", @@ -2001,7 +2000,7 @@ def get_orb_from_str(orbs): "f_xz^2", "f_z(x^2-y^2)", "f_x(x^2-3y^2)", - ] + ) orbitals = [(int(orb[0]), Orbital(orb_labs.index(orb[1:]))) for orb in orbs] orb_label = "" From 8cfaa33e1314dd633a07a39ed19a20498efbe156 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Fri, 14 Jun 2024 17:20:42 +0800 Subject: [PATCH 36/39] fix downstream lobsterpy error --- pymatgen/electronic_structure/cohp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index 94069a55c22..acf4f8f45b5 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -201,14 +201,16 @@ def has_antibnd_states_below_efermi( dict_to_return = {} if spin is None: for sp, cohp_vals in populations.items(): - dict_to_return[sp] = (max(cohp_vals[:n_energies_below_efermi])) > limit + # NOTE: Casting to bool is necessary, otherwise ended up + # getting "bool_" instead of "bool" from NumPy + dict_to_return[sp] = bool((max(cohp_vals[:n_energies_below_efermi])) > limit) else: if isinstance(spin, int): spin = Spin(spin) elif isinstance(spin, str): spin = Spin({"up": 1, "down": -1}[spin.lower()]) - dict_to_return[spin] = (max(populations[spin][:n_energies_below_efermi])) > limit + dict_to_return[spin] = bool((max(populations[spin][:n_energies_below_efermi])) > limit) return dict_to_return From 5eefb5326530935fb49a60687b3d2ddd2500dc88 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sun, 16 Jun 2024 17:05:15 +0800 Subject: [PATCH 37/39] tweak module docstring --- pymatgen/electronic_structure/boltztrap2.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pymatgen/electronic_structure/boltztrap2.py b/pymatgen/electronic_structure/boltztrap2.py index 50d09d116ac..13d7891629e 100644 --- a/pymatgen/electronic_structure/boltztrap2.py +++ b/pymatgen/electronic_structure/boltztrap2.py @@ -1,24 +1,23 @@ -"""This module provides a pymatgen interface to BoltzTraP2. +"""This module provides an interface to BoltzTraP2. Some of the code is written following the examples provided in BoltzTraP2. BoltzTraP2 is a Python software interpolating band structures and -computing materials properties from dft band structure using Boltzmann +computing materials properties from DFT band structure using Boltzmann semi-classical transport theory, developed by Georg Madsen, Jesús Carrete, Matthieu J. Verstraete. https://gitlab.com/sousaw/BoltzTraP2 https://www.sciencedirect.com/science/article/pii/S0010465518301632 -References are: - +References: Georg K.H.Madsen, Jesús Carrete, Matthieu J.Verstraete BoltzTraP2, a program for interpolating band structures and calculating semi-classical transport coefficients - Computer Physics Communications 231, 140-145, 2018 + Computer Physics Communications 231, 140-145, 2018. Madsen, G. K. H., and Singh, D. J. (2006). BoltzTraP. A code for calculating band-structure dependent quantities. - Computer Physics Communications, 175, 67-71 + Computer Physics Communications, 175, 67-71. Todo: - Read first derivative of the eigenvalues from vasprun.xml (mommat) From eb36650422a3053518fdf8078717062e1d10e64f Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel)" Date: Sat, 22 Jun 2024 10:05:09 +0800 Subject: [PATCH 38/39] need confirm: allow efermi to be None --- pymatgen/io/lobster/outputs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pymatgen/io/lobster/outputs.py b/pymatgen/io/lobster/outputs.py index dd52f024a41..3c7950a9d90 100644 --- a/pymatgen/io/lobster/outputs.py +++ b/pymatgen/io/lobster/outputs.py @@ -1225,10 +1225,10 @@ def __init__( "FATBAND_*" files will be read kpoints_file (PathLike): KPOINTS file for bandstructure calculation, typically "KPOINTS". vasprun_file (PathLike): Corresponding vasprun file. - Instead, the Fermi energy from the DFT run can be provided. Then, + Instead, the Fermi level from the DFT run can be provided. Then, this value should be set to None. structure (Structure): Structure object. - efermi (float): fermi energy in eV + efermi (float): Fermi level in eV. """ warnings.warn("Make sure all relevant FATBAND files were generated and read in!") warnings.warn("Use Lobster 3.2.0 or newer for fatband calculations!") @@ -1413,7 +1413,7 @@ def get_bandstructure(self) -> LobsterBandStructureSymmLine: kpoints=self.kpoints_array, eigenvals=self.eigenvals, lattice=self.lattice, - efermi=self.efermi, + efermi=self.efermi, # type: ignore[arg-type] labels_dict=self.label_dict, structure=self.structure, projections=self.p_eigenvals, From 220a3981f70ea451649f79a4af2a52c7432d1a1a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 02:04:53 +0000 Subject: [PATCH 39/39] pre-commit auto-fixes --- dev_scripts/chemenv/explicit_permutations.py | 1 - .../explicit_permutations_plane_algorithm.py | 1 - .../chemenv/get_plane_permutations_optimized.py | 1 - .../multi_weights_strategy_parameters.py | 1 - dev_scripts/chemenv/test_algos.py | 1 - dev_scripts/chemenv/view_environment.py | 1 - dev_scripts/potcar_scrambler.py | 1 - dev_scripts/update_pt_data.py | 3 +-- src/pymatgen/alchemy/filters.py | 4 +--- src/pymatgen/alchemy/materials.py | 4 +--- src/pymatgen/alchemy/transmuters.py | 3 +-- src/pymatgen/analysis/adsorption.py | 6 ++---- src/pymatgen/analysis/bond_dissociation.py | 1 - src/pymatgen/analysis/bond_valence.py | 1 - .../chemenv/connectivity/connected_components.py | 1 - .../chemenv/connectivity/connectivity_finder.py | 1 - .../connectivity/structure_connectivity.py | 1 - .../chemenv_strategies.py | 3 +-- .../coordination_geometry_finder.py | 1 - .../structure_environments.py | 1 - .../chemenv/coordination_environments/voronoi.py | 3 +-- .../chemenv/utils/coordination_geometry_utils.py | 3 +-- src/pymatgen/analysis/chemenv/utils/func_utils.py | 1 - .../analysis/chemenv/utils/scripts_utils.py | 1 - src/pymatgen/analysis/chempot_diagram.py | 3 +-- src/pymatgen/analysis/cost.py | 1 - src/pymatgen/analysis/diffraction/core.py | 1 - src/pymatgen/analysis/diffraction/neutron.py | 1 - src/pymatgen/analysis/diffraction/tem.py | 2 -- src/pymatgen/analysis/diffraction/xrd.py | 1 - src/pymatgen/analysis/dimensionality.py | 1 - src/pymatgen/analysis/elasticity/elastic.py | 10 ++++------ src/pymatgen/analysis/elasticity/strain.py | 4 +--- src/pymatgen/analysis/elasticity/stress.py | 1 - src/pymatgen/analysis/energy_models.py | 4 +--- src/pymatgen/analysis/eos.py | 3 +-- src/pymatgen/analysis/ewald.py | 5 ++--- .../analysis/ferroelectricity/polarization.py | 6 ++---- src/pymatgen/analysis/fragmenter.py | 1 - src/pymatgen/analysis/graphs.py | 8 +++----- src/pymatgen/analysis/hhi.py | 1 - src/pymatgen/analysis/interface_reactions.py | 1 - .../analysis/interfaces/coherent_interfaces.py | 3 +-- .../analysis/interfaces/substrate_analyzer.py | 3 +-- src/pymatgen/analysis/interfaces/zsl.py | 1 - src/pymatgen/analysis/local_env.py | 8 +++----- src/pymatgen/analysis/magnetism/analyzer.py | 7 +++---- src/pymatgen/analysis/magnetism/heisenberg.py | 1 - src/pymatgen/analysis/magnetism/jahnteller.py | 1 - src/pymatgen/analysis/molecule_matcher.py | 4 +--- .../analysis/molecule_structure_comparator.py | 1 - src/pymatgen/analysis/nmr.py | 1 - src/pymatgen/analysis/phase_diagram.py | 9 ++++----- src/pymatgen/analysis/piezo.py | 1 - src/pymatgen/analysis/piezo_sensitivity.py | 1 - src/pymatgen/analysis/pourbaix_diagram.py | 5 ++--- src/pymatgen/analysis/prototypes.py | 1 - src/pymatgen/analysis/quasiharmonic.py | 7 +++---- src/pymatgen/analysis/quasirrho.py | 4 +--- src/pymatgen/analysis/reaction_calculator.py | 6 ++---- src/pymatgen/analysis/solar/slme.py | 3 +-- src/pymatgen/analysis/structure_analyzer.py | 3 +-- src/pymatgen/analysis/structure_matcher.py | 4 +--- .../structure_prediction/dopant_predictor.py | 1 - .../substitution_probability.py | 1 - .../analysis/structure_prediction/substitutor.py | 1 - .../structure_prediction/volume_predictor.py | 1 - src/pymatgen/analysis/surface_analysis.py | 8 +++----- src/pymatgen/analysis/topological/spillage.py | 1 - src/pymatgen/analysis/transition_state.py | 3 +-- src/pymatgen/analysis/wulff.py | 3 +-- src/pymatgen/analysis/xas/spectrum.py | 3 +-- src/pymatgen/analysis/xps.py | 4 +--- src/pymatgen/apps/battery/analyzer.py | 1 - src/pymatgen/apps/battery/battery_abc.py | 3 +-- src/pymatgen/apps/battery/conversion_battery.py | 6 ++---- src/pymatgen/apps/battery/insertion_battery.py | 3 +-- src/pymatgen/apps/battery/plotter.py | 1 - src/pymatgen/apps/borg/hive.py | 1 - src/pymatgen/cli/feff_plot_cross_section.py | 1 - src/pymatgen/cli/pmg.py | 3 +-- src/pymatgen/cli/pmg_analyze.py | 3 +-- src/pymatgen/cli/pmg_config.py | 3 +-- src/pymatgen/cli/pmg_plot.py | 1 - src/pymatgen/cli/pmg_structure.py | 3 +-- src/pymatgen/command_line/bader_caller.py | 4 +--- src/pymatgen/command_line/chargemol_caller.py | 1 - src/pymatgen/command_line/critic2_caller.py | 6 ++---- src/pymatgen/command_line/enumlib_caller.py | 1 - src/pymatgen/command_line/gulp_caller.py | 1 - src/pymatgen/command_line/mcsqs_caller.py | 1 - src/pymatgen/command_line/vampire_caller.py | 1 - src/pymatgen/core/__init__.py | 3 +-- src/pymatgen/core/composition.py | 4 +--- src/pymatgen/core/interface.py | 8 +++----- src/pymatgen/core/ion.py | 1 - src/pymatgen/core/lattice.py | 6 ++---- src/pymatgen/core/operations.py | 1 - src/pymatgen/core/periodic_table.py | 4 +--- src/pymatgen/core/sites.py | 4 +--- src/pymatgen/core/spectrum.py | 3 +-- src/pymatgen/core/structure.py | 15 ++++++--------- src/pymatgen/core/surface.py | 8 +++----- src/pymatgen/core/tensors.py | 6 ++---- src/pymatgen/core/trajectory.py | 4 +--- src/pymatgen/core/xcfunc.py | 1 - .../electronic_structure/bandstructure.py | 1 - src/pymatgen/electronic_structure/boltztrap.py | 10 ++++------ src/pymatgen/electronic_structure/boltztrap2.py | 3 +-- src/pymatgen/electronic_structure/cohp.py | 6 ++---- src/pymatgen/electronic_structure/core.py | 3 +-- src/pymatgen/electronic_structure/dos.py | 10 ++++------ src/pymatgen/electronic_structure/plotter.py | 2 -- src/pymatgen/entries/__init__.py | 1 - src/pymatgen/entries/compatibility.py | 5 ++--- src/pymatgen/entries/computed_entries.py | 8 +++----- src/pymatgen/entries/correction_calculator.py | 5 ++--- src/pymatgen/entries/entry_tools.py | 4 +--- src/pymatgen/entries/exp_entries.py | 1 - src/pymatgen/entries/mixing_scheme.py | 1 - src/pymatgen/ext/cod.py | 1 - src/pymatgen/ext/matproj.py | 4 +--- src/pymatgen/ext/matproj_legacy.py | 8 +++----- src/pymatgen/ext/optimade.py | 3 +-- src/pymatgen/io/abinit/abiobjects.py | 1 - src/pymatgen/io/abinit/abitimer.py | 1 - src/pymatgen/io/abinit/inputs.py | 1 - src/pymatgen/io/abinit/netcdf.py | 1 - src/pymatgen/io/abinit/pseudos.py | 6 ++---- src/pymatgen/io/adf.py | 1 - src/pymatgen/io/aims/inputs.py | 4 +--- src/pymatgen/io/aims/outputs.py | 4 +--- src/pymatgen/io/aims/parsers.py | 1 - src/pymatgen/io/aims/sets/base.py | 1 - src/pymatgen/io/ase.py | 4 +--- src/pymatgen/io/atat.py | 1 - src/pymatgen/io/babel.py | 4 +--- src/pymatgen/io/cif.py | 4 +--- src/pymatgen/io/common.py | 3 +-- src/pymatgen/io/core.py | 3 +-- src/pymatgen/io/cp2k/inputs.py | 4 +--- src/pymatgen/io/cp2k/outputs.py | 1 - src/pymatgen/io/cp2k/sets.py | 3 +-- src/pymatgen/io/cssr.py | 1 - src/pymatgen/io/exciting/inputs.py | 1 - src/pymatgen/io/feff/inputs.py | 3 +-- src/pymatgen/io/feff/outputs.py | 1 - src/pymatgen/io/feff/sets.py | 1 - src/pymatgen/io/fiesta.py | 4 +--- src/pymatgen/io/gaussian.py | 3 +-- src/pymatgen/io/lammps/data.py | 6 ++---- src/pymatgen/io/lammps/generators.py | 1 - src/pymatgen/io/lammps/inputs.py | 4 +--- src/pymatgen/io/lammps/outputs.py | 1 - src/pymatgen/io/lammps/sets.py | 3 +-- src/pymatgen/io/lammps/utils.py | 1 - src/pymatgen/io/lmto.py | 1 - src/pymatgen/io/lobster/inputs.py | 4 +--- src/pymatgen/io/lobster/lobsterenv.py | 4 +--- src/pymatgen/io/lobster/outputs.py | 1 - src/pymatgen/io/nwchem.py | 1 - src/pymatgen/io/openff.py | 1 - src/pymatgen/io/packmol.py | 1 - src/pymatgen/io/phonopy.py | 3 +-- src/pymatgen/io/pwmat/inputs.py | 4 +--- src/pymatgen/io/pwmat/outputs.py | 1 - src/pymatgen/io/pwscf.py | 1 - src/pymatgen/io/qchem/inputs.py | 1 - src/pymatgen/io/qchem/outputs.py | 1 - src/pymatgen/io/qchem/sets.py | 1 - src/pymatgen/io/res.py | 4 +--- src/pymatgen/io/shengbte.py | 1 - src/pymatgen/io/template.py | 1 - src/pymatgen/io/vasp/inputs.py | 6 ++---- src/pymatgen/io/vasp/optics.py | 6 ++---- src/pymatgen/io/vasp/outputs.py | 4 +--- src/pymatgen/io/vasp/sets.py | 4 +--- src/pymatgen/io/xr.py | 1 - src/pymatgen/io/xtb/outputs.py | 1 - src/pymatgen/io/xyz.py | 1 - src/pymatgen/io/zeopp.py | 1 - src/pymatgen/phonon/bandstructure.py | 4 +--- src/pymatgen/phonon/dos.py | 3 +-- src/pymatgen/phonon/gruneisen.py | 3 +-- src/pymatgen/phonon/ir_spectra.py | 1 - src/pymatgen/phonon/plotter.py | 2 -- src/pymatgen/phonon/thermal_displacements.py | 1 - src/pymatgen/symmetry/analyzer.py | 1 - src/pymatgen/symmetry/bandstructure.py | 1 - src/pymatgen/symmetry/groups.py | 4 +--- src/pymatgen/symmetry/kpath.py | 1 - src/pymatgen/symmetry/maggroups.py | 4 +--- src/pymatgen/symmetry/settings.py | 5 ++--- src/pymatgen/symmetry/site_symmetries.py | 1 - src/pymatgen/symmetry/structure.py | 6 ++---- .../transformations/advanced_transformations.py | 1 - .../transformations/site_transformations.py | 1 - .../transformations/standard_transformations.py | 4 +--- src/pymatgen/util/coord.py | 2 -- src/pymatgen/util/plotting.py | 1 - src/pymatgen/util/provenance.py | 1 - src/pymatgen/util/testing/__init__.py | 1 - src/pymatgen/util/testing/aims.py | 1 - src/pymatgen/vis/plotters.py | 1 - src/pymatgen/vis/structure_chemview.py | 1 - src/pymatgen/vis/structure_vtk.py | 1 - tests/alchemy/test_filters.py | 1 - tests/alchemy/test_materials.py | 1 - .../connectivity/test_connected_components.py | 1 - .../test_chemenv_strategies.py | 3 +-- .../test_coordination_geometries.py | 3 +-- .../test_coordination_geometry_finder.py | 3 +-- .../coordination_environments/test_read_write.py | 3 +-- .../test_structure_environments.py | 3 +-- .../coordination_environments/test_voronoi.py | 1 - .../coordination_environments/test_weights.py | 3 +-- .../utils/test_coordination_geometry_utils.py | 3 +-- tests/analysis/chemenv/utils/test_func_utils.py | 3 +-- tests/analysis/chemenv/utils/test_graph_utils.py | 1 - tests/analysis/chemenv/utils/test_math_utils.py | 1 - tests/analysis/diffraction/test_neutron.py | 3 +-- tests/analysis/diffraction/test_tem.py | 3 +-- tests/analysis/diffraction/test_xrd.py | 3 +-- tests/analysis/elasticity/test_elastic.py | 5 ++--- tests/analysis/elasticity/test_strain.py | 1 - tests/analysis/elasticity/test_stress.py | 3 +-- .../ferroelectricity/test_polarization.py | 3 +-- .../interfaces/test_coherent_interface.py | 1 - .../interfaces/test_substrate_analyzer.py | 1 - tests/analysis/interfaces/test_zsl.py | 3 +-- tests/analysis/magnetism/test_analyzer.py | 3 +-- tests/analysis/magnetism/test_heisenberg.py | 1 - tests/analysis/magnetism/test_jahnteller.py | 3 +-- tests/analysis/solar/test_slme.py | 3 +-- .../structure_prediction/test_dopant_predictor.py | 3 +-- .../test_substitution_probability.py | 3 +-- .../structure_prediction/test_volume_predictor.py | 3 +-- tests/analysis/test_adsorption.py | 1 - tests/analysis/test_bond_dissociation.py | 1 - tests/analysis/test_bond_valence.py | 3 +-- tests/analysis/test_chempot_diagram.py | 3 +-- tests/analysis/test_cost.py | 3 +-- tests/analysis/test_dimensionality.py | 1 - tests/analysis/test_disorder.py | 3 +-- tests/analysis/test_energy_models.py | 3 +-- tests/analysis/test_eos.py | 3 +-- tests/analysis/test_ewald.py | 3 +-- tests/analysis/test_fragmenter.py | 1 - tests/analysis/test_functional_groups.py | 1 - tests/analysis/test_graphs.py | 3 +-- tests/analysis/test_hhi.py | 3 +-- tests/analysis/test_interface_reactions.py | 3 +-- tests/analysis/test_local_env.py | 3 +-- tests/analysis/test_molecule_matcher.py | 3 +-- tests/analysis/test_nmr.py | 3 +-- tests/analysis/test_phase_diagram.py | 3 +-- tests/analysis/test_piezo.py | 1 - tests/analysis/test_piezo_sensitivity.py | 3 +-- tests/analysis/test_pourbaix_diagram.py | 3 +-- .../analysis/test_quasi_harmonic_debye_approx.py | 1 - tests/analysis/test_quasirrho.py | 1 - tests/analysis/test_reaction_calculator.py | 3 +-- tests/analysis/test_structure_analyzer.py | 3 +-- tests/analysis/test_structure_matcher.py | 3 +-- tests/analysis/test_surface_analysis.py | 5 ++--- tests/analysis/test_transition_state.py | 1 - tests/analysis/test_wulff.py | 3 +-- tests/analysis/topological/test_spillage.py | 3 +-- tests/analysis/xas/test_spectrum.py | 3 +-- tests/apps/battery/test_analyzer.py | 3 +-- tests/apps/battery/test_conversion_battery.py | 3 +-- tests/apps/battery/test_insertion_battery.py | 3 +-- tests/apps/battery/test_plotter.py | 1 - tests/apps/borg/test_hive.py | 3 +-- tests/apps/borg/test_queen.py | 3 +-- tests/command_line/test_bader_caller.py | 3 +-- tests/command_line/test_critic2_caller.py | 3 +-- tests/command_line/test_enumlib_caller.py | 3 +-- tests/command_line/test_gulp_caller.py | 1 - tests/command_line/test_mcsqs_caller.py | 1 - tests/command_line/test_vampire_caller.py | 3 +-- tests/core/test_bonds.py | 3 +-- tests/core/test_composition.py | 3 +-- tests/core/test_interface.py | 3 +-- tests/core/test_ion.py | 1 - tests/core/test_lattice.py | 3 +-- tests/core/test_molecular_orbitals.py | 1 - tests/core/test_operations.py | 1 - tests/core/test_periodic_table.py | 3 +-- tests/core/test_sites.py | 3 +-- tests/core/test_spectrum.py | 5 ++--- tests/core/test_structure.py | 3 +-- tests/core/test_surface.py | 5 ++--- tests/core/test_tensors.py | 3 +-- tests/core/test_trajectory.py | 1 - tests/core/test_units.py | 3 +-- tests/core/test_xcfunc.py | 1 - tests/electronic_structure/test_bandstructure.py | 3 +-- tests/electronic_structure/test_boltztrap.py | 3 +-- tests/electronic_structure/test_boltztrap2.py | 3 +-- tests/electronic_structure/test_cohp.py | 3 +-- tests/electronic_structure/test_core.py | 1 - tests/electronic_structure/test_dos.py | 3 +-- tests/electronic_structure/test_plotter.py | 3 +-- tests/entries/test_compatibility.py | 5 ++--- tests/entries/test_computed_entries.py | 3 +-- tests/entries/test_correction_calculator.py | 1 - tests/entries/test_entry_tools.py | 1 - tests/entries/test_exp_entries.py | 3 +-- tests/entries/test_mixing_scheme.py | 1 - tests/ext/test_cod.py | 1 - tests/ext/test_matproj.py | 5 ++--- tests/ext/test_optimade.py | 1 - tests/io/abinit/test_abiobjects.py | 3 +-- tests/io/abinit/test_inputs.py | 1 - tests/io/abinit/test_netcdf.py | 1 - tests/io/abinit/test_pseudos.py | 3 +-- tests/io/aims/conftest.py | 1 - tests/io/aims/test_aims_inputs.py | 1 - tests/io/aims/test_aims_outputs.py | 1 - tests/io/aims/test_aims_parsers.py | 1 - tests/io/aims/test_sets/test_input_set.py | 1 - tests/io/cp2k/test_inputs.py | 3 +-- tests/io/cp2k/test_outputs.py | 3 +-- tests/io/cp2k/test_sets.py | 3 +-- tests/io/exciting/test_inputs.py | 1 - tests/io/feff/test_inputs.py | 3 +-- tests/io/feff/test_sets.py | 1 - tests/io/lammps/test_data.py | 5 ++--- tests/io/lammps/test_inputs.py | 1 - tests/io/lammps/test_outputs.py | 1 - tests/io/lobster/test_inputs.py | 3 +-- tests/io/lobster/test_lobsterenv.py | 3 +-- tests/io/pwmat/test_inputs.py | 1 - tests/io/qchem/test_inputs.py | 1 - tests/io/qchem/test_outputs.py | 3 +-- tests/io/qchem/test_sets.py | 1 - tests/io/qchem/test_utils.py | 1 - tests/io/test_adf.py | 3 +-- tests/io/test_ase.py | 1 - tests/io/test_atat.py | 3 +-- tests/io/test_babel.py | 3 +-- tests/io/test_cif.py | 3 +-- tests/io/test_core.py | 1 - tests/io/test_gaussian.py | 3 +-- tests/io/test_jarvis.py | 1 - tests/io/test_lmto.py | 1 - tests/io/test_nwchem.py | 3 +-- tests/io/test_openff.py | 1 - tests/io/test_packmol.py | 1 - tests/io/test_phonopy.py | 3 +-- tests/io/test_pwscf.py | 3 +-- tests/io/test_res.py | 3 +-- tests/io/test_shengbte.py | 1 - tests/io/test_template_input.py | 1 - tests/io/test_wannier90.py | 3 +-- tests/io/test_xcrysden.py | 1 - tests/io/test_xyz.py | 3 +-- tests/io/test_zeopp.py | 3 +-- tests/io/vasp/test_inputs.py | 3 +-- tests/io/vasp/test_optics.py | 1 - tests/io/vasp/test_outputs.py | 3 +-- tests/io/vasp/test_sets.py | 3 +-- tests/io/xtb/test_outputs.py | 3 +-- tests/optimization/test_linear_assignment.py | 3 +-- tests/optimization/test_neighbors.py | 1 - tests/phonon/test_bandstructure.py | 3 +-- tests/phonon/test_dos.py | 3 +-- tests/phonon/test_gruneisen.py | 3 +-- tests/phonon/test_ir_spectra.py | 1 - tests/phonon/test_plotter.py | 1 - tests/phonon/test_thermal_displacements.py | 3 +-- tests/symmetry/test_analyzer.py | 3 +-- tests/symmetry/test_groups.py | 3 +-- tests/symmetry/test_kpath_hin.py | 3 +-- tests/symmetry/test_kpath_lm.py | 3 +-- tests/symmetry/test_kpath_sc.py | 3 +-- tests/symmetry/test_kpaths.py | 1 - tests/symmetry/test_maggroups.py | 1 - tests/symmetry/test_settings.py | 1 - tests/symmetry/test_site_symmetries.py | 1 - tests/test_cli.py | 1 - .../test_advanced_transformations.py | 3 +-- .../transformations/test_site_transformations.py | 1 - .../test_standard_transformations.py | 3 +-- tests/util/test_coord.py | 3 +-- tests/util/test_num.py | 1 - tests/util/test_plotting.py | 1 - tests/util/test_provenance.py | 1 - tests/util/test_string.py | 1 - tests/util/test_typing.py | 1 - tests/vis/test_plotters.py | 1 - 392 files changed, 272 insertions(+), 726 deletions(-) diff --git a/dev_scripts/chemenv/explicit_permutations.py b/dev_scripts/chemenv/explicit_permutations.py index f3225fa5293..097e80b25e0 100644 --- a/dev_scripts/chemenv/explicit_permutations.py +++ b/dev_scripts/chemenv/explicit_permutations.py @@ -10,7 +10,6 @@ import os import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import ( AllCoordinationGeometries, ExplicitPermutationsAlgorithm, diff --git a/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py b/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py index bd71080c300..689ae58d86a 100644 --- a/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py +++ b/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py @@ -9,7 +9,6 @@ import json import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import AllCoordinationGeometries from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import ( AbstractGeometry, diff --git a/dev_scripts/chemenv/get_plane_permutations_optimized.py b/dev_scripts/chemenv/get_plane_permutations_optimized.py index 1244d13e487..127ca009f19 100644 --- a/dev_scripts/chemenv/get_plane_permutations_optimized.py +++ b/dev_scripts/chemenv/get_plane_permutations_optimized.py @@ -15,7 +15,6 @@ import numpy as np import tabulate - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import AllCoordinationGeometries from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import ( AbstractGeometry, diff --git a/dev_scripts/chemenv/strategies/multi_weights_strategy_parameters.py b/dev_scripts/chemenv/strategies/multi_weights_strategy_parameters.py index 495a8bb668e..70449d924f8 100644 --- a/dev_scripts/chemenv/strategies/multi_weights_strategy_parameters.py +++ b/dev_scripts/chemenv/strategies/multi_weights_strategy_parameters.py @@ -8,7 +8,6 @@ import matplotlib.pyplot as plt import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import ( AngleNbSetWeight, CNBiasNbSetWeight, diff --git a/dev_scripts/chemenv/test_algos.py b/dev_scripts/chemenv/test_algos.py index dbffdce27e5..17933a067a3 100644 --- a/dev_scripts/chemenv/test_algos.py +++ b/dev_scripts/chemenv/test_algos.py @@ -8,7 +8,6 @@ from random import shuffle import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import AllCoordinationGeometries from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import ( AbstractGeometry, diff --git a/dev_scripts/chemenv/view_environment.py b/dev_scripts/chemenv/view_environment.py index 2caa22e9f34..69aa6adc42e 100644 --- a/dev_scripts/chemenv/view_environment.py +++ b/dev_scripts/chemenv/view_environment.py @@ -3,7 +3,6 @@ from __future__ import annotations import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import ( SEPARATION_PLANE, AllCoordinationGeometries, diff --git a/dev_scripts/potcar_scrambler.py b/dev_scripts/potcar_scrambler.py index 0dd2b0190fa..c0793c713d9 100644 --- a/dev_scripts/potcar_scrambler.py +++ b/dev_scripts/potcar_scrambler.py @@ -9,7 +9,6 @@ import numpy as np from monty.os.path import zpath from monty.serialization import zopen - from pymatgen.core import SETTINGS from pymatgen.io.vasp import Potcar, PotcarSingle from pymatgen.io.vasp.sets import _load_yaml_config diff --git a/dev_scripts/update_pt_data.py b/dev_scripts/update_pt_data.py index 88f321ed712..ee7889c75ea 100644 --- a/dev_scripts/update_pt_data.py +++ b/dev_scripts/update_pt_data.py @@ -11,9 +11,8 @@ import requests from monty.dev import requires from monty.serialization import dumpfn, loadfn -from ruamel import yaml - from pymatgen.core import Element, get_el_sp +from ruamel import yaml try: from bs4 import BeautifulSoup diff --git a/src/pymatgen/alchemy/filters.py b/src/pymatgen/alchemy/filters.py index d133c0289e3..c16c05bf634 100644 --- a/src/pymatgen/alchemy/filters.py +++ b/src/pymatgen/alchemy/filters.py @@ -7,15 +7,13 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.analysis.structure_matcher import ElementComparator, StructureMatcher from pymatgen.core import get_el_sp from pymatgen.symmetry.analyzer import SpacegroupAnalyzer if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self class AbstractStructureFilter(MSONable, abc.ABC): diff --git a/src/pymatgen/alchemy/materials.py b/src/pymatgen/alchemy/materials.py index 2fa8b754721..701133215e3 100644 --- a/src/pymatgen/alchemy/materials.py +++ b/src/pymatgen/alchemy/materials.py @@ -12,7 +12,6 @@ from warnings import warn from monty.json import MSONable, jsanitize - from pymatgen.core.structure import Structure from pymatgen.io.cif import CifParser from pymatgen.io.vasp.inputs import Poscar @@ -24,9 +23,8 @@ from collections.abc import Sequence from typing import Any - from typing_extensions import Self - from pymatgen.alchemy.filters import AbstractStructureFilter + from typing_extensions import Self class TransformedStructure(MSONable): diff --git a/src/pymatgen/alchemy/transmuters.py b/src/pymatgen/alchemy/transmuters.py index 802716520fc..d51ea47c247 100644 --- a/src/pymatgen/alchemy/transmuters.py +++ b/src/pymatgen/alchemy/transmuters.py @@ -21,9 +21,8 @@ from collections.abc import Sequence from typing import Callable - from typing_extensions import Self - from pymatgen.alchemy.filters import AbstractStructureFilter + from typing_extensions import Self __author__ = "Shyue Ping Ong, Will Richards" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/src/pymatgen/analysis/adsorption.py b/src/pymatgen/analysis/adsorption.py index 5fe6b7a3742..88f9f98dc3e 100644 --- a/src/pymatgen/analysis/adsorption.py +++ b/src/pymatgen/analysis/adsorption.py @@ -12,8 +12,6 @@ from matplotlib import patches from matplotlib.path import Path from monty.serialization import loadfn -from scipy.spatial import Delaunay - from pymatgen import vis from pymatgen.analysis.local_env import VoronoiNN from pymatgen.analysis.structure_matcher import StructureMatcher @@ -22,13 +20,13 @@ from pymatgen.core.surface import generate_all_slabs from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.coord import in_coord_list_pbc +from scipy.spatial import Delaunay if TYPE_CHECKING: import matplotlib.pyplot as plt from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core.surface import Slab + from typing_extensions import Self __author__ = "Joseph Montoya" __copyright__ = "Copyright 2016, The Materials Project" diff --git a/src/pymatgen/analysis/bond_dissociation.py b/src/pymatgen/analysis/bond_dissociation.py index 73b9f2d4fe2..cbf6ed83845 100644 --- a/src/pymatgen/analysis/bond_dissociation.py +++ b/src/pymatgen/analysis/bond_dissociation.py @@ -8,7 +8,6 @@ import networkx as nx from monty.json import MSONable - from pymatgen.analysis.fragmenter import open_ring from pymatgen.analysis.graphs import MoleculeGraph, MolGraphSplitError from pymatgen.analysis.local_env import OpenBabelNN diff --git a/src/pymatgen/analysis/bond_valence.py b/src/pymatgen/analysis/bond_valence.py index e9cbe1fc47b..a81a71f5462 100644 --- a/src/pymatgen/analysis/bond_valence.py +++ b/src/pymatgen/analysis/bond_valence.py @@ -11,7 +11,6 @@ import numpy as np from monty.serialization import loadfn - from pymatgen.core import Element, Species, get_el_sp from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/analysis/chemenv/connectivity/connected_components.py b/src/pymatgen/analysis/chemenv/connectivity/connected_components.py index 3e75f758bc8..a7b361eda31 100644 --- a/src/pymatgen/analysis/chemenv/connectivity/connected_components.py +++ b/src/pymatgen/analysis/chemenv/connectivity/connected_components.py @@ -13,7 +13,6 @@ from monty.json import MSONable, jsanitize from networkx.algorithms.components import is_connected from networkx.algorithms.traversal import bfs_tree - from pymatgen.analysis.chemenv.connectivity.environment_nodes import EnvironmentNode from pymatgen.analysis.chemenv.utils.chemenv_errors import ChemenvError from pymatgen.analysis.chemenv.utils.graph_utils import get_delta diff --git a/src/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py b/src/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py index 99cb354f89c..26495f4a437 100644 --- a/src/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py +++ b/src/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py @@ -5,7 +5,6 @@ import logging import numpy as np - from pymatgen.analysis.chemenv.connectivity.structure_connectivity import StructureConnectivity __author__ = "David Waroquiers" diff --git a/src/pymatgen/analysis/chemenv/connectivity/structure_connectivity.py b/src/pymatgen/analysis/chemenv/connectivity/structure_connectivity.py index 7258be9baf4..4fb4c54cc50 100644 --- a/src/pymatgen/analysis/chemenv/connectivity/structure_connectivity.py +++ b/src/pymatgen/analysis/chemenv/connectivity/structure_connectivity.py @@ -9,7 +9,6 @@ import networkx as nx import numpy as np from monty.json import MSONable, jsanitize - from pymatgen.analysis.chemenv.connectivity.connected_components import ConnectedComponent from pymatgen.analysis.chemenv.connectivity.environment_nodes import get_environment_node from pymatgen.analysis.chemenv.coordination_environments.structure_environments import LightStructureEnvironments diff --git a/src/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py b/src/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py index 3f814ac3ade..abc7c2b4778 100644 --- a/src/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py +++ b/src/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py @@ -13,8 +13,6 @@ import numpy as np from monty.json import MSONable -from scipy.stats import gmean - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import AllCoordinationGeometries from pymatgen.analysis.chemenv.coordination_environments.voronoi import DetailedVoronoiContainer from pymatgen.analysis.chemenv.utils.chemenv_errors import EquivalentSiteSearchError @@ -29,6 +27,7 @@ from pymatgen.core.operations import SymmOp from pymatgen.core.sites import PeriodicSite from pymatgen.symmetry.analyzer import SpacegroupAnalyzer +from scipy.stats import gmean if TYPE_CHECKING: from typing import ClassVar diff --git a/src/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py b/src/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py index 2e2b91e0066..523448b5bde 100644 --- a/src/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py +++ b/src/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py @@ -23,7 +23,6 @@ import numpy as np from numpy.linalg import norm, svd - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import MultiWeightsChemenvStrategy from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import ( diff --git a/src/pymatgen/analysis/chemenv/coordination_environments/structure_environments.py b/src/pymatgen/analysis/chemenv/coordination_environments/structure_environments.py index 5d03166f83f..5b81229ced8 100644 --- a/src/pymatgen/analysis/chemenv/coordination_environments/structure_environments.py +++ b/src/pymatgen/analysis/chemenv/coordination_environments/structure_environments.py @@ -17,7 +17,6 @@ from matplotlib.gridspec import GridSpec from matplotlib.patches import Polygon from monty.json import MontyDecoder, MSONable, jsanitize - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import AllCoordinationGeometries from pymatgen.analysis.chemenv.coordination_environments.voronoi import DetailedVoronoiContainer from pymatgen.analysis.chemenv.utils.chemenv_errors import ChemenvError diff --git a/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py b/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py index 77fc1ffcbd4..08f257d3ae2 100644 --- a/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py +++ b/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py @@ -9,8 +9,6 @@ import matplotlib.pyplot as plt import numpy as np from monty.json import MSONable -from scipy.spatial import Voronoi - from pymatgen.analysis.chemenv.utils.coordination_geometry_utils import ( get_lower_and_upper_f, rectangle_surface_intersection, @@ -20,6 +18,7 @@ from pymatgen.analysis.chemenv.utils.math_utils import normal_cdf_step from pymatgen.core.sites import PeriodicSite from pymatgen.core.structure import Structure +from scipy.spatial import Voronoi if TYPE_CHECKING: from typing_extensions import Self diff --git a/src/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py b/src/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py index edf391b023b..358d03d39a0 100644 --- a/src/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py +++ b/src/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py @@ -7,12 +7,11 @@ import numpy as np from numpy.linalg import norm +from pymatgen.analysis.chemenv.utils.chemenv_errors import SolidAngleError from scipy.integrate import quad from scipy.interpolate import UnivariateSpline from scipy.spatial import ConvexHull -from pymatgen.analysis.chemenv.utils.chemenv_errors import SolidAngleError - if TYPE_CHECKING: from typing import Callable diff --git a/src/pymatgen/analysis/chemenv/utils/func_utils.py b/src/pymatgen/analysis/chemenv/utils/func_utils.py index 5bae04c94a9..d8437cde208 100644 --- a/src/pymatgen/analysis/chemenv/utils/func_utils.py +++ b/src/pymatgen/analysis/chemenv/utils/func_utils.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.analysis.chemenv.utils.math_utils import ( power2_decreasing_exp, power2_inverse_decreasing, diff --git a/src/pymatgen/analysis/chemenv/utils/scripts_utils.py b/src/pymatgen/analysis/chemenv/utils/scripts_utils.py index ca0fe90c08c..e153a7fcb63 100644 --- a/src/pymatgen/analysis/chemenv/utils/scripts_utils.py +++ b/src/pymatgen/analysis/chemenv/utils/scripts_utils.py @@ -6,7 +6,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import ( SimpleAbundanceChemenvStrategy, SimplestChemenvStrategy, diff --git a/src/pymatgen/analysis/chempot_diagram.py b/src/pymatgen/analysis/chempot_diagram.py index a2c6cfc7031..769c3a69b8f 100644 --- a/src/pymatgen/analysis/chempot_diagram.py +++ b/src/pymatgen/analysis/chempot_diagram.py @@ -33,13 +33,12 @@ import plotly.express as px from monty.json import MSONable from plotly.graph_objects import Figure, Mesh3d, Scatter, Scatter3d -from scipy.spatial import ConvexHull, HalfspaceIntersection - from pymatgen.analysis.phase_diagram import PDEntry, PhaseDiagram from pymatgen.core.composition import Composition, Element from pymatgen.util.coord import Simplex from pymatgen.util.due import Doi, due from pymatgen.util.string import htmlify +from scipy.spatial import ConvexHull, HalfspaceIntersection if TYPE_CHECKING: from pymatgen.entries.computed_entries import ComputedEntry diff --git a/src/pymatgen/analysis/cost.py b/src/pymatgen/analysis/cost.py index 458fea711e6..8ed4689301c 100644 --- a/src/pymatgen/analysis/cost.py +++ b/src/pymatgen/analysis/cost.py @@ -17,7 +17,6 @@ import scipy.constants as const from monty.design_patterns import singleton - from pymatgen.analysis.phase_diagram import PDEntry, PhaseDiagram from pymatgen.core import Composition, Element from pymatgen.util.provenance import is_valid_bibtex diff --git a/src/pymatgen/analysis/diffraction/core.py b/src/pymatgen/analysis/diffraction/core.py index 4e852b5e8f8..4a4dce2623a 100644 --- a/src/pymatgen/analysis/diffraction/core.py +++ b/src/pymatgen/analysis/diffraction/core.py @@ -8,7 +8,6 @@ import matplotlib.pyplot as plt import numpy as np - from pymatgen.core.spectrum import Spectrum from pymatgen.util.plotting import add_fig_kwargs, pretty_plot diff --git a/src/pymatgen/analysis/diffraction/neutron.py b/src/pymatgen/analysis/diffraction/neutron.py index 59d36759ba0..d9ef7328935 100644 --- a/src/pymatgen/analysis/diffraction/neutron.py +++ b/src/pymatgen/analysis/diffraction/neutron.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.analysis.diffraction.core import ( AbstractDiffractionPatternCalculator, DiffractionPattern, diff --git a/src/pymatgen/analysis/diffraction/tem.py b/src/pymatgen/analysis/diffraction/tem.py index 45b8b9f9cf4..98103a22945 100644 --- a/src/pymatgen/analysis/diffraction/tem.py +++ b/src/pymatgen/analysis/diffraction/tem.py @@ -11,7 +11,6 @@ import pandas as pd import plotly.graph_objects as go import scipy.constants as sc - from pymatgen.analysis.diffraction.core import AbstractDiffractionPatternCalculator from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.string import latexify_spacegroup, unicodeify_spacegroup @@ -19,7 +18,6 @@ if TYPE_CHECKING: from numpy.typing import NDArray - from pymatgen.core import Structure __author__ = "Frank Wan, Jason Liang" diff --git a/src/pymatgen/analysis/diffraction/xrd.py b/src/pymatgen/analysis/diffraction/xrd.py index 49eb07e05a0..654cab6d3fe 100644 --- a/src/pymatgen/analysis/diffraction/xrd.py +++ b/src/pymatgen/analysis/diffraction/xrd.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.analysis.diffraction.core import ( AbstractDiffractionPatternCalculator, DiffractionPattern, diff --git a/src/pymatgen/analysis/dimensionality.py b/src/pymatgen/analysis/dimensionality.py index 7ce19444e03..93746d4f5db 100644 --- a/src/pymatgen/analysis/dimensionality.py +++ b/src/pymatgen/analysis/dimensionality.py @@ -29,7 +29,6 @@ import networkx as nx import numpy as np from networkx.readwrite import json_graph - from pymatgen.analysis.graphs import MoleculeGraph, StructureGraph from pymatgen.analysis.local_env import JmolNN from pymatgen.analysis.structure_analyzer import get_max_bond_lengths diff --git a/src/pymatgen/analysis/elasticity/elastic.py b/src/pymatgen/analysis/elasticity/elastic.py index 7ac2ef19015..805b6017013 100644 --- a/src/pymatgen/analysis/elasticity/elastic.py +++ b/src/pymatgen/analysis/elasticity/elastic.py @@ -13,24 +13,22 @@ import numpy as np import sympy as sp -from scipy.integrate import quad -from scipy.optimize import root -from scipy.special import factorial - from pymatgen.analysis.elasticity.strain import Strain from pymatgen.analysis.elasticity.stress import Stress from pymatgen.core.tensors import DEFAULT_QUAD, SquareTensor, Tensor, TensorCollection, get_uvec from pymatgen.core.units import Unit from pymatgen.util.due import Doi, due +from scipy.integrate import quad +from scipy.optimize import root +from scipy.special import factorial if TYPE_CHECKING: from collections.abc import Sequence from typing import Literal from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self __author__ = "Joseph Montoya" diff --git a/src/pymatgen/analysis/elasticity/strain.py b/src/pymatgen/analysis/elasticity/strain.py index 99fe716a249..13092b5daf2 100644 --- a/src/pymatgen/analysis/elasticity/strain.py +++ b/src/pymatgen/analysis/elasticity/strain.py @@ -12,7 +12,6 @@ import numpy as np import scipy - from pymatgen.core.lattice import Lattice from pymatgen.core.tensors import SquareTensor, symmetry_reduce @@ -21,9 +20,8 @@ from typing import Literal from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core.structure import Structure + from typing_extensions import Self __author__ = "Joseph Montoya" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/src/pymatgen/analysis/elasticity/stress.py b/src/pymatgen/analysis/elasticity/stress.py index 3e5765130ca..720eb00dc4f 100644 --- a/src/pymatgen/analysis/elasticity/stress.py +++ b/src/pymatgen/analysis/elasticity/stress.py @@ -9,7 +9,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.core.tensors import SquareTensor if TYPE_CHECKING: diff --git a/src/pymatgen/analysis/energy_models.py b/src/pymatgen/analysis/energy_models.py index fa3c6a1d091..501287f7f01 100644 --- a/src/pymatgen/analysis/energy_models.py +++ b/src/pymatgen/analysis/energy_models.py @@ -10,14 +10,12 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.analysis.ewald import EwaldSummation from pymatgen.symmetry.analyzer import SpacegroupAnalyzer if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self __version__ = "0.1" diff --git a/src/pymatgen/analysis/eos.py b/src/pymatgen/analysis/eos.py index b5f49e472fa..fc526884537 100644 --- a/src/pymatgen/analysis/eos.py +++ b/src/pymatgen/analysis/eos.py @@ -13,10 +13,9 @@ from typing import TYPE_CHECKING import numpy as np -from scipy.optimize import leastsq, minimize - from pymatgen.core.units import FloatWithUnit from pymatgen.util.plotting import add_fig_kwargs, get_ax_fig, pretty_plot +from scipy.optimize import leastsq, minimize if TYPE_CHECKING: from typing import ClassVar diff --git a/src/pymatgen/analysis/ewald.py b/src/pymatgen/analysis/ewald.py index e37d6a4c793..46fb6c523ee 100644 --- a/src/pymatgen/analysis/ewald.py +++ b/src/pymatgen/analysis/ewald.py @@ -11,11 +11,10 @@ import numpy as np from monty.json import MSONable -from scipy import constants -from scipy.special import comb, erfc - from pymatgen.core.structure import Structure from pymatgen.util.due import Doi, due +from scipy import constants +from scipy.special import comb, erfc if TYPE_CHECKING: from typing import Any diff --git a/src/pymatgen/analysis/ferroelectricity/polarization.py b/src/pymatgen/analysis/ferroelectricity/polarization.py index 0ec0e7e1043..2e0175fdf3b 100644 --- a/src/pymatgen/analysis/ferroelectricity/polarization.py +++ b/src/pymatgen/analysis/ferroelectricity/polarization.py @@ -48,17 +48,15 @@ from typing import TYPE_CHECKING import numpy as np -from scipy.interpolate import UnivariateSpline - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure +from scipy.interpolate import UnivariateSpline if TYPE_CHECKING: from collections.abc import Sequence - from typing_extensions import Self - from pymatgen.core.sites import PeriodicSite + from typing_extensions import Self __author__ = "Tess Smidt" diff --git a/src/pymatgen/analysis/fragmenter.py b/src/pymatgen/analysis/fragmenter.py index 7458a46dfeb..98ad92a2b24 100644 --- a/src/pymatgen/analysis/fragmenter.py +++ b/src/pymatgen/analysis/fragmenter.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.analysis.graphs import MoleculeGraph, MolGraphSplitError from pymatgen.analysis.local_env import OpenBabelNN, metal_edge_extender from pymatgen.io.babel import BabelMolAdaptor diff --git a/src/pymatgen/analysis/graphs.py b/src/pymatgen/analysis/graphs.py index 9a206c5e578..998db8a72bf 100644 --- a/src/pymatgen/analysis/graphs.py +++ b/src/pymatgen/analysis/graphs.py @@ -20,13 +20,12 @@ from monty.json import MSONable from networkx.drawing.nx_agraph import write_dot from networkx.readwrite import json_graph -from scipy.spatial import KDTree -from scipy.stats import describe - from pymatgen.core import Lattice, Molecule, PeriodicSite, Structure from pymatgen.core.structure import FunctionalGroups from pymatgen.util.coord import lattice_points_in_supercell from pymatgen.vis.structure_vtk import EL_COLORS +from scipy.spatial import KDTree +from scipy.stats import describe try: import igraph @@ -39,11 +38,10 @@ from igraph import Graph from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.analysis.local_env import NearNeighbors from pymatgen.core import Species from pymatgen.util.typing import Tuple3Ints + from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/src/pymatgen/analysis/hhi.py b/src/pymatgen/analysis/hhi.py index 85708e78366..b893fe3a131 100644 --- a/src/pymatgen/analysis/hhi.py +++ b/src/pymatgen/analysis/hhi.py @@ -14,7 +14,6 @@ import os from monty.design_patterns import singleton - from pymatgen.core import Composition, Element __author__ = "Anubhav Jain" diff --git a/src/pymatgen/analysis/interface_reactions.py b/src/pymatgen/analysis/interface_reactions.py index 9c89adbc8b1..908cd538ff0 100644 --- a/src/pymatgen/analysis/interface_reactions.py +++ b/src/pymatgen/analysis/interface_reactions.py @@ -15,7 +15,6 @@ from monty.json import MSONable from pandas import DataFrame from plotly.graph_objects import Figure, Scatter - from pymatgen.analysis.phase_diagram import GrandPotentialPhaseDiagram, PhaseDiagram from pymatgen.analysis.reaction_calculator import Reaction from pymatgen.core.composition import Composition diff --git a/src/pymatgen/analysis/interfaces/coherent_interfaces.py b/src/pymatgen/analysis/interfaces/coherent_interfaces.py index 3e145fc5618..e52dab38e04 100644 --- a/src/pymatgen/analysis/interfaces/coherent_interfaces.py +++ b/src/pymatgen/analysis/interfaces/coherent_interfaces.py @@ -7,12 +7,11 @@ import numpy as np from numpy.testing import assert_allclose -from scipy.linalg import polar - from pymatgen.analysis.elasticity.strain import Deformation from pymatgen.analysis.interfaces.zsl import ZSLGenerator, fast_norm from pymatgen.core.interface import Interface, label_termination from pymatgen.core.surface import SlabGenerator +from scipy.linalg import polar if TYPE_CHECKING: from collections.abc import Iterator, Sequence diff --git a/src/pymatgen/analysis/interfaces/substrate_analyzer.py b/src/pymatgen/analysis/interfaces/substrate_analyzer.py index 41746fdd24d..62b2a4a5152 100644 --- a/src/pymatgen/analysis/interfaces/substrate_analyzer.py +++ b/src/pymatgen/analysis/interfaces/substrate_analyzer.py @@ -11,10 +11,9 @@ if TYPE_CHECKING: from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core import Structure from pymatgen.util.typing import Tuple3Ints + from typing_extensions import Self @dataclass diff --git a/src/pymatgen/analysis/interfaces/zsl.py b/src/pymatgen/analysis/interfaces/zsl.py index 8aae62a46c1..fdd17dbcdb0 100644 --- a/src/pymatgen/analysis/interfaces/zsl.py +++ b/src/pymatgen/analysis/interfaces/zsl.py @@ -8,7 +8,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.util.due import Doi, due from pymatgen.util.numba import njit diff --git a/src/pymatgen/analysis/local_env.py b/src/pymatgen/analysis/local_env.py index 7d31bf192df..56c5365955e 100644 --- a/src/pymatgen/analysis/local_env.py +++ b/src/pymatgen/analysis/local_env.py @@ -19,13 +19,12 @@ import numpy as np from monty.dev import deprecated, requires from monty.serialization import loadfn -from ruamel.yaml import YAML -from scipy.spatial import Voronoi - from pymatgen.analysis.bond_valence import BV_PARAMS, BVAnalyzer from pymatgen.analysis.graphs import MoleculeGraph, StructureGraph from pymatgen.analysis.molecule_structure_comparator import CovalentRadius from pymatgen.core import Element, IStructure, PeriodicNeighbor, PeriodicSite, Site, Species, Structure +from ruamel.yaml import YAML +from scipy.spatial import Voronoi try: from openbabel import openbabel @@ -35,10 +34,9 @@ if TYPE_CHECKING: from typing import Any - from typing_extensions import Self - from pymatgen.core.composition import SpeciesLike from pymatgen.util.typing import Tuple3Ints + from typing_extensions import Self __author__ = "Shyue Ping Ong, Geoffroy Hautier, Sai Jayaraman, " diff --git a/src/pymatgen/analysis/magnetism/analyzer.py b/src/pymatgen/analysis/magnetism/analyzer.py index 6c10495630b..59359e773fd 100644 --- a/src/pymatgen/analysis/magnetism/analyzer.py +++ b/src/pymatgen/analysis/magnetism/analyzer.py @@ -13,10 +13,6 @@ import numpy as np from monty.serialization import loadfn -from ruamel.yaml.error import MarkedYAMLError -from scipy.signal import argrelextrema -from scipy.stats import gaussian_kde - from pymatgen.core.structure import DummySpecies, Element, Species, Structure from pymatgen.electronic_structure.core import Magmom from pymatgen.symmetry.analyzer import SpacegroupAnalyzer @@ -24,6 +20,9 @@ from pymatgen.transformations.advanced_transformations import MagOrderingTransformation, MagOrderParameterConstraint from pymatgen.transformations.standard_transformations import AutoOxiStateDecorationTransformation from pymatgen.util.due import Doi, due +from ruamel.yaml.error import MarkedYAMLError +from scipy.signal import argrelextrema +from scipy.stats import gaussian_kde if TYPE_CHECKING: from typing import Any diff --git a/src/pymatgen/analysis/magnetism/heisenberg.py b/src/pymatgen/analysis/magnetism/heisenberg.py index 35f4c12f116..ac627f903b1 100644 --- a/src/pymatgen/analysis/magnetism/heisenberg.py +++ b/src/pymatgen/analysis/magnetism/heisenberg.py @@ -15,7 +15,6 @@ import pandas as pd from monty.json import MSONable, jsanitize from monty.serialization import dumpfn - from pymatgen.analysis.graphs import StructureGraph from pymatgen.analysis.local_env import MinimumDistanceNN from pymatgen.analysis.magnetism import CollinearMagneticStructureAnalyzer, Ordering diff --git a/src/pymatgen/analysis/magnetism/jahnteller.py b/src/pymatgen/analysis/magnetism/jahnteller.py index dda2d3b4907..90727f19e15 100644 --- a/src/pymatgen/analysis/magnetism/jahnteller.py +++ b/src/pymatgen/analysis/magnetism/jahnteller.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Literal, cast import numpy as np - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.local_env import LocalStructOrderParams, get_neighbors_of_site_with_index from pymatgen.core import Species, get_el_sp diff --git a/src/pymatgen/analysis/molecule_matcher.py b/src/pymatgen/analysis/molecule_matcher.py index a8da516d5a5..1b779e6898c 100644 --- a/src/pymatgen/analysis/molecule_matcher.py +++ b/src/pymatgen/analysis/molecule_matcher.py @@ -22,14 +22,12 @@ import numpy as np from monty.dev import requires from monty.json import MSONable +from pymatgen.core.structure import Molecule from scipy.optimize import linear_sum_assignment from scipy.spatial.distance import cdist -from pymatgen.core.structure import Molecule - try: from openbabel import openbabel - from pymatgen.io.babel import BabelMolAdaptor except ImportError: openbabel = BabelMolAdaptor = None # type: ignore[misc] diff --git a/src/pymatgen/analysis/molecule_structure_comparator.py b/src/pymatgen/analysis/molecule_structure_comparator.py index 32d603abd28..28400b65a34 100644 --- a/src/pymatgen/analysis/molecule_structure_comparator.py +++ b/src/pymatgen/analysis/molecule_structure_comparator.py @@ -14,7 +14,6 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.util.due import Doi, due if TYPE_CHECKING: diff --git a/src/pymatgen/analysis/nmr.py b/src/pymatgen/analysis/nmr.py index bf16031f028..91a7ccd21d4 100644 --- a/src/pymatgen/analysis/nmr.py +++ b/src/pymatgen/analysis/nmr.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, NamedTuple import numpy as np - from pymatgen.core import Site, Species from pymatgen.core.tensors import SquareTensor from pymatgen.core.units import FloatWithUnit diff --git a/src/pymatgen/analysis/phase_diagram.py b/src/pymatgen/analysis/phase_diagram.py index 8411ac64da2..b9d99a7ca44 100644 --- a/src/pymatgen/analysis/phase_diagram.py +++ b/src/pymatgen/analysis/phase_diagram.py @@ -21,11 +21,6 @@ from matplotlib.colors import LinearSegmentedColormap, Normalize from matplotlib.font_manager import FontProperties from monty.json import MontyDecoder, MSONable -from scipy import interpolate -from scipy.optimize import minimize -from scipy.spatial import ConvexHull -from tqdm import tqdm - from pymatgen.analysis.reaction_calculator import Reaction, ReactionError from pymatgen.core import DummySpecies, Element, get_el_sp from pymatgen.core.composition import Composition @@ -34,6 +29,10 @@ from pymatgen.util.due import Doi, due from pymatgen.util.plotting import pretty_plot from pymatgen.util.string import htmlify, latexify +from scipy import interpolate +from scipy.optimize import minimize +from scipy.spatial import ConvexHull +from tqdm import tqdm if TYPE_CHECKING: from collections.abc import Collection, Iterator, Sequence diff --git a/src/pymatgen/analysis/piezo.py b/src/pymatgen/analysis/piezo.py index 2f168e1eef4..7a5b32ac8d0 100644 --- a/src/pymatgen/analysis/piezo.py +++ b/src/pymatgen/analysis/piezo.py @@ -6,7 +6,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.core.tensors import Tensor if TYPE_CHECKING: diff --git a/src/pymatgen/analysis/piezo_sensitivity.py b/src/pymatgen/analysis/piezo_sensitivity.py index 0a93140dc15..d5b4f232199 100644 --- a/src/pymatgen/analysis/piezo_sensitivity.py +++ b/src/pymatgen/analysis/piezo_sensitivity.py @@ -7,7 +7,6 @@ import numpy as np from monty.dev import requires - from pymatgen.core.tensors import Tensor from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/analysis/pourbaix_diagram.py b/src/pymatgen/analysis/pourbaix_diagram.py index 74fa89f3d22..dad540db5fb 100644 --- a/src/pymatgen/analysis/pourbaix_diagram.py +++ b/src/pymatgen/analysis/pourbaix_diagram.py @@ -16,9 +16,6 @@ import numpy as np from monty.json import MontyDecoder, MSONable -from scipy.spatial import ConvexHull, HalfspaceIntersection -from scipy.special import comb - from pymatgen.analysis.phase_diagram import PDEntry, PhaseDiagram from pymatgen.analysis.reaction_calculator import Reaction, ReactionError from pymatgen.core import Composition, Element @@ -29,6 +26,8 @@ from pymatgen.util.due import Doi, due from pymatgen.util.plotting import pretty_plot from pymatgen.util.string import Stringify +from scipy.spatial import ConvexHull, HalfspaceIntersection +from scipy.special import comb if TYPE_CHECKING: from typing import Any diff --git a/src/pymatgen/analysis/prototypes.py b/src/pymatgen/analysis/prototypes.py index a9cdf29c70c..6423e53170c 100644 --- a/src/pymatgen/analysis/prototypes.py +++ b/src/pymatgen/analysis/prototypes.py @@ -17,7 +17,6 @@ from typing import TYPE_CHECKING from monty.serialization import loadfn - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.util.due import Doi, due diff --git a/src/pymatgen/analysis/quasiharmonic.py b/src/pymatgen/analysis/quasiharmonic.py index d1c7612fecf..f7b8aa08a05 100644 --- a/src/pymatgen/analysis/quasiharmonic.py +++ b/src/pymatgen/analysis/quasiharmonic.py @@ -15,15 +15,14 @@ import numpy as np from monty.dev import deprecated +from pymatgen.analysis.eos import EOS, PolynomialEOS +from pymatgen.core.units import FloatWithUnit +from pymatgen.util.due import Doi, due from scipy.constants import physical_constants from scipy.integrate import quadrature from scipy.misc import derivative from scipy.optimize import minimize -from pymatgen.analysis.eos import EOS, PolynomialEOS -from pymatgen.core.units import FloatWithUnit -from pymatgen.util.due import Doi, due - __author__ = "Kiran Mathew, Brandon Bocklund" __credits__ = "Cormac Toher" diff --git a/src/pymatgen/analysis/quasirrho.py b/src/pymatgen/analysis/quasirrho.py index 0c66f730db3..5846f19339b 100644 --- a/src/pymatgen/analysis/quasirrho.py +++ b/src/pymatgen/analysis/quasirrho.py @@ -15,16 +15,14 @@ import numpy as np import scipy.constants as const - from pymatgen.core.units import kb as kb_ev from pymatgen.util.due import Doi, due if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.core import Molecule from pymatgen.io.gaussian import GaussianOutput from pymatgen.io.qchem.outputs import QCOutput + from typing_extensions import Self __author__ = "Alex Epstein" __copyright__ = "Copyright 2020, The Materials Project" diff --git a/src/pymatgen/analysis/reaction_calculator.py b/src/pymatgen/analysis/reaction_calculator.py index 456e6ea97d9..638e9189318 100644 --- a/src/pymatgen/analysis/reaction_calculator.py +++ b/src/pymatgen/analysis/reaction_calculator.py @@ -10,18 +10,16 @@ import numpy as np from monty.fractions import gcd_float from monty.json import MontyDecoder, MSONable -from uncertainties import ufloat - from pymatgen.core.composition import Composition from pymatgen.entries.computed_entries import ComputedEntry +from uncertainties import ufloat if TYPE_CHECKING: from collections.abc import Mapping - from typing_extensions import Self - from pymatgen.core import Element, Species from pymatgen.util.typing import CompositionLike + from typing_extensions import Self __author__ = "Shyue Ping Ong, Anubhav Jain" __copyright__ = "Copyright 2011, The Materials Project" diff --git a/src/pymatgen/analysis/solar/slme.py b/src/pymatgen/analysis/solar/slme.py index cc7fdf2dabf..976ef49b701 100644 --- a/src/pymatgen/analysis/solar/slme.py +++ b/src/pymatgen/analysis/solar/slme.py @@ -22,10 +22,9 @@ from scipy.integrate import simpson except ImportError: from scipy.integrate import simps as simpson -from scipy.interpolate import interp1d - from pymatgen.io.vasp.outputs import Vasprun from pymatgen.util.due import Doi, due +from scipy.interpolate import interp1d due.cite( Doi("10.1021/acs.chemmater.9b02166"), diff --git a/src/pymatgen/analysis/structure_analyzer.py b/src/pymatgen/analysis/structure_analyzer.py index 45bc60da98d..a044216444a 100644 --- a/src/pymatgen/analysis/structure_analyzer.py +++ b/src/pymatgen/analysis/structure_analyzer.py @@ -10,11 +10,10 @@ import matplotlib.pyplot as plt import numpy as np -from scipy.spatial import Voronoi - from pymatgen.analysis.local_env import JmolNN, VoronoiNN from pymatgen.core import Composition, Element, PeriodicSite, Species from pymatgen.symmetry.analyzer import SpacegroupAnalyzer +from scipy.spatial import Voronoi if TYPE_CHECKING: from pymatgen.core import Structure diff --git a/src/pymatgen/analysis/structure_matcher.py b/src/pymatgen/analysis/structure_matcher.py index 0ad9c9db788..7a2650bef71 100644 --- a/src/pymatgen/analysis/structure_matcher.py +++ b/src/pymatgen/analysis/structure_matcher.py @@ -8,7 +8,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.core import Composition, Lattice, Structure, get_el_sp from pymatgen.optimization.linear_assignment import LinearAssignment from pymatgen.util.coord import lattice_points_in_supercell @@ -18,9 +17,8 @@ from collections.abc import Mapping, Sequence from typing import Literal - from typing_extensions import Self - from pymatgen.util.typing import SpeciesLike + from typing_extensions import Self __author__ = "William Davidson Richards, Stephen Dacek, Shyue Ping Ong" __copyright__ = "Copyright 2011, The Materials Project" diff --git a/src/pymatgen/analysis/structure_prediction/dopant_predictor.py b/src/pymatgen/analysis/structure_prediction/dopant_predictor.py index e21603e13ee..dcc750ec9f3 100644 --- a/src/pymatgen/analysis/structure_prediction/dopant_predictor.py +++ b/src/pymatgen/analysis/structure_prediction/dopant_predictor.py @@ -5,7 +5,6 @@ import warnings import numpy as np - from pymatgen.analysis.structure_prediction.substitution_probability import SubstitutionPredictor from pymatgen.core import Element, Species diff --git a/src/pymatgen/analysis/structure_prediction/substitution_probability.py b/src/pymatgen/analysis/structure_prediction/substitution_probability.py index 210e218d2b1..75720ce9419 100644 --- a/src/pymatgen/analysis/structure_prediction/substitution_probability.py +++ b/src/pymatgen/analysis/structure_prediction/substitution_probability.py @@ -13,7 +13,6 @@ from typing import TYPE_CHECKING from monty.design_patterns import cached_class - from pymatgen.core import Species, get_el_sp from pymatgen.util.due import Doi, due diff --git a/src/pymatgen/analysis/structure_prediction/substitutor.py b/src/pymatgen/analysis/structure_prediction/substitutor.py index 2921355d637..c36df98f030 100644 --- a/src/pymatgen/analysis/structure_prediction/substitutor.py +++ b/src/pymatgen/analysis/structure_prediction/substitutor.py @@ -9,7 +9,6 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.alchemy.filters import RemoveDuplicatesFilter, RemoveExistingFilter from pymatgen.alchemy.materials import TransformedStructure from pymatgen.alchemy.transmuters import StandardTransmuter diff --git a/src/pymatgen/analysis/structure_prediction/volume_predictor.py b/src/pymatgen/analysis/structure_prediction/volume_predictor.py index dd749e8416a..5ce74b652f4 100644 --- a/src/pymatgen/analysis/structure_prediction/volume_predictor.py +++ b/src/pymatgen/analysis/structure_prediction/volume_predictor.py @@ -7,7 +7,6 @@ import numpy as np from monty.serialization import loadfn - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Structure diff --git a/src/pymatgen/analysis/surface_analysis.py b/src/pymatgen/analysis/surface_analysis.py index ff49d82088f..f395f90afd0 100644 --- a/src/pymatgen/analysis/surface_analysis.py +++ b/src/pymatgen/analysis/surface_analysis.py @@ -42,9 +42,6 @@ import matplotlib.pyplot as plt import numpy as np -from sympy import Symbol -from sympy.solvers import linsolve, solve - from pymatgen.analysis.wulff import WulffShape from pymatgen.core import Structure from pymatgen.core.composition import Composition @@ -54,11 +51,12 @@ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.due import Doi, due from pymatgen.util.plotting import pretty_plot +from sympy import Symbol +from sympy.solvers import linsolve, solve if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.util.typing import Tuple3Ints + from typing_extensions import Self EV_PER_ANG2_TO_JOULES_PER_M2 = 16.0217656 diff --git a/src/pymatgen/analysis/topological/spillage.py b/src/pymatgen/analysis/topological/spillage.py index d0da47ccfe1..e088b98553b 100644 --- a/src/pymatgen/analysis/topological/spillage.py +++ b/src/pymatgen/analysis/topological/spillage.py @@ -8,7 +8,6 @@ from __future__ import annotations import numpy as np - from pymatgen.io.vasp.outputs import Wavecar diff --git a/src/pymatgen/analysis/transition_state.py b/src/pymatgen/analysis/transition_state.py index 5dbbe1fbb54..e7c6d9b2924 100644 --- a/src/pymatgen/analysis/transition_state.py +++ b/src/pymatgen/analysis/transition_state.py @@ -15,12 +15,11 @@ import matplotlib.pyplot as plt import numpy as np from monty.json import MSONable, jsanitize -from scipy.interpolate import CubicSpline - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Structure from pymatgen.io.vasp import Outcar from pymatgen.util.plotting import pretty_plot +from scipy.interpolate import CubicSpline if TYPE_CHECKING: from typing_extensions import Self diff --git a/src/pymatgen/analysis/wulff.py b/src/pymatgen/analysis/wulff.py index 420d77c4161..23a6986d87f 100644 --- a/src/pymatgen/analysis/wulff.py +++ b/src/pymatgen/analysis/wulff.py @@ -25,11 +25,10 @@ import matplotlib.pyplot as plt import numpy as np import plotly.graph_objects as go -from scipy.spatial import ConvexHull - from pymatgen.core.structure import Structure from pymatgen.util.coord import get_angle from pymatgen.util.string import unicodeify_spacegroup +from scipy.spatial import ConvexHull if TYPE_CHECKING: from pymatgen.core.lattice import Lattice diff --git a/src/pymatgen/analysis/xas/spectrum.py b/src/pymatgen/analysis/xas/spectrum.py index 007c4596642..97af8b85282 100644 --- a/src/pymatgen/analysis/xas/spectrum.py +++ b/src/pymatgen/analysis/xas/spectrum.py @@ -7,11 +7,10 @@ from typing import TYPE_CHECKING import numpy as np -from scipy.interpolate import interp1d - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core.spectrum import Spectrum from pymatgen.symmetry.analyzer import SpacegroupAnalyzer +from scipy.interpolate import interp1d if TYPE_CHECKING: from typing import Literal diff --git a/src/pymatgen/analysis/xps.py b/src/pymatgen/analysis/xps.py index 1aca37a1eee..4e9acff9540 100644 --- a/src/pymatgen/analysis/xps.py +++ b/src/pymatgen/analysis/xps.py @@ -25,15 +25,13 @@ import numpy as np import pandas as pd - from pymatgen.core import Element from pymatgen.core.spectrum import Spectrum from pymatgen.util.due import Doi, due if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.electronic_structure.dos import CompleteDos + from typing_extensions import Self due.cite( diff --git a/src/pymatgen/apps/battery/analyzer.py b/src/pymatgen/apps/battery/analyzer.py index 2543b44da92..3d08e147c35 100644 --- a/src/pymatgen/apps/battery/analyzer.py +++ b/src/pymatgen/apps/battery/analyzer.py @@ -6,7 +6,6 @@ from collections import defaultdict import scipy.constants as const - from pymatgen.core import Composition, Element, Species __author__ = "Anubhav Jain" diff --git a/src/pymatgen/apps/battery/battery_abc.py b/src/pymatgen/apps/battery/battery_abc.py index 78f4d984498..8465f85445f 100644 --- a/src/pymatgen/apps/battery/battery_abc.py +++ b/src/pymatgen/apps/battery/battery_abc.py @@ -12,9 +12,8 @@ from typing import TYPE_CHECKING from monty.json import MSONable -from scipy.constants import N_A - from pymatgen.core import Composition, Element +from scipy.constants import N_A if TYPE_CHECKING: from pymatgen.entries.computed_entries import ComputedEntry diff --git a/src/pymatgen/apps/battery/conversion_battery.py b/src/pymatgen/apps/battery/conversion_battery.py index b84da63efd6..ee274bcd40d 100644 --- a/src/pymatgen/apps/battery/conversion_battery.py +++ b/src/pymatgen/apps/battery/conversion_battery.py @@ -5,20 +5,18 @@ from dataclasses import dataclass from typing import TYPE_CHECKING -from scipy.constants import N_A - from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.analysis.reaction_calculator import BalancedReaction from pymatgen.apps.battery.battery_abc import AbstractElectrode, AbstractVoltagePair from pymatgen.core import Composition, Element from pymatgen.core.units import Charge, Time +from scipy.constants import N_A if TYPE_CHECKING: from collections.abc import Iterable - from typing_extensions import Self - from pymatgen.entries.computed_entries import ComputedEntry + from typing_extensions import Self @dataclass diff --git a/src/pymatgen/apps/battery/insertion_battery.py b/src/pymatgen/apps/battery/insertion_battery.py index 76631f53368..5f073fbc916 100644 --- a/src/pymatgen/apps/battery/insertion_battery.py +++ b/src/pymatgen/apps/battery/insertion_battery.py @@ -9,13 +9,12 @@ from typing import TYPE_CHECKING from monty.json import MontyDecoder -from scipy.constants import N_A - from pymatgen.analysis.phase_diagram import PDEntry, PhaseDiagram from pymatgen.apps.battery.battery_abc import AbstractElectrode, AbstractVoltagePair from pymatgen.core import Composition, Element from pymatgen.core.units import Charge, Time from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry +from scipy.constants import N_A if TYPE_CHECKING: from collections.abc import Iterable diff --git a/src/pymatgen/apps/battery/plotter.py b/src/pymatgen/apps/battery/plotter.py index f8725ef25e6..150f413784b 100644 --- a/src/pymatgen/apps/battery/plotter.py +++ b/src/pymatgen/apps/battery/plotter.py @@ -6,7 +6,6 @@ import matplotlib.pyplot as plt import plotly.graph_objects as go - from pymatgen.util.plotting import pretty_plot if TYPE_CHECKING: diff --git a/src/pymatgen/apps/borg/hive.py b/src/pymatgen/apps/borg/hive.py index a90380d0dee..87aaeeb2500 100644 --- a/src/pymatgen/apps/borg/hive.py +++ b/src/pymatgen/apps/borg/hive.py @@ -12,7 +12,6 @@ from monty.io import zopen from monty.json import MSONable - from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry from pymatgen.io.gaussian import GaussianOutput from pymatgen.io.vasp.inputs import Incar, Poscar, Potcar diff --git a/src/pymatgen/cli/feff_plot_cross_section.py b/src/pymatgen/cli/feff_plot_cross_section.py index a9a59f3a650..2bf9d89fe23 100755 --- a/src/pymatgen/cli/feff_plot_cross_section.py +++ b/src/pymatgen/cli/feff_plot_cross_section.py @@ -7,7 +7,6 @@ import argparse import matplotlib.pyplot as plt - from pymatgen.io.feff.outputs import Xmu from pymatgen.util.plotting import pretty_plot diff --git a/src/pymatgen/cli/pmg.py b/src/pymatgen/cli/pmg.py index 491d4ab2c11..1ee6a93ed7e 100755 --- a/src/pymatgen/cli/pmg.py +++ b/src/pymatgen/cli/pmg.py @@ -7,8 +7,6 @@ import argparse import itertools -from tabulate import tabulate, tabulate_formats - from pymatgen.cli.pmg_analyze import analyze from pymatgen.cli.pmg_config import configure_pmg from pymatgen.cli.pmg_plot import plot @@ -17,6 +15,7 @@ from pymatgen.core import SETTINGS from pymatgen.core.structure import Structure from pymatgen.io.vasp import Incar, Potcar +from tabulate import tabulate, tabulate_formats def parse_view(args): diff --git a/src/pymatgen/cli/pmg_analyze.py b/src/pymatgen/cli/pmg_analyze.py index c12ef4186ef..4db3fbcb2f6 100644 --- a/src/pymatgen/cli/pmg_analyze.py +++ b/src/pymatgen/cli/pmg_analyze.py @@ -7,11 +7,10 @@ import os import re -from tabulate import tabulate - from pymatgen.apps.borg.hive import SimpleVaspToComputedEntryDrone, VaspToComputedEntryDrone from pymatgen.apps.borg.queen import BorgQueen from pymatgen.io.vasp import Outcar +from tabulate import tabulate __author__ = "Shyue Ping Ong" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/src/pymatgen/cli/pmg_config.py b/src/pymatgen/cli/pmg_config.py index d8253983e19..8a0f4a04317 100755 --- a/src/pymatgen/cli/pmg_config.py +++ b/src/pymatgen/cli/pmg_config.py @@ -13,11 +13,10 @@ from monty.json import jsanitize from monty.serialization import dumpfn, loadfn -from ruamel import yaml - from pymatgen.core import OLD_SETTINGS_FILE, SETTINGS_FILE, Element from pymatgen.io.cp2k.inputs import GaussianTypeOrbitalBasisSet, GthPotential from pymatgen.io.cp2k.utils import chunk +from ruamel import yaml if TYPE_CHECKING: from argparse import Namespace diff --git a/src/pymatgen/cli/pmg_plot.py b/src/pymatgen/cli/pmg_plot.py index 89d0fa269ae..56e7d629660 100755 --- a/src/pymatgen/cli/pmg_plot.py +++ b/src/pymatgen/cli/pmg_plot.py @@ -5,7 +5,6 @@ from __future__ import annotations import matplotlib.pyplot as plt - from pymatgen.analysis.diffraction.xrd import XRDCalculator from pymatgen.core.structure import Structure from pymatgen.electronic_structure.plotter import DosPlotter diff --git a/src/pymatgen/cli/pmg_structure.py b/src/pymatgen/cli/pmg_structure.py index 3f85040fede..b8046b699ce 100755 --- a/src/pymatgen/cli/pmg_structure.py +++ b/src/pymatgen/cli/pmg_structure.py @@ -4,11 +4,10 @@ from __future__ import annotations -from tabulate import tabulate - from pymatgen.analysis.structure_matcher import ElementComparator, StructureMatcher from pymatgen.core.structure import Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer +from tabulate import tabulate __author__ = "Shyue Ping Ong" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/src/pymatgen/command_line/bader_caller.py b/src/pymatgen/command_line/bader_caller.py index dbbb743acc9..c85baf861ed 100644 --- a/src/pymatgen/command_line/bader_caller.py +++ b/src/pymatgen/command_line/bader_caller.py @@ -27,7 +27,6 @@ from monty.dev import deprecated from monty.shutil import decompress_file from monty.tempfile import ScratchDir - from pymatgen.io.common import VolumetricData from pymatgen.io.vasp.inputs import Potcar from pymatgen.io.vasp.outputs import Chgcar @@ -35,9 +34,8 @@ if TYPE_CHECKING: from typing import Any - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self __author__ = "shyuepingong" __version__ = "0.1" diff --git a/src/pymatgen/command_line/chargemol_caller.py b/src/pymatgen/command_line/chargemol_caller.py index d9b17a27404..a22e4b345a0 100644 --- a/src/pymatgen/command_line/chargemol_caller.py +++ b/src/pymatgen/command_line/chargemol_caller.py @@ -51,7 +51,6 @@ import numpy as np from monty.tempfile import ScratchDir - from pymatgen.core import Element from pymatgen.io.vasp.inputs import Potcar from pymatgen.io.vasp.outputs import Chgcar diff --git a/src/pymatgen/command_line/critic2_caller.py b/src/pymatgen/command_line/critic2_caller.py index 4f6261afe3e..9855345d7ff 100644 --- a/src/pymatgen/command_line/critic2_caller.py +++ b/src/pymatgen/command_line/critic2_caller.py @@ -52,18 +52,16 @@ from monty.json import MSONable from monty.serialization import loadfn from monty.tempfile import ScratchDir -from scipy.spatial import KDTree - from pymatgen.analysis.graphs import StructureGraph from pymatgen.core import DummySpecies from pymatgen.io.vasp.inputs import Potcar from pymatgen.io.vasp.outputs import Chgcar, VolumetricData from pymatgen.util.due import Doi, due +from scipy.spatial import KDTree if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) diff --git a/src/pymatgen/command_line/enumlib_caller.py b/src/pymatgen/command_line/enumlib_caller.py index bdae0256e0e..619060b8386 100644 --- a/src/pymatgen/command_line/enumlib_caller.py +++ b/src/pymatgen/command_line/enumlib_caller.py @@ -39,7 +39,6 @@ from monty.dev import requires from monty.fractions import lcm from monty.tempfile import ScratchDir - from pymatgen.core import DummySpecies, PeriodicSite, Structure from pymatgen.io.vasp.inputs import Poscar from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/command_line/gulp_caller.py b/src/pymatgen/command_line/gulp_caller.py index b13912f2433..fa3e250b99a 100644 --- a/src/pymatgen/command_line/gulp_caller.py +++ b/src/pymatgen/command_line/gulp_caller.py @@ -10,7 +10,6 @@ import subprocess from monty.tempfile import ScratchDir - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.core import Element, Lattice, Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/command_line/mcsqs_caller.py b/src/pymatgen/command_line/mcsqs_caller.py index dd1cca70b1c..980f04e2f13 100644 --- a/src/pymatgen/command_line/mcsqs_caller.py +++ b/src/pymatgen/command_line/mcsqs_caller.py @@ -13,7 +13,6 @@ from typing import TYPE_CHECKING, NamedTuple from monty.dev import requires - from pymatgen.core.structure import Structure if TYPE_CHECKING: diff --git a/src/pymatgen/command_line/vampire_caller.py b/src/pymatgen/command_line/vampire_caller.py index 8709088514d..44aa50c3895 100644 --- a/src/pymatgen/command_line/vampire_caller.py +++ b/src/pymatgen/command_line/vampire_caller.py @@ -21,7 +21,6 @@ import pandas as pd from monty.dev import requires from monty.json import MSONable - from pymatgen.analysis.magnetism.heisenberg import HeisenbergMapper __author__ = "ncfrey" diff --git a/src/pymatgen/core/__init__.py b/src/pymatgen/core/__init__.py index 2b160c6b793..c1f047de11c 100644 --- a/src/pymatgen/core/__init__.py +++ b/src/pymatgen/core/__init__.py @@ -7,8 +7,6 @@ from importlib.metadata import PackageNotFoundError, version from typing import Any -from ruamel.yaml import YAML - from pymatgen.core.composition import Composition from pymatgen.core.lattice import Lattice from pymatgen.core.operations import SymmOp @@ -16,6 +14,7 @@ from pymatgen.core.sites import PeriodicSite, Site from pymatgen.core.structure import IMolecule, IStructure, Molecule, PeriodicNeighbor, SiteCollection, Structure from pymatgen.core.units import ArrayWithUnit, FloatWithUnit, Unit +from ruamel.yaml import YAML __author__ = "Pymatgen Development Team" __email__ = "pymatgen@googlegroups.com" diff --git a/src/pymatgen/core/composition.py b/src/pymatgen/core/composition.py index a387dafd12c..8eaa6a5400d 100644 --- a/src/pymatgen/core/composition.py +++ b/src/pymatgen/core/composition.py @@ -18,7 +18,6 @@ from monty.fractions import gcd, gcd_float from monty.json import MSONable from monty.serialization import loadfn - from pymatgen.core.periodic_table import DummySpecies, Element, ElementType, Species, get_el_sp from pymatgen.core.units import Mass from pymatgen.util.string import Stringify, formula_double_format @@ -27,9 +26,8 @@ from collections.abc import Generator, Iterator from typing import Any, ClassVar - from typing_extensions import Self - from pymatgen.util.typing import SpeciesLike + from typing_extensions import Self module_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/src/pymatgen/core/interface.py b/src/pymatgen/core/interface.py index 0ac14fd1453..2856130e014 100644 --- a/src/pymatgen/core/interface.py +++ b/src/pymatgen/core/interface.py @@ -15,9 +15,6 @@ import numpy as np from monty.fractions import lcm from numpy.testing import assert_allclose -from scipy.cluster.hierarchy import fcluster, linkage -from scipy.spatial.distance import squareform - from pymatgen.analysis.adsorption import AdsorbateSiteFinder from pymatgen.core.lattice import Lattice from pymatgen.core.sites import PeriodicSite, Site @@ -25,16 +22,17 @@ from pymatgen.core.surface import Slab from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.typing import Tuple3Ints +from scipy.cluster.hierarchy import fcluster, linkage +from scipy.spatial.distance import squareform if TYPE_CHECKING: from collections.abc import Sequence from typing import Any, Callable from numpy.typing import ArrayLike, NDArray - from typing_extensions import Self - from pymatgen.core import Element from pymatgen.util.typing import CompositionLike, Matrix3D, MillerIndex, Tuple3Floats, Vector3D + from typing_extensions import Self Tuple4Ints = tuple[int, int, int, int] logger = logging.getLogger(__name__) diff --git a/src/pymatgen/core/ion.py b/src/pymatgen/core/ion.py index ec246d1ca34..c3d7cf3271c 100644 --- a/src/pymatgen/core/ion.py +++ b/src/pymatgen/core/ion.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.core.composition import Composition, reduce_formula from pymatgen.util.string import Stringify, charge_string, formula_double_format diff --git a/src/pymatgen/core/lattice.py b/src/pymatgen/core/lattice.py index 5448c09813a..89f28628e6d 100644 --- a/src/pymatgen/core/lattice.py +++ b/src/pymatgen/core/lattice.py @@ -16,19 +16,17 @@ import numpy as np from monty.dev import deprecated from monty.json import MSONable -from scipy.spatial import Voronoi - from pymatgen.util.coord import pbc_shortest_vectors from pymatgen.util.due import Doi, due +from scipy.spatial import Voronoi if TYPE_CHECKING: from collections.abc import Iterator from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core.operations import SymmOp from pymatgen.util.typing import MillerIndex, PbcLike, Vector3D + from typing_extensions import Self __author__ = "Shyue Ping Ong, Michael Kocher" __copyright__ = "Copyright 2011, The Materials Project" diff --git a/src/pymatgen/core/operations.py b/src/pymatgen/core/operations.py index 54f24f8515b..c67603c288f 100644 --- a/src/pymatgen/core/operations.py +++ b/src/pymatgen/core/operations.py @@ -10,7 +10,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.electronic_structure.core import Magmom from pymatgen.util.due import Doi, due from pymatgen.util.string import transformation_to_string diff --git a/src/pymatgen/core/periodic_table.py b/src/pymatgen/core/periodic_table.py index f26cff49a01..d1be4dd4795 100644 --- a/src/pymatgen/core/periodic_table.py +++ b/src/pymatgen/core/periodic_table.py @@ -17,7 +17,6 @@ import numpy as np from monty.dev import deprecated from monty.json import MSONable - from pymatgen.core.units import SUPPORTED_UNIT_NAMES, FloatWithUnit, Ha_to_eV, Length, Mass, Unit from pymatgen.io.core import ParseError from pymatgen.util.string import Stringify, formula_double_format @@ -25,9 +24,8 @@ if TYPE_CHECKING: from typing import Any, Callable, Literal - from typing_extensions import Self - from pymatgen.util.typing import SpeciesLike + from typing_extensions import Self # Load element data from JSON file with open(Path(__file__).absolute().parent / "periodic_table.json", encoding="utf-8") as ptable_json: diff --git a/src/pymatgen/core/sites.py b/src/pymatgen/core/sites.py index a703c79a335..fd8a61118ef 100644 --- a/src/pymatgen/core/sites.py +++ b/src/pymatgen/core/sites.py @@ -8,7 +8,6 @@ import numpy as np from monty.json import MontyDecoder, MontyEncoder, MSONable - from pymatgen.core.composition import Composition from pymatgen.core.lattice import Lattice from pymatgen.core.periodic_table import DummySpecies, Element, Species, get_el_sp @@ -18,9 +17,8 @@ from typing import Any from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.util.typing import CompositionLike, SpeciesLike, Vector3D + from typing_extensions import Self class Site(collections.abc.Hashable, MSONable): diff --git a/src/pymatgen/core/spectrum.py b/src/pymatgen/core/spectrum.py index 6567f3a63a2..94ade3ce43e 100644 --- a/src/pymatgen/core/spectrum.py +++ b/src/pymatgen/core/spectrum.py @@ -8,11 +8,10 @@ import numpy as np from monty.json import MSONable +from pymatgen.util.coord import get_linear_interpolated_value from scipy import stats from scipy.ndimage import convolve1d -from pymatgen.util.coord import get_linear_interpolated_value - if TYPE_CHECKING: from typing import Callable, Literal diff --git a/src/pymatgen/core/structure.py b/src/pymatgen/core/structure.py index 9886d3035d0..f0d7d477ad6 100644 --- a/src/pymatgen/core/structure.py +++ b/src/pymatgen/core/structure.py @@ -31,12 +31,6 @@ from monty.json import MSONable from numpy import cross, eye from numpy.linalg import norm -from ruamel.yaml import YAML -from scipy.cluster.hierarchy import fcluster, linkage -from scipy.linalg import expm, polar -from scipy.spatial.distance import squareform -from tabulate import tabulate - from pymatgen.core.bonds import CovalentBond, get_bond_length from pymatgen.core.composition import Composition from pymatgen.core.lattice import Lattice, get_points_in_spheres @@ -47,6 +41,11 @@ from pymatgen.electronic_structure.core import Magmom from pymatgen.symmetry.maggroups import MagneticSpaceGroup from pymatgen.util.coord import all_distances, get_angle, lattice_points_in_supercell +from ruamel.yaml import YAML +from scipy.cluster.hierarchy import fcluster, linkage +from scipy.linalg import expm, polar +from scipy.spatial.distance import squareform +from tabulate import tabulate if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Sequence @@ -59,9 +58,8 @@ from ase.optimize.optimize import Optimizer from matgl.ext.ase import TrajectoryObserver from numpy.typing import ArrayLike, NDArray - from typing_extensions import Self - from pymatgen.util.typing import CompositionLike, MillerIndex, PathLike, PbcLike, SpeciesLike + from typing_extensions import Self FileFormats = Literal["cif", "poscar", "cssr", "json", "yaml", "yml", "xsf", "mcsqs", "res", "pwmat", ""] StructureSources = Literal["Materials Project", "COD"] @@ -829,7 +827,6 @@ def _relax( from ase.constraints import ExpCellFilter from ase.io import read from ase.optimize.optimize import Optimizer - from pymatgen.io.ase import AseAtomsAdaptor opt_kwargs = opt_kwargs or {} diff --git a/src/pymatgen/core/surface.py b/src/pymatgen/core/surface.py index 7e8e969e405..f59d3a3cbc0 100644 --- a/src/pymatgen/core/surface.py +++ b/src/pymatgen/core/surface.py @@ -27,26 +27,24 @@ import numpy as np from monty.fractions import lcm -from scipy.cluster.hierarchy import fcluster, linkage -from scipy.spatial.distance import squareform - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Lattice, PeriodicSite, Structure, get_el_sp from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.coord import in_coord_list from pymatgen.util.due import Doi, due from pymatgen.util.typing import Tuple3Ints +from scipy.cluster.hierarchy import fcluster, linkage +from scipy.spatial.distance import squareform if TYPE_CHECKING: from collections.abc import Sequence from typing import Any from numpy.typing import ArrayLike, NDArray - from typing_extensions import Self - from pymatgen.core.composition import Element, Species from pymatgen.symmetry.groups import CrystalSystem from pymatgen.util.typing import MillerIndex + from typing_extensions import Self __author__ = "Richard Tran, Wenhao Sun, Zihan Xu, Shyue Ping Ong" diff --git a/src/pymatgen/core/tensors.py b/src/pymatgen/core/tensors.py index cb7234cc6ed..feacea08c43 100644 --- a/src/pymatgen/core/tensors.py +++ b/src/pymatgen/core/tensors.py @@ -15,21 +15,19 @@ import numpy as np from monty.json import MSONable from monty.serialization import loadfn -from scipy.linalg import polar - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core.lattice import Lattice from pymatgen.core.operations import SymmOp from pymatgen.symmetry.analyzer import SpacegroupAnalyzer +from scipy.linalg import polar if TYPE_CHECKING: from collections.abc import Sequence from typing import Any from numpy.typing import NDArray - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self __author__ = "Joseph Montoya" __credits__ = "Maarten de Jong, Shyam Dwaraknath, Wei Chen, Mark Asta, Anubhav Jain, Terence Lew" diff --git a/src/pymatgen/core/trajectory.py b/src/pymatgen/core/trajectory.py index ad595bb0e45..1d50e2a8df5 100644 --- a/src/pymatgen/core/trajectory.py +++ b/src/pymatgen/core/trajectory.py @@ -13,7 +13,6 @@ import numpy as np from monty.io import zopen from monty.json import MSONable - from pymatgen.core.structure import Composition, DummySpecies, Element, Lattice, Molecule, Species, Structure from pymatgen.io.ase import AseAtomsAdaptor @@ -21,9 +20,8 @@ from collections.abc import Iterator from typing import Any - from typing_extensions import Self - from pymatgen.util.typing import Matrix3D, PathLike, SitePropsType, Vector3D + from typing_extensions import Self __author__ = "Eric Sivonxay, Shyam Dwaraknath, Mingjian Wen, Evan Spotte-Smith" diff --git a/src/pymatgen/core/xcfunc.py b/src/pymatgen/core/xcfunc.py index a17d9c939fb..4b2e46d7376 100644 --- a/src/pymatgen/core/xcfunc.py +++ b/src/pymatgen/core/xcfunc.py @@ -6,7 +6,6 @@ from monty.functools import lazy_property from monty.json import MSONable - from pymatgen.core.libxcfunc import LibxcFunc if TYPE_CHECKING: diff --git a/src/pymatgen/electronic_structure/bandstructure.py b/src/pymatgen/electronic_structure/bandstructure.py index 73378506807..7f6e304b51b 100644 --- a/src/pymatgen/electronic_structure/bandstructure.py +++ b/src/pymatgen/electronic_structure/bandstructure.py @@ -11,7 +11,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.core import Element, Lattice, Structure, get_el_sp from pymatgen.electronic_structure.core import Orbital, Spin from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/electronic_structure/boltztrap.py b/src/pymatgen/electronic_structure/boltztrap.py index fe4d8ce2649..980ccb4f1a7 100644 --- a/src/pymatgen/electronic_structure/boltztrap.py +++ b/src/pymatgen/electronic_structure/boltztrap.py @@ -31,10 +31,6 @@ from monty.dev import requires from monty.json import MSONable, jsanitize from monty.os import cd -from scipy import constants -from scipy.optimize import fsolve -from scipy.spatial import distance - from pymatgen.core.lattice import Lattice from pymatgen.core.units import Energy, Length from pymatgen.electronic_structure.bandstructure import BandStructureSymmLine, Kpoint @@ -42,15 +38,17 @@ from pymatgen.electronic_structure.dos import CompleteDos, Dos, Spin from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.symmetry.bandstructure import HighSymmKpath +from scipy import constants +from scipy.optimize import fsolve +from scipy.spatial import distance if TYPE_CHECKING: from typing import Literal from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core.sites import PeriodicSite from pymatgen.core.structure import Structure + from typing_extensions import Self __author__ = "Geoffroy Hautier, Zachary Gibbs, Francesco Ricci, Anubhav Jain" __copyright__ = "Copyright 2013, The Materials Project" diff --git a/src/pymatgen/electronic_structure/boltztrap2.py b/src/pymatgen/electronic_structure/boltztrap2.py index 13d7891629e..41b622827f9 100644 --- a/src/pymatgen/electronic_structure/boltztrap2.py +++ b/src/pymatgen/electronic_structure/boltztrap2.py @@ -32,8 +32,6 @@ import matplotlib.pyplot as plt import numpy as np from monty.serialization import dumpfn, loadfn -from tqdm import tqdm - from pymatgen.electronic_structure.bandstructure import BandStructure, BandStructureSymmLine, Spin from pymatgen.electronic_structure.boltztrap import BoltztrapError from pymatgen.electronic_structure.dos import CompleteDos, Dos, Orbital @@ -41,6 +39,7 @@ from pymatgen.io.ase import AseAtomsAdaptor from pymatgen.io.vasp import Vasprun from pymatgen.symmetry.bandstructure import HighSymmKpath +from tqdm import tqdm if TYPE_CHECKING: from pathlib import Path diff --git a/src/pymatgen/electronic_structure/cohp.py b/src/pymatgen/electronic_structure/cohp.py index acf4f8f45b5..2b5e9c1cd2d 100644 --- a/src/pymatgen/electronic_structure/cohp.py +++ b/src/pymatgen/electronic_structure/cohp.py @@ -19,8 +19,6 @@ import numpy as np from monty.json import MSONable -from scipy.interpolate import InterpolatedUnivariateSpline - from pymatgen.core.sites import PeriodicSite from pymatgen.core.structure import Structure from pymatgen.electronic_structure.core import Orbital, Spin @@ -29,15 +27,15 @@ from pymatgen.util.coord import get_linear_interpolated_value from pymatgen.util.due import Doi, due from pymatgen.util.num import round_to_sigfigs +from scipy.interpolate import InterpolatedUnivariateSpline if TYPE_CHECKING: from collections.abc import Sequence from typing import Any, Literal from numpy.typing import NDArray - from typing_extensions import Self - from pymatgen.util.typing import PathLike, SpinLike, Vector3D + from typing_extensions import Self __author__ = "Marco Esters, Janine George" __copyright__ = "Copyright 2017, The Materials Project" diff --git a/src/pymatgen/electronic_structure/core.py b/src/pymatgen/electronic_structure/core.py index cfac056dd42..e513ee5c62e 100644 --- a/src/pymatgen/electronic_structure/core.py +++ b/src/pymatgen/electronic_structure/core.py @@ -14,9 +14,8 @@ from collections.abc import Sequence from numpy.typing import NDArray - from typing_extensions import Self - from pymatgen.core import Lattice + from typing_extensions import Self @unique diff --git a/src/pymatgen/electronic_structure/dos.py b/src/pymatgen/electronic_structure/dos.py index 3f1ed3feebe..07e42c998b3 100644 --- a/src/pymatgen/electronic_structure/dos.py +++ b/src/pymatgen/electronic_structure/dos.py @@ -8,23 +8,21 @@ import numpy as np from monty.json import MSONable -from scipy.constants import value as _cd -from scipy.ndimage import gaussian_filter1d -from scipy.signal import hilbert - from pymatgen.core import Structure, get_el_sp from pymatgen.core.spectrum import Spectrum from pymatgen.electronic_structure.core import Orbital, OrbitalType, Spin from pymatgen.util.coord import get_linear_interpolated_value +from scipy.constants import value as _cd +from scipy.ndimage import gaussian_filter1d +from scipy.signal import hilbert if TYPE_CHECKING: from collections.abc import Mapping from numpy.typing import ArrayLike, NDArray - from typing_extensions import Self - from pymatgen.core.sites import PeriodicSite from pymatgen.util.typing import SpeciesLike, Tuple3Floats + from typing_extensions import Self class DOS(Spectrum): diff --git a/src/pymatgen/electronic_structure/plotter.py b/src/pymatgen/electronic_structure/plotter.py index d7737e80a32..65709fb4cb8 100644 --- a/src/pymatgen/electronic_structure/plotter.py +++ b/src/pymatgen/electronic_structure/plotter.py @@ -20,7 +20,6 @@ from matplotlib.gridspec import GridSpec from monty.dev import requires from monty.json import jsanitize - from pymatgen.core import Element from pymatgen.electronic_structure.bandstructure import BandStructureSymmLine from pymatgen.electronic_structure.boltztrap import BoltztrapError @@ -37,7 +36,6 @@ from typing import Literal from numpy.typing import ArrayLike - from pymatgen.electronic_structure.dos import CompleteDos, Dos logger = logging.getLogger(__name__) diff --git a/src/pymatgen/entries/__init__.py b/src/pymatgen/entries/__init__.py index 51e6d6cee5a..fd0471b8416 100644 --- a/src/pymatgen/entries/__init__.py +++ b/src/pymatgen/entries/__init__.py @@ -12,7 +12,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.core.composition import Composition if TYPE_CHECKING: diff --git a/src/pymatgen/entries/compatibility.py b/src/pymatgen/entries/compatibility.py index 8108b788b7b..ba2ef68dd07 100644 --- a/src/pymatgen/entries/compatibility.py +++ b/src/pymatgen/entries/compatibility.py @@ -15,9 +15,6 @@ from monty.design_patterns import cached_class from monty.json import MSONable from monty.serialization import loadfn -from tqdm import tqdm -from uncertainties import ufloat - from pymatgen.analysis.structure_analyzer import oxide_type, sulfide_type from pymatgen.core import SETTINGS, Composition, Element from pymatgen.entries.computed_entries import ( @@ -30,6 +27,8 @@ ) from pymatgen.io.vasp.sets import MITRelaxSet, MPRelaxSet, VaspInputSet from pymatgen.util.due import Doi, due +from tqdm import tqdm +from uncertainties import ufloat if TYPE_CHECKING: from collections.abc import Sequence diff --git a/src/pymatgen/entries/computed_entries.py b/src/pymatgen/entries/computed_entries.py index d17eb36bbe6..b80a45f8b29 100644 --- a/src/pymatgen/entries/computed_entries.py +++ b/src/pymatgen/entries/computed_entries.py @@ -17,19 +17,17 @@ import numpy as np from monty.json import MontyDecoder, MontyEncoder, MSONable -from scipy.interpolate import interp1d -from uncertainties import ufloat - from pymatgen.core.composition import Composition from pymatgen.entries import Entry from pymatgen.util.due import Doi, due +from scipy.interpolate import interp1d +from uncertainties import ufloat if TYPE_CHECKING: from typing import Literal - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self __author__ = "Ryan Kingsbury, Matt McDermott, Shyue Ping Ong, Anubhav Jain" __copyright__ = "Copyright 2011-2020, The Materials Project" diff --git a/src/pymatgen/entries/correction_calculator.py b/src/pymatgen/entries/correction_calculator.py index e832a0576c0..778c3885752 100644 --- a/src/pymatgen/entries/correction_calculator.py +++ b/src/pymatgen/entries/correction_calculator.py @@ -10,12 +10,11 @@ import numpy as np import plotly.graph_objects as go from monty.serialization import loadfn -from ruamel import yaml -from scipy.optimize import curve_fit - from pymatgen.analysis.reaction_calculator import ComputedReaction from pymatgen.analysis.structure_analyzer import sulfide_type from pymatgen.core import Composition, Element +from ruamel import yaml +from scipy.optimize import curve_fit class CorrectionCalculator: diff --git a/src/pymatgen/entries/entry_tools.py b/src/pymatgen/entries/entry_tools.py index 6fe98612ca5..fc6eb821891 100644 --- a/src/pymatgen/entries/entry_tools.py +++ b/src/pymatgen/entries/entry_tools.py @@ -16,7 +16,6 @@ from typing import TYPE_CHECKING from monty.json import MontyDecoder, MontyEncoder, MSONable - from pymatgen.analysis.phase_diagram import PDEntry from pymatgen.analysis.structure_matcher import SpeciesComparator, StructureMatcher from pymatgen.core import Composition, Element @@ -25,10 +24,9 @@ from collections.abc import Iterable from typing import Literal - from typing_extensions import Self - from pymatgen.entries import Entry from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry + from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/src/pymatgen/entries/exp_entries.py b/src/pymatgen/entries/exp_entries.py index e012ed7a9fb..78adad7a6ef 100644 --- a/src/pymatgen/entries/exp_entries.py +++ b/src/pymatgen/entries/exp_entries.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING from monty.json import MSONable - from pymatgen.analysis.phase_diagram import PDEntry from pymatgen.analysis.thermochemistry import ThermoData from pymatgen.core.composition import Composition diff --git a/src/pymatgen/entries/mixing_scheme.py b/src/pymatgen/entries/mixing_scheme.py index 8f8fb637ab5..8a4a50fb214 100644 --- a/src/pymatgen/entries/mixing_scheme.py +++ b/src/pymatgen/entries/mixing_scheme.py @@ -11,7 +11,6 @@ import numpy as np import pandas as pd - from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.entries.compatibility import ( diff --git a/src/pymatgen/ext/cod.py b/src/pymatgen/ext/cod.py index 21983b6891f..0bc9966c2dc 100644 --- a/src/pymatgen/ext/cod.py +++ b/src/pymatgen/ext/cod.py @@ -34,7 +34,6 @@ import requests from monty.dev import requires - from pymatgen.core.composition import Composition from pymatgen.core.structure import Structure diff --git a/src/pymatgen/ext/matproj.py b/src/pymatgen/ext/matproj.py index a7c4318a440..71a4cff7a7a 100644 --- a/src/pymatgen/ext/matproj.py +++ b/src/pymatgen/ext/matproj.py @@ -20,7 +20,6 @@ import requests from monty.json import MontyDecoder - from pymatgen.core import SETTINGS from pymatgen.core import __version__ as PMG_VERSION from pymatgen.symmetry.analyzer import SpacegroupAnalyzer @@ -29,11 +28,10 @@ from typing import Callable from mp_api.client import MPRester as _MPResterNew - from typing_extensions import Self - from pymatgen.core.structure import Structure from pymatgen.entries.computed_entries import ComputedStructureEntry from pymatgen.ext.matproj_legacy import _MPResterLegacy + from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/src/pymatgen/ext/matproj_legacy.py b/src/pymatgen/ext/matproj_legacy.py index 26b28e3ed42..ff3ea075ec3 100644 --- a/src/pymatgen/ext/matproj_legacy.py +++ b/src/pymatgen/ext/matproj_legacy.py @@ -20,9 +20,6 @@ import requests from monty.json import MontyDecoder, MontyEncoder -from ruamel.yaml import YAML -from tqdm import tqdm - from pymatgen.core import SETTINGS, Composition, Element, Structure from pymatgen.core import __version__ as PMG_VERSION from pymatgen.core.surface import get_symmetrically_equivalent_miller_indices @@ -31,15 +28,16 @@ from pymatgen.entries.exp_entries import ExpEntry from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.due import Doi, due +from ruamel.yaml import YAML +from tqdm import tqdm if TYPE_CHECKING: from collections.abc import Sequence from typing import Any, Literal - from typing_extensions import Self - from pymatgen.phonon.bandstructure import PhononBandStructureSymmLine from pymatgen.phonon.dos import CompletePhononDos + from typing_extensions import Self logger = logging.getLogger(__name__) MP_LOG_FILE = os.path.join(os.path.expanduser("~"), ".mprester.log.yaml") diff --git a/src/pymatgen/ext/optimade.py b/src/pymatgen/ext/optimade.py index 2f5c1345429..fa7e9fb3d3c 100644 --- a/src/pymatgen/ext/optimade.py +++ b/src/pymatgen/ext/optimade.py @@ -8,11 +8,10 @@ from urllib.parse import urljoin, urlparse import requests -from tqdm import tqdm - from pymatgen.core import DummySpecies, Structure from pymatgen.util.due import Doi, due from pymatgen.util.provenance import StructureNL +from tqdm import tqdm if TYPE_CHECKING: from typing import ClassVar diff --git a/src/pymatgen/io/abinit/abiobjects.py b/src/pymatgen/io/abinit/abiobjects.py index 2057f79543f..46482f44b3a 100644 --- a/src/pymatgen/io/abinit/abiobjects.py +++ b/src/pymatgen/io/abinit/abiobjects.py @@ -13,7 +13,6 @@ from monty.collections import AttrDict from monty.design_patterns import singleton from monty.json import MontyDecoder, MontyEncoder, MSONable - from pymatgen.core import ArrayWithUnit, Lattice, Species, Structure, units if TYPE_CHECKING: diff --git a/src/pymatgen/io/abinit/abitimer.py b/src/pymatgen/io/abinit/abitimer.py index 036d8ef1911..ceb86f03207 100644 --- a/src/pymatgen/io/abinit/abitimer.py +++ b/src/pymatgen/io/abinit/abitimer.py @@ -14,7 +14,6 @@ import numpy as np import pandas as pd from matplotlib.gridspec import GridSpec - from pymatgen.io.core import ParseError from pymatgen.util.plotting import add_fig_kwargs, get_ax_fig diff --git a/src/pymatgen/io/abinit/inputs.py b/src/pymatgen/io/abinit/inputs.py index 180fabff915..1382f819d7a 100644 --- a/src/pymatgen/io/abinit/inputs.py +++ b/src/pymatgen/io/abinit/inputs.py @@ -18,7 +18,6 @@ import numpy as np from monty.collections import AttrDict from monty.json import MSONable - from pymatgen.core.structure import Structure from pymatgen.io.abinit import abiobjects as aobj from pymatgen.io.abinit.pseudos import Pseudo, PseudoTable diff --git a/src/pymatgen/io/abinit/netcdf.py b/src/pymatgen/io/abinit/netcdf.py index fbde8ef1477..8c7538e46c8 100644 --- a/src/pymatgen/io/abinit/netcdf.py +++ b/src/pymatgen/io/abinit/netcdf.py @@ -12,7 +12,6 @@ from monty.dev import requires from monty.functools import lazy_property from monty.string import marquee - from pymatgen.core.structure import Structure from pymatgen.core.units import ArrayWithUnit from pymatgen.core.xcfunc import XcFunc diff --git a/src/pymatgen/io/abinit/pseudos.py b/src/pymatgen/io/abinit/pseudos.py index 5a56b93e1d3..b0927fe0484 100644 --- a/src/pymatgen/io/abinit/pseudos.py +++ b/src/pymatgen/io/abinit/pseudos.py @@ -24,12 +24,11 @@ from monty.itertools import iterator_from_slice from monty.json import MontyDecoder, MSONable from monty.os.path import find_exts -from tabulate import tabulate - from pymatgen.core import Element from pymatgen.core.xcfunc import XcFunc from pymatgen.io.core import ParseError from pymatgen.util.plotting import add_fig_kwargs, get_ax_fig +from tabulate import tabulate if TYPE_CHECKING: from collections.abc import Iterator, Sequence @@ -37,9 +36,8 @@ import matplotlib.pyplot as plt from numpy.typing import NDArray - from typing_extensions import Self - from pymatgen.core import Structure + from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/src/pymatgen/io/adf.py b/src/pymatgen/io/adf.py index 865493b697f..59a3d8bfabc 100644 --- a/src/pymatgen/io/adf.py +++ b/src/pymatgen/io/adf.py @@ -10,7 +10,6 @@ from monty.itertools import chunks from monty.json import MSONable from monty.serialization import zopen - from pymatgen.core.structure import Molecule if TYPE_CHECKING: diff --git a/src/pymatgen/io/aims/inputs.py b/src/pymatgen/io/aims/inputs.py index 489de5390ec..342c01b40d0 100644 --- a/src/pymatgen/io/aims/inputs.py +++ b/src/pymatgen/io/aims/inputs.py @@ -17,16 +17,14 @@ from monty.io import zopen from monty.json import MontyDecoder, MSONable from monty.os.path import zpath - from pymatgen.core import SETTINGS, Element, Lattice, Molecule, Structure if TYPE_CHECKING: from collections.abc import Sequence from typing import Any - from typing_extensions import Self - from pymatgen.util.typing import Tuple3Floats, Tuple3Ints + from typing_extensions import Self __author__ = "Thomas A. R. Purcell" __version__ = "1.0" diff --git a/src/pymatgen/io/aims/outputs.py b/src/pymatgen/io/aims/outputs.py index 45f680f6d84..f6012472ac5 100644 --- a/src/pymatgen/io/aims/outputs.py +++ b/src/pymatgen/io/aims/outputs.py @@ -6,7 +6,6 @@ import numpy as np from monty.json import MontyDecoder, MSONable - from pymatgen.io.aims.parsers import ( read_aims_header_info, read_aims_header_info_from_content, @@ -19,10 +18,9 @@ from pathlib import Path from typing import Any - from typing_extensions import Self - from pymatgen.core import Molecule, Structure from pymatgen.util.typing import Matrix3D, Vector3D + from typing_extensions import Self __author__ = "Andrey Sobolev and Thomas A. R. Purcell" __version__ = "1.0" diff --git a/src/pymatgen/io/aims/parsers.py b/src/pymatgen/io/aims/parsers.py index 005a78f8aeb..3021c49279c 100644 --- a/src/pymatgen/io/aims/parsers.py +++ b/src/pymatgen/io/aims/parsers.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, cast import numpy as np - from pymatgen.core import Lattice, Molecule, Structure from pymatgen.core.tensors import Tensor from pymatgen.util.typing import Tuple3Floats diff --git a/src/pymatgen/io/aims/sets/base.py b/src/pymatgen/io/aims/sets/base.py index 52ceed9b182..c15385d9a2e 100644 --- a/src/pymatgen/io/aims/sets/base.py +++ b/src/pymatgen/io/aims/sets/base.py @@ -12,7 +12,6 @@ import numpy as np from monty.json import MontyDecoder, MontyEncoder - from pymatgen.core import Molecule, Structure from pymatgen.io.aims.inputs import AimsControlIn, AimsGeometryIn from pymatgen.io.aims.parsers import AimsParseError, read_aims_output diff --git a/src/pymatgen/io/ase.py b/src/pymatgen/io/ase.py index 7058a9d2eee..bae2dbc5a62 100644 --- a/src/pymatgen/io/ase.py +++ b/src/pymatgen/io/ase.py @@ -12,7 +12,6 @@ import numpy as np from monty.json import MontyDecoder, MSONable, jsanitize - from pymatgen.core.structure import Molecule, Structure try: @@ -36,9 +35,8 @@ def __init__(self, *args, **kwargs): from typing import Any from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core.structure import SiteCollection + from typing_extensions import Self __author__ = "Shyue Ping Ong, Andrew S. Rosen" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/src/pymatgen/io/atat.py b/src/pymatgen/io/atat.py index 7badcab828c..98660051d6b 100644 --- a/src/pymatgen/io/atat.py +++ b/src/pymatgen/io/atat.py @@ -3,7 +3,6 @@ from __future__ import annotations import numpy as np - from pymatgen.core import Lattice, Structure, get_el_sp __author__ = "Matthew Horton" diff --git a/src/pymatgen/io/babel.py b/src/pymatgen/io/babel.py index 0d0e19871c4..a648a783831 100644 --- a/src/pymatgen/io/babel.py +++ b/src/pymatgen/io/babel.py @@ -11,7 +11,6 @@ from typing import TYPE_CHECKING from monty.dev import requires - from pymatgen.core.structure import IMolecule, Molecule try: @@ -20,9 +19,8 @@ openbabel = pybel = None if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.analysis.graphs import MoleculeGraph + from typing_extensions import Self __author__ = "Shyue Ping Ong, Qi Wang" diff --git a/src/pymatgen/io/cif.py b/src/pymatgen/io/cif.py index 6b0a2453a71..5020d66d83b 100644 --- a/src/pymatgen/io/cif.py +++ b/src/pymatgen/io/cif.py @@ -19,7 +19,6 @@ from monty.dev import deprecated from monty.io import zopen from monty.serialization import loadfn - from pymatgen.core import Composition, DummySpecies, Element, Lattice, PeriodicSite, Species, Structure, get_el_sp from pymatgen.core.operations import MagSymmOp, SymmOp from pymatgen.electronic_structure.core import Magmom @@ -33,9 +32,8 @@ from typing import Any from numpy.typing import NDArray - from typing_extensions import Self - from pymatgen.util.typing import PathLike, Vector3D + from typing_extensions import Self __author__ = "Shyue Ping Ong, Will Richards, Matthew Horton" diff --git a/src/pymatgen/io/common.py b/src/pymatgen/io/common.py index 00a0cac7932..0949bf08306 100644 --- a/src/pymatgen/io/common.py +++ b/src/pymatgen/io/common.py @@ -11,11 +11,10 @@ import numpy as np from monty.io import zopen from monty.json import MSONable -from scipy.interpolate import RegularGridInterpolator - from pymatgen.core import Element, Site, Structure from pymatgen.core.units import ang_to_bohr, bohr_to_angstrom from pymatgen.electronic_structure.core import Spin +from scipy.interpolate import RegularGridInterpolator if TYPE_CHECKING: from pathlib import Path diff --git a/src/pymatgen/io/core.py b/src/pymatgen/io/core.py index 1392e8a564e..c4c944706eb 100644 --- a/src/pymatgen/io/core.py +++ b/src/pymatgen/io/core.py @@ -37,9 +37,8 @@ from monty.json import MSONable if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.util.typing import PathLike + from typing_extensions import Self __author__ = "Ryan Kingsbury" diff --git a/src/pymatgen/io/cp2k/inputs.py b/src/pymatgen/io/cp2k/inputs.py index 8076e51d09c..83054d4380b 100644 --- a/src/pymatgen/io/cp2k/inputs.py +++ b/src/pymatgen/io/cp2k/inputs.py @@ -37,7 +37,6 @@ from monty.dev import deprecated from monty.io import zopen from monty.json import MSONable - from pymatgen.core import Element from pymatgen.io.cp2k.utils import chunk, postprocessor, preprocessor from pymatgen.io.vasp.inputs import Kpoints as VaspKpoints @@ -48,11 +47,10 @@ from collections.abc import Sequence from typing import Any, Literal - from typing_extensions import Self - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Molecule, Structure from pymatgen.util.typing import Kpoint, Tuple3Ints + from typing_extensions import Self __author__ = "Nicholas Winner" __version__ = "2.0" diff --git a/src/pymatgen/io/cp2k/outputs.py b/src/pymatgen/io/cp2k/outputs.py index 19c63761eba..0fecc2bd98b 100644 --- a/src/pymatgen/io/cp2k/outputs.py +++ b/src/pymatgen/io/cp2k/outputs.py @@ -17,7 +17,6 @@ from monty.io import zopen from monty.json import MSONable, jsanitize from monty.re import regrep - from pymatgen.core.structure import Molecule, Structure from pymatgen.core.units import Ha_to_eV from pymatgen.electronic_structure.bandstructure import BandStructure, BandStructureSymmLine diff --git a/src/pymatgen/io/cp2k/sets.py b/src/pymatgen/io/cp2k/sets.py index d6fc950a583..9af72d01594 100644 --- a/src/pymatgen/io/cp2k/sets.py +++ b/src/pymatgen/io/cp2k/sets.py @@ -24,8 +24,6 @@ import warnings import numpy as np -from ruamel.yaml import YAML - from pymatgen.core import SETTINGS from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Element, Molecule, Structure @@ -67,6 +65,7 @@ from pymatgen.io.cp2k.utils import get_truncated_coulomb_cutoff, get_unique_site_indices from pymatgen.io.vasp.inputs import Kpoints as VaspKpoints from pymatgen.io.vasp.inputs import KpointsSupportedModes +from ruamel.yaml import YAML __author__ = "Nicholas Winner" __version__ = "2.0" diff --git a/src/pymatgen/io/cssr.py b/src/pymatgen/io/cssr.py index 97631f3bf28..52d02996815 100644 --- a/src/pymatgen/io/cssr.py +++ b/src/pymatgen/io/cssr.py @@ -6,7 +6,6 @@ from typing import TYPE_CHECKING from monty.io import zopen - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure diff --git a/src/pymatgen/io/exciting/inputs.py b/src/pymatgen/io/exciting/inputs.py index 13f280c8fa0..2a61fa77184 100644 --- a/src/pymatgen/io/exciting/inputs.py +++ b/src/pymatgen/io/exciting/inputs.py @@ -10,7 +10,6 @@ import scipy.constants as const from monty.io import zopen from monty.json import MSONable - from pymatgen.core import Element, Lattice, Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.symmetry.bandstructure import HighSymmKpath diff --git a/src/pymatgen/io/feff/inputs.py b/src/pymatgen/io/feff/inputs.py index 5a3ed16ac35..52e95a14521 100644 --- a/src/pymatgen/io/feff/inputs.py +++ b/src/pymatgen/io/feff/inputs.py @@ -15,14 +15,13 @@ import numpy as np from monty.io import zopen from monty.json import MSONable -from tabulate import tabulate - from pymatgen.core import Element, Lattice, Molecule, Structure from pymatgen.io.cif import CifParser from pymatgen.io.core import ParseError from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.io_utils import clean_lines from pymatgen.util.string import str_delimited +from tabulate import tabulate if TYPE_CHECKING: from typing_extensions import Self diff --git a/src/pymatgen/io/feff/outputs.py b/src/pymatgen/io/feff/outputs.py index 133a28c7821..4337ee78741 100644 --- a/src/pymatgen/io/feff/outputs.py +++ b/src/pymatgen/io/feff/outputs.py @@ -13,7 +13,6 @@ import numpy as np from monty.io import zopen from monty.json import MSONable - from pymatgen.core import Element from pymatgen.electronic_structure.core import Orbital, Spin from pymatgen.electronic_structure.dos import CompleteDos, Dos diff --git a/src/pymatgen/io/feff/sets.py b/src/pymatgen/io/feff/sets.py index 55ddd7fd588..4231f1772ad 100644 --- a/src/pymatgen/io/feff/sets.py +++ b/src/pymatgen/io/feff/sets.py @@ -20,7 +20,6 @@ from monty.json import MSONable from monty.os.path import zpath from monty.serialization import loadfn - from pymatgen.core.structure import Molecule, Structure from pymatgen.io.feff.inputs import Atoms, Header, Potential, Tags diff --git a/src/pymatgen/io/fiesta.py b/src/pymatgen/io/fiesta.py index 45ad8954c72..dc75893c37a 100644 --- a/src/pymatgen/io/fiesta.py +++ b/src/pymatgen/io/fiesta.py @@ -17,15 +17,13 @@ from monty.io import zopen from monty.json import MSONable - from pymatgen.core.structure import Molecule if TYPE_CHECKING: from pathlib import Path - from typing_extensions import Self - from pymatgen.util.typing import Tuple3Ints + from typing_extensions import Self __author__ = "ndardenne" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/src/pymatgen/io/gaussian.py b/src/pymatgen/io/gaussian.py index ca150bab18a..36102e6c334 100644 --- a/src/pymatgen/io/gaussian.py +++ b/src/pymatgen/io/gaussian.py @@ -9,14 +9,13 @@ import numpy as np import scipy.constants as cst from monty.io import zopen -from scipy.stats import norm - from pymatgen.core import Composition, Element, Molecule from pymatgen.core.operations import SymmOp from pymatgen.core.units import Ha_to_eV from pymatgen.electronic_structure.core import Spin from pymatgen.util.coord import get_angle from pymatgen.util.plotting import pretty_plot +from scipy.stats import norm if TYPE_CHECKING: from pathlib import Path diff --git a/src/pymatgen/io/lammps/data.py b/src/pymatgen/io/lammps/data.py index 69d9d834853..46ac9b97765 100644 --- a/src/pymatgen/io/lammps/data.py +++ b/src/pymatgen/io/lammps/data.py @@ -27,20 +27,18 @@ from monty.io import zopen from monty.json import MSONable from monty.serialization import loadfn -from ruamel.yaml import YAML - from pymatgen.core import Element, Lattice, Molecule, Structure from pymatgen.core.operations import SymmOp from pymatgen.util.io_utils import clean_lines +from ruamel.yaml import YAML if TYPE_CHECKING: from collections.abc import Sequence from typing import Any, Literal - from typing_extensions import Self - from pymatgen.core.sites import Site from pymatgen.core.structure import SiteCollection + from typing_extensions import Self __author__ = "Kiran Mathew, Zhi Deng, Tingzheng Hou" __copyright__ = "Copyright 2018, The Materials Virtual Lab" diff --git a/src/pymatgen/io/lammps/generators.py b/src/pymatgen/io/lammps/generators.py index afc2f39b5dc..5e1c0a30950 100644 --- a/src/pymatgen/io/lammps/generators.py +++ b/src/pymatgen/io/lammps/generators.py @@ -15,7 +15,6 @@ from string import Template from monty.io import zopen - from pymatgen.core import Structure from pymatgen.io.core import InputGenerator from pymatgen.io.lammps.data import CombinedData, LammpsData diff --git a/src/pymatgen/io/lammps/inputs.py b/src/pymatgen/io/lammps/inputs.py index 565feea80bf..5ea8477d5fd 100644 --- a/src/pymatgen/io/lammps/inputs.py +++ b/src/pymatgen/io/lammps/inputs.py @@ -19,7 +19,6 @@ from monty.dev import deprecated from monty.io import zopen from monty.json import MSONable - from pymatgen.core import __version__ as CURRENT_VER from pymatgen.io.core import InputFile from pymatgen.io.lammps.data import CombinedData, LammpsData @@ -28,9 +27,8 @@ if TYPE_CHECKING: from os import PathLike - from typing_extensions import Self - from pymatgen.io.core import InputSet + from typing_extensions import Self __author__ = "Kiran Mathew, Brandon Wood, Zhi Deng, Manas Likhit, Guillaume Brunin (Matgenix)" __copyright__ = "Copyright 2018, The Materials Virtual Lab" diff --git a/src/pymatgen/io/lammps/outputs.py b/src/pymatgen/io/lammps/outputs.py index 033a7d58b4a..b8e9688c06d 100644 --- a/src/pymatgen/io/lammps/outputs.py +++ b/src/pymatgen/io/lammps/outputs.py @@ -14,7 +14,6 @@ import pandas as pd from monty.io import zopen from monty.json import MSONable - from pymatgen.io.lammps.data import LammpsBox if TYPE_CHECKING: diff --git a/src/pymatgen/io/lammps/sets.py b/src/pymatgen/io/lammps/sets.py index d61510f3d67..54e63afd5f2 100644 --- a/src/pymatgen/io/lammps/sets.py +++ b/src/pymatgen/io/lammps/sets.py @@ -18,9 +18,8 @@ from pymatgen.io.lammps.inputs import LammpsInputFile if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.util.typing import PathLike + from typing_extensions import Self __author__ = "Ryan Kingsbury, Guillaume Brunin (Matgenix)" __copyright__ = "Copyright 2021, The Materials Project" diff --git a/src/pymatgen/io/lammps/utils.py b/src/pymatgen/io/lammps/utils.py index 2ccca0ea2e0..cc8537d8af6 100644 --- a/src/pymatgen/io/lammps/utils.py +++ b/src/pymatgen/io/lammps/utils.py @@ -11,7 +11,6 @@ import numpy as np from monty.dev import deprecated from monty.tempfile import ScratchDir - from pymatgen.core.operations import SymmOp from pymatgen.core.structure import Molecule from pymatgen.io.babel import BabelMolAdaptor diff --git a/src/pymatgen/io/lmto.py b/src/pymatgen/io/lmto.py index 747a65ca42e..5c6057d8190 100644 --- a/src/pymatgen/io/lmto.py +++ b/src/pymatgen/io/lmto.py @@ -11,7 +11,6 @@ import numpy as np from monty.io import zopen - from pymatgen.core.structure import Structure from pymatgen.core.units import Ry_to_eV, bohr_to_angstrom from pymatgen.electronic_structure.core import Spin diff --git a/src/pymatgen/io/lobster/inputs.py b/src/pymatgen/io/lobster/inputs.py index 9eef7c654a0..f3759a38b93 100644 --- a/src/pymatgen/io/lobster/inputs.py +++ b/src/pymatgen/io/lobster/inputs.py @@ -22,7 +22,6 @@ from monty.io import zopen from monty.json import MSONable from monty.serialization import loadfn - from pymatgen.core.structure import Structure from pymatgen.io.vasp import Vasprun from pymatgen.io.vasp.inputs import Incar, Kpoints, Potcar @@ -32,10 +31,9 @@ if TYPE_CHECKING: from typing import Any, ClassVar, Literal - from typing_extensions import Self - from pymatgen.core.composition import Composition from pymatgen.util.typing import PathLike, Tuple3Ints + from typing_extensions import Self MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/src/pymatgen/io/lobster/lobsterenv.py b/src/pymatgen/io/lobster/lobsterenv.py index 919ea6bb678..7982b025c99 100644 --- a/src/pymatgen/io/lobster/lobsterenv.py +++ b/src/pymatgen/io/lobster/lobsterenv.py @@ -20,7 +20,6 @@ import numpy as np from monty.dev import deprecated - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import LocalGeometryFinder from pymatgen.analysis.chemenv.coordination_environments.structure_environments import LightStructureEnvironments @@ -32,10 +31,9 @@ from pymatgen.util.due import Doi, due if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.core import Structure from pymatgen.core.periodic_table import Element + from typing_extensions import Self __author__ = "Janine George" __copyright__ = "Copyright 2021, The Materials Project" diff --git a/src/pymatgen/io/lobster/outputs.py b/src/pymatgen/io/lobster/outputs.py index 31ebe16b04d..266e42096c5 100644 --- a/src/pymatgen/io/lobster/outputs.py +++ b/src/pymatgen/io/lobster/outputs.py @@ -22,7 +22,6 @@ import numpy as np from monty.io import zopen from monty.json import MSONable - from pymatgen.core.structure import Structure from pymatgen.electronic_structure.bandstructure import LobsterBandStructureSymmLine from pymatgen.electronic_structure.core import Orbital, Spin diff --git a/src/pymatgen/io/nwchem.py b/src/pymatgen/io/nwchem.py index 04686e878f7..8b39dcb40e3 100644 --- a/src/pymatgen/io/nwchem.py +++ b/src/pymatgen/io/nwchem.py @@ -29,7 +29,6 @@ import numpy as np from monty.io import zopen from monty.json import MSONable - from pymatgen.analysis.excitation import ExcitationSpectrum from pymatgen.core.structure import Molecule, Structure from pymatgen.core.units import Energy, FloatWithUnit diff --git a/src/pymatgen/io/openff.py b/src/pymatgen/io/openff.py index 37e610409c0..bef308811d5 100644 --- a/src/pymatgen/io/openff.py +++ b/src/pymatgen/io/openff.py @@ -6,7 +6,6 @@ from pathlib import Path import numpy as np - import pymatgen from pymatgen.analysis.graphs import MoleculeGraph from pymatgen.analysis.local_env import OpenBabelNN, metal_edge_extender diff --git a/src/pymatgen/io/packmol.py b/src/pymatgen/io/packmol.py index ed159029864..d1f1f7f6c6a 100644 --- a/src/pymatgen/io/packmol.py +++ b/src/pymatgen/io/packmol.py @@ -26,7 +26,6 @@ class that provides a run() method for running packmol locally. from typing import TYPE_CHECKING import numpy as np - from pymatgen.core import Molecule from pymatgen.io.core import InputGenerator, InputSet diff --git a/src/pymatgen/io/phonopy.py b/src/pymatgen/io/phonopy.py index d0d39f014e4..6e156b2e2e5 100644 --- a/src/pymatgen/io/phonopy.py +++ b/src/pymatgen/io/phonopy.py @@ -5,14 +5,13 @@ import numpy as np from monty.dev import requires from monty.serialization import loadfn -from scipy.interpolate import InterpolatedUnivariateSpline - from pymatgen.core import Lattice, Structure from pymatgen.phonon.bandstructure import PhononBandStructure, PhononBandStructureSymmLine from pymatgen.phonon.dos import CompletePhononDos, PhononDos from pymatgen.phonon.gruneisen import GruneisenParameter, GruneisenPhononBandStructureSymmLine from pymatgen.phonon.thermal_displacements import ThermalDisplacementMatrices from pymatgen.symmetry.bandstructure import HighSymmKpath +from scipy.interpolate import InterpolatedUnivariateSpline try: from phonopy import Phonopy diff --git a/src/pymatgen/io/pwmat/inputs.py b/src/pymatgen/io/pwmat/inputs.py index a0330270d00..1051405ffe9 100644 --- a/src/pymatgen/io/pwmat/inputs.py +++ b/src/pymatgen/io/pwmat/inputs.py @@ -8,14 +8,12 @@ import numpy as np from monty.io import zopen from monty.json import MSONable - from pymatgen.core import Lattice, Structure from pymatgen.symmetry.kpath import KPathSeek if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.util.typing import PathLike + from typing_extensions import Self __author__ = "Hanyu Liu" __email__ = "domainofbuaa@gmail.com" diff --git a/src/pymatgen/io/pwmat/outputs.py b/src/pymatgen/io/pwmat/outputs.py index 3daf32b57c0..0ebd966efd2 100644 --- a/src/pymatgen/io/pwmat/outputs.py +++ b/src/pymatgen/io/pwmat/outputs.py @@ -8,7 +8,6 @@ import numpy as np from monty.io import zopen from monty.json import MSONable - from pymatgen.io.pwmat.inputs import ACstrExtractor, AtomConfig, LineLocator if TYPE_CHECKING: diff --git a/src/pymatgen/io/pwscf.py b/src/pymatgen/io/pwscf.py index 73aa43f4a55..2af861b9941 100644 --- a/src/pymatgen/io/pwscf.py +++ b/src/pymatgen/io/pwscf.py @@ -8,7 +8,6 @@ from monty.io import zopen from monty.re import regrep - from pymatgen.core import Element, Lattice, Structure from pymatgen.util.io_utils import clean_lines diff --git a/src/pymatgen/io/qchem/inputs.py b/src/pymatgen/io/qchem/inputs.py index 27f2816103e..bb20603c8bd 100644 --- a/src/pymatgen/io/qchem/inputs.py +++ b/src/pymatgen/io/qchem/inputs.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING from monty.io import zopen - from pymatgen.core import Molecule from pymatgen.io.core import InputFile diff --git a/src/pymatgen/io/qchem/outputs.py b/src/pymatgen/io/qchem/outputs.py index c2cccd2ddc9..6db29b910ac 100644 --- a/src/pymatgen/io/qchem/outputs.py +++ b/src/pymatgen/io/qchem/outputs.py @@ -16,7 +16,6 @@ import pandas as pd from monty.io import zopen from monty.json import MSONable, jsanitize - from pymatgen.analysis.graphs import MoleculeGraph from pymatgen.analysis.local_env import OpenBabelNN from pymatgen.core import Molecule diff --git a/src/pymatgen/io/qchem/sets.py b/src/pymatgen/io/qchem/sets.py index 1b40d49fb4c..7be1ddbc67a 100644 --- a/src/pymatgen/io/qchem/sets.py +++ b/src/pymatgen/io/qchem/sets.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING from monty.io import zopen - from pymatgen.io.qchem.inputs import QCInput from pymatgen.io.qchem.utils import lower_and_check_unique diff --git a/src/pymatgen/io/res.py b/src/pymatgen/io/res.py index d43efbaaf00..6218392ed1e 100644 --- a/src/pymatgen/io/res.py +++ b/src/pymatgen/io/res.py @@ -17,7 +17,6 @@ from monty.io import zopen from monty.json import MSONable - from pymatgen.core import Element, Lattice, PeriodicSite, Structure from pymatgen.entries.computed_entries import ComputedStructureEntry from pymatgen.io.core import ParseError @@ -28,9 +27,8 @@ from pathlib import Path from typing import Any, Callable, Literal - from typing_extensions import Self - from pymatgen.util.typing import Tuple3Ints, Vector3D + from typing_extensions import Self @dataclass(frozen=True) diff --git a/src/pymatgen/io/shengbte.py b/src/pymatgen/io/shengbte.py index bd8009d93cb..7ccdee00c23 100644 --- a/src/pymatgen/io/shengbte.py +++ b/src/pymatgen/io/shengbte.py @@ -8,7 +8,6 @@ import numpy as np from monty.dev import requires from monty.json import MSONable - from pymatgen.core.structure import Structure from pymatgen.io.vasp import Kpoints diff --git a/src/pymatgen/io/template.py b/src/pymatgen/io/template.py index 2ee08031ede..6103f09d105 100644 --- a/src/pymatgen/io/template.py +++ b/src/pymatgen/io/template.py @@ -9,7 +9,6 @@ from typing import TYPE_CHECKING from monty.io import zopen - from pymatgen.io.core import InputGenerator, InputSet if TYPE_CHECKING: diff --git a/src/pymatgen/io/vasp/inputs.py b/src/pymatgen/io/vasp/inputs.py index cda9ff18cf6..25572a2b1ab 100644 --- a/src/pymatgen/io/vasp/inputs.py +++ b/src/pymatgen/io/vasp/inputs.py @@ -31,23 +31,21 @@ from monty.os import cd from monty.os.path import zpath from monty.serialization import dumpfn, loadfn -from tabulate import tabulate - from pymatgen.core import SETTINGS, Element, Lattice, Structure, get_el_sp from pymatgen.electronic_structure.core import Magmom from pymatgen.util.io_utils import clean_lines from pymatgen.util.string import str_delimited from pymatgen.util.typing import Kpoint, Tuple3Floats, Tuple3Ints, Vector3D +from tabulate import tabulate if TYPE_CHECKING: from collections.abc import Iterator from typing import Any, ClassVar, Literal from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.symmetry.bandstructure import HighSymmKpath from pymatgen.util.typing import PathLike + from typing_extensions import Self __author__ = "Shyue Ping Ong, Geoffroy Hautier, Rickard Armiento, Vincent L Chevrier, Stephen Dacek" diff --git a/src/pymatgen/io/vasp/optics.py b/src/pymatgen/io/vasp/optics.py index 9e1e1b66cf7..bc52f8ec197 100644 --- a/src/pymatgen/io/vasp/optics.py +++ b/src/pymatgen/io/vasp/optics.py @@ -10,16 +10,14 @@ import scipy.constants import scipy.special from monty.json import MSONable -from tqdm import tqdm - from pymatgen.electronic_structure.core import Spin from pymatgen.io.vasp.outputs import Vasprun, Waveder +from tqdm import tqdm if TYPE_CHECKING: from numpy.typing import ArrayLike, NDArray - from typing_extensions import Self - from pymatgen.util.typing import PathLike + from typing_extensions import Self __author__ = "Jimmy-Xuan Shen" __copyright__ = "Copyright 2022, The Materials Project" diff --git a/src/pymatgen/io/vasp/outputs.py b/src/pymatgen/io/vasp/outputs.py index f321fdfe40a..ed9e013ecc9 100644 --- a/src/pymatgen/io/vasp/outputs.py +++ b/src/pymatgen/io/vasp/outputs.py @@ -24,7 +24,6 @@ from monty.os.path import zpath from monty.re import regrep from numpy.testing import assert_allclose - from pymatgen.core import Composition, Element, Lattice, Structure from pymatgen.core.trajectory import Trajectory from pymatgen.core.units import unitized @@ -51,9 +50,8 @@ from xml.etree.ElementTree import Element as XML_Element from numpy.typing import NDArray - from typing_extensions import Self - from pymatgen.util.typing import PathLike + from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/src/pymatgen/io/vasp/sets.py b/src/pymatgen/io/vasp/sets.py index 08ce0dedb18..5bc09f6d46b 100644 --- a/src/pymatgen/io/vasp/sets.py +++ b/src/pymatgen/io/vasp/sets.py @@ -45,7 +45,6 @@ from monty.dev import deprecated from monty.json import MSONable from monty.serialization import loadfn - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Element, PeriodicSite, SiteCollection, Species, Structure from pymatgen.io.core import InputGenerator @@ -59,9 +58,8 @@ if TYPE_CHECKING: from typing import Callable, Literal, Union - from typing_extensions import Self - from pymatgen.util.typing import PathLike, Tuple3Ints, Vector3D + from typing_extensions import Self UserPotcarFunctional = Union[ Literal["PBE", "PBE_52", "PBE_54", "LDA", "LDA_52", "LDA_54", "PW91", "LDA_US", "PW91_US"], None diff --git a/src/pymatgen/io/xr.py b/src/pymatgen/io/xr.py index 645c8a6910c..3fb644c11bd 100644 --- a/src/pymatgen/io/xr.py +++ b/src/pymatgen/io/xr.py @@ -15,7 +15,6 @@ import numpy as np from monty.io import zopen - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure diff --git a/src/pymatgen/io/xtb/outputs.py b/src/pymatgen/io/xtb/outputs.py index 4751c251a8f..60ca8c52834 100644 --- a/src/pymatgen/io/xtb/outputs.py +++ b/src/pymatgen/io/xtb/outputs.py @@ -7,7 +7,6 @@ import re from monty.json import MSONable - from pymatgen.core import Molecule from pymatgen.io.xyz import XYZ diff --git a/src/pymatgen/io/xyz.py b/src/pymatgen/io/xyz.py index 81430321a28..28914a47010 100644 --- a/src/pymatgen/io/xyz.py +++ b/src/pymatgen/io/xyz.py @@ -8,7 +8,6 @@ import pandas as pd from monty.io import zopen - from pymatgen.core import Molecule, Structure from pymatgen.core.structure import SiteCollection diff --git a/src/pymatgen/io/zeopp.py b/src/pymatgen/io/zeopp.py index fa8299a2925..5ebee530013 100644 --- a/src/pymatgen/io/zeopp.py +++ b/src/pymatgen/io/zeopp.py @@ -31,7 +31,6 @@ from monty.dev import requires from monty.io import zopen from monty.tempfile import ScratchDir - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Molecule, Structure from pymatgen.io.cssr import Cssr diff --git a/src/pymatgen/phonon/bandstructure.py b/src/pymatgen/phonon/bandstructure.py index d56ff99df89..17cb899b948 100644 --- a/src/pymatgen/phonon/bandstructure.py +++ b/src/pymatgen/phonon/bandstructure.py @@ -7,7 +7,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.electronic_structure.bandstructure import Kpoint @@ -18,9 +17,8 @@ from typing import Any from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.util.typing import Tuple3Ints + from typing_extensions import Self def get_reasonable_repetitions(n_atoms: int) -> Tuple3Ints: diff --git a/src/pymatgen/phonon/dos.py b/src/pymatgen/phonon/dos.py index e4e26c192d4..0e3e0ea2296 100644 --- a/src/pymatgen/phonon/dos.py +++ b/src/pymatgen/phonon/dos.py @@ -8,10 +8,9 @@ import scipy.constants as const from monty.functools import lazy_property from monty.json import MSONable -from scipy.ndimage import gaussian_filter1d - from pymatgen.core.structure import Structure from pymatgen.util.coord import get_linear_interpolated_value +from scipy.ndimage import gaussian_filter1d if TYPE_CHECKING: from collections.abc import Sequence diff --git a/src/pymatgen/phonon/gruneisen.py b/src/pymatgen/phonon/gruneisen.py index a4aee2cc443..67517b4faa7 100644 --- a/src/pymatgen/phonon/gruneisen.py +++ b/src/pymatgen/phonon/gruneisen.py @@ -8,13 +8,12 @@ import scipy.constants as const from monty.dev import requires from monty.json import MSONable -from scipy.interpolate import UnivariateSpline - from pymatgen.core import Structure from pymatgen.core.lattice import Lattice from pymatgen.core.units import amu_to_kg from pymatgen.phonon.bandstructure import PhononBandStructure, PhononBandStructureSymmLine from pymatgen.phonon.dos import PhononDos +from scipy.interpolate import UnivariateSpline try: import phonopy diff --git a/src/pymatgen/phonon/ir_spectra.py b/src/pymatgen/phonon/ir_spectra.py index 462f4fa94b9..9626e6c5726 100644 --- a/src/pymatgen/phonon/ir_spectra.py +++ b/src/pymatgen/phonon/ir_spectra.py @@ -11,7 +11,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.core.spectrum import Spectrum from pymatgen.core.structure import Structure from pymatgen.util.plotting import add_fig_kwargs diff --git a/src/pymatgen/phonon/plotter.py b/src/pymatgen/phonon/plotter.py index 4fe7295fe15..abbd219d530 100644 --- a/src/pymatgen/phonon/plotter.py +++ b/src/pymatgen/phonon/plotter.py @@ -10,7 +10,6 @@ import scipy.constants as const from matplotlib.collections import LineCollection from monty.json import jsanitize - from pymatgen.electronic_structure.plotter import BSDOSPlotter, plot_brillouin_zone from pymatgen.phonon.bandstructure import PhononBandStructureSymmLine from pymatgen.phonon.gruneisen import GruneisenPhononBandStructureSymmLine @@ -23,7 +22,6 @@ from matplotlib.axes import Axes from matplotlib.figure import Figure - from pymatgen.core import Structure from pymatgen.phonon.dos import PhononDos from pymatgen.phonon.gruneisen import GruneisenParameter diff --git a/src/pymatgen/phonon/thermal_displacements.py b/src/pymatgen/phonon/thermal_displacements.py index b021cbc57b3..818b5f6f911 100644 --- a/src/pymatgen/phonon/thermal_displacements.py +++ b/src/pymatgen/phonon/thermal_displacements.py @@ -8,7 +8,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core.structure import Structure from pymatgen.io.cif import CifFile, CifParser, CifWriter, str2float diff --git a/src/pymatgen/symmetry/analyzer.py b/src/pymatgen/symmetry/analyzer.py index 1f08b8f6904..17b29e3e69e 100644 --- a/src/pymatgen/symmetry/analyzer.py +++ b/src/pymatgen/symmetry/analyzer.py @@ -25,7 +25,6 @@ import numpy as np import scipy.cluster import spglib - from pymatgen.core.lattice import Lattice from pymatgen.core.operations import SymmOp from pymatgen.core.structure import Molecule, PeriodicSite, Structure diff --git a/src/pymatgen/symmetry/bandstructure.py b/src/pymatgen/symmetry/bandstructure.py index 05d416a0fbe..c37009792e1 100644 --- a/src/pymatgen/symmetry/bandstructure.py +++ b/src/pymatgen/symmetry/bandstructure.py @@ -9,7 +9,6 @@ import networkx as nx import numpy as np - from pymatgen.electronic_structure.bandstructure import BandStructureSymmLine from pymatgen.electronic_structure.core import Spin from pymatgen.symmetry.analyzer import cite_conventional_cell_algo diff --git a/src/pymatgen/symmetry/groups.py b/src/pymatgen/symmetry/groups.py index 3ecaf8ac1d1..a0c3e79ab87 100644 --- a/src/pymatgen/symmetry/groups.py +++ b/src/pymatgen/symmetry/groups.py @@ -17,19 +17,17 @@ import numpy as np from monty.design_patterns import cached_class from monty.serialization import loadfn - from pymatgen.util.string import Stringify if TYPE_CHECKING: from typing import ClassVar, Literal from numpy.typing import ArrayLike - from typing_extensions import Self - from pymatgen.core.lattice import Lattice # Don't import at runtime to avoid circular import from pymatgen.core.operations import SymmOp # noqa: TCH004 + from typing_extensions import Self CrystalSystem = Literal["cubic", "hexagonal", "monoclinic", "orthorhombic", "tetragonal", "triclinic", "trigonal"] diff --git a/src/pymatgen/symmetry/kpath.py b/src/pymatgen/symmetry/kpath.py index 222dab404d3..a1983f5047d 100644 --- a/src/pymatgen/symmetry/kpath.py +++ b/src/pymatgen/symmetry/kpath.py @@ -12,7 +12,6 @@ import numpy as np import spglib from monty.dev import requires - from pymatgen.core.lattice import Lattice from pymatgen.core.operations import MagSymmOp, SymmOp from pymatgen.symmetry.analyzer import SpacegroupAnalyzer, cite_conventional_cell_algo diff --git a/src/pymatgen/symmetry/maggroups.py b/src/pymatgen/symmetry/maggroups.py index 33270ee8c82..1b7b72ee33a 100644 --- a/src/pymatgen/symmetry/maggroups.py +++ b/src/pymatgen/symmetry/maggroups.py @@ -11,7 +11,6 @@ import numpy as np from monty.design_patterns import cached_class - from pymatgen.core.operations import MagSymmOp from pymatgen.electronic_structure.core import Magmom from pymatgen.symmetry.groups import SymmetryGroup, in_array_list @@ -21,9 +20,8 @@ if TYPE_CHECKING: from collections.abc import Sequence - from typing_extensions import Self - from pymatgen.core.lattice import Lattice + from typing_extensions import Self __author__ = "Matthew Horton, Shyue Ping Ong" diff --git a/src/pymatgen/symmetry/settings.py b/src/pymatgen/symmetry/settings.py index 3abd7f547f3..99597c1cbe3 100644 --- a/src/pymatgen/symmetry/settings.py +++ b/src/pymatgen/symmetry/settings.py @@ -7,12 +7,11 @@ from typing import TYPE_CHECKING import numpy as np -from sympy import Matrix -from sympy.parsing.sympy_parser import parse_expr - from pymatgen.core.lattice import Lattice from pymatgen.core.operations import MagSymmOp, SymmOp from pymatgen.util.string import transformation_to_string +from sympy import Matrix +from sympy.parsing.sympy_parser import parse_expr if TYPE_CHECKING: from typing_extensions import Self diff --git a/src/pymatgen/symmetry/site_symmetries.py b/src/pymatgen/symmetry/site_symmetries.py index c73a321270a..ed889cb5f72 100644 --- a/src/pymatgen/symmetry/site_symmetries.py +++ b/src/pymatgen/symmetry/site_symmetries.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING import numpy as np - from pymatgen.core.operations import SymmOp from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/symmetry/structure.py b/src/pymatgen/symmetry/structure.py index 22d975c9b53..093b5f8af39 100644 --- a/src/pymatgen/symmetry/structure.py +++ b/src/pymatgen/symmetry/structure.py @@ -5,16 +5,14 @@ from typing import TYPE_CHECKING import numpy as np -from tabulate import tabulate - from pymatgen.core.structure import PeriodicSite, Structure +from tabulate import tabulate if TYPE_CHECKING: from collections.abc import Sequence - from typing_extensions import Self - from pymatgen.symmetry.analyzer import SpacegroupOperations + from typing_extensions import Self class SymmetrizedStructure(Structure): diff --git a/src/pymatgen/transformations/advanced_transformations.py b/src/pymatgen/transformations/advanced_transformations.py index fa1b209915f..57b89c43efe 100644 --- a/src/pymatgen/transformations/advanced_transformations.py +++ b/src/pymatgen/transformations/advanced_transformations.py @@ -16,7 +16,6 @@ from monty.dev import requires from monty.fractions import lcm from monty.json import MSONable - from pymatgen.analysis.adsorption import AdsorbateSiteFinder from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.energy_models import SymmetryModel diff --git a/src/pymatgen/transformations/site_transformations.py b/src/pymatgen/transformations/site_transformations.py index 220769fd0f4..7ba3151fc90 100644 --- a/src/pymatgen/transformations/site_transformations.py +++ b/src/pymatgen/transformations/site_transformations.py @@ -14,7 +14,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.analysis.ewald import EwaldMinimizer, EwaldSummation from pymatgen.analysis.local_env import MinimumDistanceNN from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/transformations/standard_transformations.py b/src/pymatgen/transformations/standard_transformations.py index 92bbe883da7..616c7de84a7 100644 --- a/src/pymatgen/transformations/standard_transformations.py +++ b/src/pymatgen/transformations/standard_transformations.py @@ -12,7 +12,6 @@ import numpy as np from numpy import around - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.elasticity.strain import Deformation from pymatgen.analysis.ewald import EwaldMinimizer, EwaldSummation @@ -25,10 +24,9 @@ from pymatgen.transformations.transformation_abc import AbstractTransformation if TYPE_CHECKING: - from typing_extensions import Self - from pymatgen.core.sites import PeriodicSite from pymatgen.util.typing import SpeciesLike + from typing_extensions import Self logger = logging.getLogger(__name__) diff --git a/src/pymatgen/util/coord.py b/src/pymatgen/util/coord.py index eabbc2dadfa..e00dceb5655 100644 --- a/src/pymatgen/util/coord.py +++ b/src/pymatgen/util/coord.py @@ -11,7 +11,6 @@ import numpy as np from monty.json import MSONable - from pymatgen.util import coord_cython if TYPE_CHECKING: @@ -19,7 +18,6 @@ from typing import Literal from numpy.typing import ArrayLike - from pymatgen.util.typing import PbcLike diff --git a/src/pymatgen/util/plotting.py b/src/pymatgen/util/plotting.py index 13958cc9b22..b94cba844e0 100644 --- a/src/pymatgen/util/plotting.py +++ b/src/pymatgen/util/plotting.py @@ -12,7 +12,6 @@ import numpy as np import palettable.colorbrewer.diverging from matplotlib import cm, colors - from pymatgen.core import Element if TYPE_CHECKING: diff --git a/src/pymatgen/util/provenance.py b/src/pymatgen/util/provenance.py index 7fcb5c54a4b..0e72704b1cc 100644 --- a/src/pymatgen/util/provenance.py +++ b/src/pymatgen/util/provenance.py @@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, NamedTuple from monty.json import MontyDecoder, MontyEncoder - from pymatgen.core.structure import Molecule, Structure try: diff --git a/src/pymatgen/util/testing/__init__.py b/src/pymatgen/util/testing/__init__.py index 9ec92a8ee25..211690effa9 100644 --- a/src/pymatgen/util/testing/__init__.py +++ b/src/pymatgen/util/testing/__init__.py @@ -17,7 +17,6 @@ import pytest from monty.json import MontyDecoder, MontyEncoder, MSONable from monty.serialization import loadfn - from pymatgen.core import ROOT, SETTINGS, Structure if TYPE_CHECKING: diff --git a/src/pymatgen/util/testing/aims.py b/src/pymatgen/util/testing/aims.py index 057df663083..ce5b85525a7 100644 --- a/src/pymatgen/util/testing/aims.py +++ b/src/pymatgen/util/testing/aims.py @@ -10,7 +10,6 @@ import numpy as np from monty.io import zopen - from pymatgen.core import Molecule, Structure diff --git a/src/pymatgen/vis/plotters.py b/src/pymatgen/vis/plotters.py index 5670e24f8ae..e77d4c92ccb 100644 --- a/src/pymatgen/vis/plotters.py +++ b/src/pymatgen/vis/plotters.py @@ -5,7 +5,6 @@ import importlib import matplotlib.pyplot as plt - from pymatgen.util.plotting import pretty_plot diff --git a/src/pymatgen/vis/structure_chemview.py b/src/pymatgen/vis/structure_chemview.py index 9007bb9446f..73069535a91 100644 --- a/src/pymatgen/vis/structure_chemview.py +++ b/src/pymatgen/vis/structure_chemview.py @@ -3,7 +3,6 @@ from __future__ import annotations import numpy as np - from pymatgen.analysis.molecule_structure_comparator import CovalentRadius from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/src/pymatgen/vis/structure_vtk.py b/src/pymatgen/vis/structure_vtk.py index c2329591744..cdb15313a80 100644 --- a/src/pymatgen/vis/structure_vtk.py +++ b/src/pymatgen/vis/structure_vtk.py @@ -12,7 +12,6 @@ import numpy as np from monty.dev import requires from monty.serialization import loadfn - from pymatgen.core import PeriodicSite, Species, Structure from pymatgen.util.coord import in_coord_list diff --git a/tests/alchemy/test_filters.py b/tests/alchemy/test_filters.py index 16737ae67fa..418213e1dd3 100644 --- a/tests/alchemy/test_filters.py +++ b/tests/alchemy/test_filters.py @@ -4,7 +4,6 @@ from unittest import TestCase from monty.json import MontyDecoder - from pymatgen.alchemy.filters import ( ContainsSpecieFilter, RemoveDuplicatesFilter, diff --git a/tests/alchemy/test_materials.py b/tests/alchemy/test_materials.py index c667b27277e..0ec5c800f01 100644 --- a/tests/alchemy/test_materials.py +++ b/tests/alchemy/test_materials.py @@ -4,7 +4,6 @@ from copy import deepcopy import pytest - from pymatgen.alchemy.filters import ContainsSpecieFilter from pymatgen.alchemy.materials import TransformedStructure from pymatgen.core import SETTINGS diff --git a/tests/analysis/chemenv/connectivity/test_connected_components.py b/tests/analysis/chemenv/connectivity/test_connected_components.py index 2768fc5b815..dc9b01e62ea 100644 --- a/tests/analysis/chemenv/connectivity/test_connected_components.py +++ b/tests/analysis/chemenv/connectivity/test_connected_components.py @@ -7,7 +7,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.analysis.chemenv.connectivity.connected_components import ConnectedComponent from pymatgen.analysis.chemenv.connectivity.connectivity_finder import ConnectivityFinder from pymatgen.analysis.chemenv.connectivity.environment_nodes import EnvironmentNode diff --git a/tests/analysis/chemenv/coordination_environments/test_chemenv_strategies.py b/tests/analysis/chemenv/coordination_environments/test_chemenv_strategies.py index 631aed31b01..ef2109769e5 100644 --- a/tests/analysis/chemenv/coordination_environments/test_chemenv_strategies.py +++ b/tests/analysis/chemenv/coordination_environments/test_chemenv_strategies.py @@ -1,8 +1,6 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import ( AdditionalConditionInt, AngleCutoffFloat, @@ -11,6 +9,7 @@ SimplestChemenvStrategy, ) from pymatgen.util.testing import PymatgenTest +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/coordination_environments/test_coordination_geometries.py b/tests/analysis/chemenv/coordination_environments/test_coordination_geometries.py index 3cbda25a5fe..03a323c65e9 100644 --- a/tests/analysis/chemenv/coordination_environments/test_coordination_geometries.py +++ b/tests/analysis/chemenv/coordination_environments/test_coordination_geometries.py @@ -3,8 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import ( AllCoordinationGeometries, CoordinationGeometry, @@ -12,6 +10,7 @@ SeparationPlane, ) from pymatgen.util.testing import PymatgenTest +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/coordination_environments/test_coordination_geometry_finder.py b/tests/analysis/chemenv/coordination_environments/test_coordination_geometry_finder.py index fc26a3c33af..2428253b876 100644 --- a/tests/analysis/chemenv/coordination_environments/test_coordination_geometry_finder.py +++ b/tests/analysis/chemenv/coordination_environments/test_coordination_geometry_finder.py @@ -3,8 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.chemenv.coordination_environments.coordination_geometries import AllCoordinationGeometries from pymatgen.analysis.chemenv.coordination_environments.coordination_geometry_finder import ( AbstractGeometry, @@ -13,6 +11,7 @@ ) from pymatgen.core.structure import Lattice, Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/coordination_environments/test_read_write.py b/tests/analysis/chemenv/coordination_environments/test_read_write.py index 42ee8b8cb91..0bb61b6163a 100644 --- a/tests/analysis/chemenv/coordination_environments/test_read_write.py +++ b/tests/analysis/chemenv/coordination_environments/test_read_write.py @@ -3,8 +3,6 @@ import json from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import ( AngleNbSetWeight, CNBiasNbSetWeight, @@ -23,6 +21,7 @@ from pymatgen.analysis.chemenv.coordination_environments.voronoi import DetailedVoronoiContainer from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/coordination_environments/test_structure_environments.py b/tests/analysis/chemenv/coordination_environments/test_structure_environments.py index b9a8ec160d7..8ce3f92ec4b 100644 --- a/tests/analysis/chemenv/coordination_environments/test_structure_environments.py +++ b/tests/analysis/chemenv/coordination_environments/test_structure_environments.py @@ -5,8 +5,6 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import ( MultiWeightsChemenvStrategy, SimplestChemenvStrategy, @@ -18,6 +16,7 @@ ) from pymatgen.core import Species, Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/coordination_environments/test_voronoi.py b/tests/analysis/chemenv/coordination_environments/test_voronoi.py index cfe7b6af420..2adaa1f343c 100644 --- a/tests/analysis/chemenv/coordination_environments/test_voronoi.py +++ b/tests/analysis/chemenv/coordination_environments/test_voronoi.py @@ -3,7 +3,6 @@ import random import numpy as np - from pymatgen.analysis.chemenv.coordination_environments.voronoi import DetailedVoronoiContainer from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure diff --git a/tests/analysis/chemenv/coordination_environments/test_weights.py b/tests/analysis/chemenv/coordination_environments/test_weights.py index f4f117dbe38..5295d47c635 100644 --- a/tests/analysis/chemenv/coordination_environments/test_weights.py +++ b/tests/analysis/chemenv/coordination_environments/test_weights.py @@ -3,8 +3,6 @@ import json import pytest -from pytest import approx - from pymatgen.analysis.chemenv.coordination_environments.chemenv_strategies import ( AngleNbSetWeight, CNBiasNbSetWeight, @@ -17,6 +15,7 @@ ) from pymatgen.analysis.chemenv.coordination_environments.structure_environments import StructureEnvironments from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/utils/test_coordination_geometry_utils.py b/tests/analysis/chemenv/utils/test_coordination_geometry_utils.py index 10e8ff8eb2e..7124904f7cd 100644 --- a/tests/analysis/chemenv/utils/test_coordination_geometry_utils.py +++ b/tests/analysis/chemenv/utils/test_coordination_geometry_utils.py @@ -5,10 +5,9 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.chemenv.utils.coordination_geometry_utils import Plane from pymatgen.util.testing import PymatgenTest +from pytest import approx __author__ = "David Waroquiers" diff --git a/tests/analysis/chemenv/utils/test_func_utils.py b/tests/analysis/chemenv/utils/test_func_utils.py index 265c26d39f4..e01c3d66cc8 100644 --- a/tests/analysis/chemenv/utils/test_func_utils.py +++ b/tests/analysis/chemenv/utils/test_func_utils.py @@ -2,13 +2,12 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.analysis.chemenv.utils.func_utils import ( CSMFiniteRatioFunction, CSMInfiniteRatioFunction, DeltaCSMRatioFunction, ) +from pytest import approx __author__ = "waroquiers" diff --git a/tests/analysis/chemenv/utils/test_graph_utils.py b/tests/analysis/chemenv/utils/test_graph_utils.py index 54ca8e9ef08..cf7ef01fa30 100644 --- a/tests/analysis/chemenv/utils/test_graph_utils.py +++ b/tests/analysis/chemenv/utils/test_graph_utils.py @@ -2,7 +2,6 @@ import pytest from numpy.testing import assert_allclose - from pymatgen.analysis.chemenv.connectivity.environment_nodes import EnvironmentNode from pymatgen.analysis.chemenv.utils.graph_utils import MultiGraphCycle, SimpleGraphCycle, get_delta from pymatgen.util.testing import PymatgenTest diff --git a/tests/analysis/chemenv/utils/test_math_utils.py b/tests/analysis/chemenv/utils/test_math_utils.py index 87420c9c61e..3b0cab7d229 100644 --- a/tests/analysis/chemenv/utils/test_math_utils.py +++ b/tests/analysis/chemenv/utils/test_math_utils.py @@ -2,7 +2,6 @@ import numpy as np from numpy.testing import assert_allclose - from pymatgen.analysis.chemenv.utils.math_utils import ( _cartesian_product, cosinus_step, diff --git a/tests/analysis/diffraction/test_neutron.py b/tests/analysis/diffraction/test_neutron.py index f9f54d9d4fa..5256710bae1 100644 --- a/tests/analysis/diffraction/test_neutron.py +++ b/tests/analysis/diffraction/test_neutron.py @@ -1,12 +1,11 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.analysis.diffraction.neutron import NDCalculator from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.util.testing import PymatgenTest +from pytest import approx """ These calculated values were verified with VESTA and FullProf. diff --git a/tests/analysis/diffraction/test_tem.py b/tests/analysis/diffraction/test_tem.py index 7b5a79cea67..edbe77256ca 100644 --- a/tests/analysis/diffraction/test_tem.py +++ b/tests/analysis/diffraction/test_tem.py @@ -6,12 +6,11 @@ import pandas as pd import plotly.graph_objects as go from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.diffraction.tem import TEMCalculator from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.util.testing import PymatgenTest +from pytest import approx __author__ = "Frank Wan, Jason Liang" __copyright__ = "Copyright 2019, The Materials Project" diff --git a/tests/analysis/diffraction/test_xrd.py b/tests/analysis/diffraction/test_xrd.py index 9663d6fbf7d..ed4f9bcb2d2 100644 --- a/tests/analysis/diffraction/test_xrd.py +++ b/tests/analysis/diffraction/test_xrd.py @@ -1,12 +1,11 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.analysis.diffraction.xrd import XRDCalculator from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.util.testing import PymatgenTest +from pytest import approx """ TODO: Modify unittest doc. diff --git a/tests/analysis/elasticity/test_elastic.py b/tests/analysis/elasticity/test_elastic.py index 4fe8ecdc5da..85de3ac4728 100644 --- a/tests/analysis/elasticity/test_elastic.py +++ b/tests/analysis/elasticity/test_elastic.py @@ -8,9 +8,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx -from scipy.misc import central_diff_weights - from pymatgen.analysis.elasticity.elastic import ( ComplianceTensor, ElasticTensor, @@ -29,6 +26,8 @@ from pymatgen.core.tensors import Tensor from pymatgen.core.units import FloatWithUnit from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx +from scipy.misc import central_diff_weights TEST_DIR = f"{TEST_FILES_DIR}/analysis/elasticity" diff --git a/tests/analysis/elasticity/test_strain.py b/tests/analysis/elasticity/test_strain.py index e64de89f18d..2ffadf975e5 100644 --- a/tests/analysis/elasticity/test_strain.py +++ b/tests/analysis/elasticity/test_strain.py @@ -3,7 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.analysis.elasticity.strain import Deformation, DeformedStructureSet, Strain, convert_strain_to_deformation from pymatgen.core.structure import Structure from pymatgen.core.tensors import Tensor diff --git a/tests/analysis/elasticity/test_stress.py b/tests/analysis/elasticity/test_stress.py index cc79322de83..66a656243ed 100644 --- a/tests/analysis/elasticity/test_stress.py +++ b/tests/analysis/elasticity/test_stress.py @@ -3,11 +3,10 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.elasticity.strain import Deformation from pymatgen.analysis.elasticity.stress import Stress from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestStress(PymatgenTest): diff --git a/tests/analysis/ferroelectricity/test_polarization.py b/tests/analysis/ferroelectricity/test_polarization.py index 8c5de6645fc..67f90933990 100644 --- a/tests/analysis/ferroelectricity/test_polarization.py +++ b/tests/analysis/ferroelectricity/test_polarization.py @@ -2,8 +2,6 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.ferroelectricity.polarization import ( EnergyTrend, Polarization, @@ -14,6 +12,7 @@ from pymatgen.io.vasp.inputs import Potcar from pymatgen.io.vasp.outputs import Outcar from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/vasp/fixtures/BTO_221_99_polarization" bto_folders = ["nonpolar_polarization"] diff --git a/tests/analysis/interfaces/test_coherent_interface.py b/tests/analysis/interfaces/test_coherent_interface.py index 081cc3c4b62..1284bc8c731 100644 --- a/tests/analysis/interfaces/test_coherent_interface.py +++ b/tests/analysis/interfaces/test_coherent_interface.py @@ -1,7 +1,6 @@ from __future__ import annotations from numpy.testing import assert_allclose - from pymatgen.analysis.interfaces.coherent_interfaces import ( CoherentInterfaceBuilder, from_2d_to_3d, diff --git a/tests/analysis/interfaces/test_substrate_analyzer.py b/tests/analysis/interfaces/test_substrate_analyzer.py index a842cd4dad7..39e75ea53f5 100644 --- a/tests/analysis/interfaces/test_substrate_analyzer.py +++ b/tests/analysis/interfaces/test_substrate_analyzer.py @@ -1,7 +1,6 @@ from __future__ import annotations from numpy.testing import assert_allclose - from pymatgen.analysis.elasticity.elastic import ElasticTensor from pymatgen.analysis.interfaces.substrate_analyzer import SubstrateAnalyzer from pymatgen.symmetry.analyzer import SpacegroupAnalyzer diff --git a/tests/analysis/interfaces/test_zsl.py b/tests/analysis/interfaces/test_zsl.py index 3cc7732eb73..d4fc0bdef2a 100644 --- a/tests/analysis/interfaces/test_zsl.py +++ b/tests/analysis/interfaces/test_zsl.py @@ -2,8 +2,6 @@ import numpy as np from numpy.testing import assert_array_equal -from pytest import approx - from pymatgen.analysis.interfaces.zsl import ( ZSLGenerator, fast_norm, @@ -14,6 +12,7 @@ ) from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.testing import PymatgenTest +from pytest import approx __author__ = "Shyam Dwaraknath" __copyright__ = "Copyright 2016, The Materials Project" diff --git a/tests/analysis/magnetism/test_analyzer.py b/tests/analysis/magnetism/test_analyzer.py index bcdd02f1970..12e0f3029a6 100644 --- a/tests/analysis/magnetism/test_analyzer.py +++ b/tests/analysis/magnetism/test_analyzer.py @@ -6,8 +6,6 @@ import pytest from monty.serialization import loadfn from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.magnetism import ( CollinearMagneticStructureAnalyzer, MagneticStructureEnumerator, @@ -16,6 +14,7 @@ ) from pymatgen.core import Element, Lattice, Species, Structure from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/magnetic_orderings" diff --git a/tests/analysis/magnetism/test_heisenberg.py b/tests/analysis/magnetism/test_heisenberg.py index 3eea3bd6782..968e28524ce 100644 --- a/tests/analysis/magnetism/test_heisenberg.py +++ b/tests/analysis/magnetism/test_heisenberg.py @@ -3,7 +3,6 @@ from unittest import TestCase import pandas as pd - from pymatgen.analysis.magnetism.heisenberg import HeisenbergMapper from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR diff --git a/tests/analysis/magnetism/test_jahnteller.py b/tests/analysis/magnetism/test_jahnteller.py index 6cc3356b4aa..3a5da6a5ef4 100644 --- a/tests/analysis/magnetism/test_jahnteller.py +++ b/tests/analysis/magnetism/test_jahnteller.py @@ -3,11 +3,10 @@ from unittest import TestCase import numpy as np -from pytest import approx - from pymatgen.analysis.magnetism.jahnteller import JahnTellerAnalyzer, Species from pymatgen.core import Structure from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx class TestJahnTeller(TestCase): diff --git a/tests/analysis/solar/test_slme.py b/tests/analysis/solar/test_slme.py index 4d2e12c1ef0..906dba63bc7 100644 --- a/tests/analysis/solar/test_slme.py +++ b/tests/analysis/solar/test_slme.py @@ -1,9 +1,8 @@ from __future__ import annotations -from pytest import approx - from pymatgen.analysis.solar.slme import optics, slme from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/solar" diff --git a/tests/analysis/structure_prediction/test_dopant_predictor.py b/tests/analysis/structure_prediction/test_dopant_predictor.py index f19d032624d..a763421b0bb 100644 --- a/tests/analysis/structure_prediction/test_dopant_predictor.py +++ b/tests/analysis/structure_prediction/test_dopant_predictor.py @@ -2,14 +2,13 @@ from unittest import TestCase -from pytest import approx - from pymatgen.analysis.local_env import CrystalNN from pymatgen.analysis.structure_prediction.dopant_predictor import ( get_dopants_from_shannon_radii, get_dopants_from_substitution_probabilities, ) from pymatgen.core import Species, Structure +from pytest import approx class TestDopantPrediction(TestCase): diff --git a/tests/analysis/structure_prediction/test_substitution_probability.py b/tests/analysis/structure_prediction/test_substitution_probability.py index e6c5f9786d0..eb9115995a3 100644 --- a/tests/analysis/structure_prediction/test_substitution_probability.py +++ b/tests/analysis/structure_prediction/test_substitution_probability.py @@ -3,14 +3,13 @@ import json from unittest import TestCase -from pytest import approx - from pymatgen.analysis.structure_prediction.substitution_probability import ( SubstitutionPredictor, SubstitutionProbability, ) from pymatgen.core import Composition, Species from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/struct_predictor" diff --git a/tests/analysis/structure_prediction/test_volume_predictor.py b/tests/analysis/structure_prediction/test_volume_predictor.py index 9f6a150aeb7..069a9dbc86f 100644 --- a/tests/analysis/structure_prediction/test_volume_predictor.py +++ b/tests/analysis/structure_prediction/test_volume_predictor.py @@ -1,11 +1,10 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.analysis.structure_prediction.volume_predictor import DLSVolumePredictor, RLSVolumePredictor from pymatgen.core import Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/structure_prediction" diff --git a/tests/analysis/test_adsorption.py b/tests/analysis/test_adsorption.py index 9f611a2feaa..76fdc09a0cd 100644 --- a/tests/analysis/test_adsorption.py +++ b/tests/analysis/test_adsorption.py @@ -2,7 +2,6 @@ import numpy as np from numpy.testing import assert_allclose - from pymatgen.analysis.adsorption import AdsorbateSiteFinder, generate_all_slabs, get_rot, reorient_z from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Molecule, Structure diff --git a/tests/analysis/test_bond_dissociation.py b/tests/analysis/test_bond_dissociation.py index b29081413c9..6dd0d7f9af6 100644 --- a/tests/analysis/test_bond_dissociation.py +++ b/tests/analysis/test_bond_dissociation.py @@ -4,7 +4,6 @@ import pytest from monty.serialization import loadfn - from pymatgen.analysis.bond_dissociation import BondDissociationEnergies from pymatgen.util.testing import TEST_FILES_DIR diff --git a/tests/analysis/test_bond_valence.py b/tests/analysis/test_bond_valence.py index 8bfc158a574..5a0202e075b 100644 --- a/tests/analysis/test_bond_valence.py +++ b/tests/analysis/test_bond_valence.py @@ -1,11 +1,10 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.analysis.bond_valence import BVAnalyzer, calculate_bv_sum, calculate_bv_sum_unordered from pymatgen.core import Composition, Species, Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/bond_valence" diff --git a/tests/analysis/test_chempot_diagram.py b/tests/analysis/test_chempot_diagram.py index 648d6a5a164..5465e1d050d 100644 --- a/tests/analysis/test_chempot_diagram.py +++ b/tests/analysis/test_chempot_diagram.py @@ -2,8 +2,6 @@ import numpy as np from plotly.graph_objects import Figure -from pytest import approx - from pymatgen.analysis.chempot_diagram import ( ChemicalPotentialDiagram, get_2d_orthonormal_vector, @@ -13,6 +11,7 @@ from pymatgen.core.composition import Element from pymatgen.entries.entry_tools import EntrySet from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis" diff --git a/tests/analysis/test_cost.py b/tests/analysis/test_cost.py index c20bed2fd54..4aa598bc5fa 100644 --- a/tests/analysis/test_cost.py +++ b/tests/analysis/test_cost.py @@ -2,10 +2,9 @@ from unittest import TestCase -from pytest import approx - from pymatgen.analysis.cost import CostAnalyzer, CostDBCSV, CostDBElements from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/cost" diff --git a/tests/analysis/test_dimensionality.py b/tests/analysis/test_dimensionality.py index 8b99c6fd5e2..b83c2cb5748 100644 --- a/tests/analysis/test_dimensionality.py +++ b/tests/analysis/test_dimensionality.py @@ -3,7 +3,6 @@ import networkx as nx import pytest from monty.serialization import loadfn - from pymatgen.analysis.dimensionality import ( calculate_dimensionality_of_site, get_dimensionality_cheon, diff --git a/tests/analysis/test_disorder.py b/tests/analysis/test_disorder.py index a4b930096ac..2384bc4e296 100644 --- a/tests/analysis/test_disorder.py +++ b/tests/analysis/test_disorder.py @@ -1,10 +1,9 @@ from __future__ import annotations -from pytest import approx - from pymatgen.analysis.disorder import get_warren_cowley_parameters from pymatgen.core import Element, Structure from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestOrderParameter(PymatgenTest): diff --git a/tests/analysis/test_energy_models.py b/tests/analysis/test_energy_models.py index 36d3024ac75..a2c4aa40612 100644 --- a/tests/analysis/test_energy_models.py +++ b/tests/analysis/test_energy_models.py @@ -1,12 +1,11 @@ from __future__ import annotations -from pytest import approx - from pymatgen.analysis.energy_models import EwaldElectrostaticModel, IsingModel, SymmetryModel from pymatgen.core import Species from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx class TestEwaldElectrostaticModel: diff --git a/tests/analysis/test_eos.py b/tests/analysis/test_eos.py index 4c02d040fd4..f31c3723eb7 100644 --- a/tests/analysis/test_eos.py +++ b/tests/analysis/test_eos.py @@ -2,10 +2,9 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.eos import EOS, NumericalEOS from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestEOS(PymatgenTest): diff --git a/tests/analysis/test_ewald.py b/tests/analysis/test_ewald.py index 285152bb8fd..7ada2e539dc 100644 --- a/tests/analysis/test_ewald.py +++ b/tests/analysis/test_ewald.py @@ -4,11 +4,10 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.analysis.ewald import EwaldMinimizer, EwaldSummation from pymatgen.core.structure import Structure from pymatgen.util.testing import VASP_IN_DIR +from pytest import approx class TestEwaldSummation(TestCase): diff --git a/tests/analysis/test_fragmenter.py b/tests/analysis/test_fragmenter.py index 611edcf58dd..26b2840b3ae 100644 --- a/tests/analysis/test_fragmenter.py +++ b/tests/analysis/test_fragmenter.py @@ -1,7 +1,6 @@ from __future__ import annotations import pytest - from pymatgen.analysis.fragmenter import Fragmenter from pymatgen.analysis.graphs import MoleculeGraph from pymatgen.analysis.local_env import OpenBabelNN diff --git a/tests/analysis/test_functional_groups.py b/tests/analysis/test_functional_groups.py index e53a62960a0..bc59a7da57d 100644 --- a/tests/analysis/test_functional_groups.py +++ b/tests/analysis/test_functional_groups.py @@ -3,7 +3,6 @@ from unittest import TestCase import pytest - from pymatgen.analysis.functional_groups import FunctionalGroupExtractor from pymatgen.analysis.graphs import MoleculeGraph from pymatgen.analysis.local_env import OpenBabelNN diff --git a/tests/analysis/test_graphs.py b/tests/analysis/test_graphs.py index dcb8809a818..f196cde29ad 100644 --- a/tests/analysis/test_graphs.py +++ b/tests/analysis/test_graphs.py @@ -10,8 +10,6 @@ import networkx.algorithms.isomorphism as iso import pytest from monty.serialization import loadfn -from pytest import approx - from pymatgen.analysis.graphs import MoleculeGraph, MolGraphSplitError, PeriodicSite, StructureGraph from pymatgen.analysis.local_env import ( CovalentBondNN, @@ -25,6 +23,7 @@ from pymatgen.core import Lattice, Molecule, Site, Structure from pymatgen.core.structure import FunctionalGroups from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx try: from openbabel import openbabel diff --git a/tests/analysis/test_hhi.py b/tests/analysis/test_hhi.py index 105072426fb..d1c95587124 100644 --- a/tests/analysis/test_hhi.py +++ b/tests/analysis/test_hhi.py @@ -1,8 +1,7 @@ from __future__ import annotations -from pytest import approx - from pymatgen.analysis.hhi import HHIModel +from pytest import approx class TestHHIModel: diff --git a/tests/analysis/test_interface_reactions.py b/tests/analysis/test_interface_reactions.py index 3c47a6f9f3c..0b9665fef4f 100644 --- a/tests/analysis/test_interface_reactions.py +++ b/tests/analysis/test_interface_reactions.py @@ -8,13 +8,12 @@ from numpy.testing import assert_allclose from pandas import DataFrame from plotly.graph_objects import Figure -from scipy.spatial import ConvexHull - from pymatgen.analysis.interface_reactions import GrandPotentialInterfacialReactivity, InterfacialReactivity from pymatgen.analysis.phase_diagram import GrandPotentialPhaseDiagram, PhaseDiagram from pymatgen.analysis.reaction_calculator import Reaction from pymatgen.core.composition import Composition, Element from pymatgen.entries.computed_entries import ComputedEntry +from scipy.spatial import ConvexHull class TestInterfaceReaction(TestCase): diff --git a/tests/analysis/test_local_env.py b/tests/analysis/test_local_env.py index ed8b72c677d..1fbed74da82 100644 --- a/tests/analysis/test_local_env.py +++ b/tests/analysis/test_local_env.py @@ -7,8 +7,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.graphs import MoleculeGraph, StructureGraph from pymatgen.analysis.local_env import ( BrunnerNNReal, @@ -38,6 +36,7 @@ ) from pymatgen.core import Element, Lattice, Molecule, Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/local_env/fragmenter_files" diff --git a/tests/analysis/test_molecule_matcher.py b/tests/analysis/test_molecule_matcher.py index 485e3c75f2f..cca70ae1c2c 100644 --- a/tests/analysis/test_molecule_matcher.py +++ b/tests/analysis/test_molecule_matcher.py @@ -4,8 +4,6 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.analysis.molecule_matcher import ( BruteForceOrderMatcher, GeneticOrderMatcher, @@ -19,6 +17,7 @@ from pymatgen.core.structure import Lattice, Molecule, Structure from pymatgen.io.xyz import XYZ from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx try: from openbabel import openbabel diff --git a/tests/analysis/test_nmr.py b/tests/analysis/test_nmr.py index 14490ec7a0c..86961996a71 100644 --- a/tests/analysis/test_nmr.py +++ b/tests/analysis/test_nmr.py @@ -2,10 +2,9 @@ import numpy as np from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.analysis.nmr import ChemicalShielding, ElectricFieldGradient from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestChemicalShieldingNotation(PymatgenTest): diff --git a/tests/analysis/test_phase_diagram.py b/tests/analysis/test_phase_diagram.py index c42ca82ab6e..e05464d774d 100644 --- a/tests/analysis/test_phase_diagram.py +++ b/tests/analysis/test_phase_diagram.py @@ -12,8 +12,6 @@ import pytest from monty.serialization import dumpfn, loadfn from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.phase_diagram import ( CompoundPhaseDiagram, GrandPotentialPhaseDiagram, @@ -32,6 +30,7 @@ from pymatgen.entries.computed_entries import ComputedEntry from pymatgen.entries.entry_tools import EntrySet from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis" diff --git a/tests/analysis/test_piezo.py b/tests/analysis/test_piezo.py index 62569975053..559d1a3cc75 100644 --- a/tests/analysis/test_piezo.py +++ b/tests/analysis/test_piezo.py @@ -5,7 +5,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal - from pymatgen.analysis.piezo import PiezoTensor from pymatgen.util.testing import PymatgenTest diff --git a/tests/analysis/test_piezo_sensitivity.py b/tests/analysis/test_piezo_sensitivity.py index eafaaddc6b4..19f2444f028 100644 --- a/tests/analysis/test_piezo_sensitivity.py +++ b/tests/analysis/test_piezo_sensitivity.py @@ -5,10 +5,9 @@ import pickle import numpy as np +import pymatgen import pytest from numpy.testing import assert_allclose - -import pymatgen from pymatgen.analysis.piezo_sensitivity import ( BornEffectiveCharge, ForceConstantMatrix, diff --git a/tests/analysis/test_pourbaix_diagram.py b/tests/analysis/test_pourbaix_diagram.py index 3119877f33b..6ea90a801b3 100644 --- a/tests/analysis/test_pourbaix_diagram.py +++ b/tests/analysis/test_pourbaix_diagram.py @@ -7,13 +7,12 @@ import matplotlib.pyplot as plt import numpy as np from monty.serialization import dumpfn, loadfn -from pytest import approx - from pymatgen.analysis.pourbaix_diagram import IonEntry, MultiEntry, PourbaixDiagram, PourbaixEntry, PourbaixPlotter from pymatgen.core.composition import Composition from pymatgen.core.ion import Ion from pymatgen.entries.computed_entries import ComputedEntry from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/pourbaix_diagram" diff --git a/tests/analysis/test_quasi_harmonic_debye_approx.py b/tests/analysis/test_quasi_harmonic_debye_approx.py index 9a9aa86bce5..fce94e37d44 100644 --- a/tests/analysis/test_quasi_harmonic_debye_approx.py +++ b/tests/analysis/test_quasi_harmonic_debye_approx.py @@ -4,7 +4,6 @@ import numpy as np from numpy.testing import assert_allclose - from pymatgen.analysis.eos import EOS from pymatgen.analysis.quasiharmonic import QuasiHarmonicDebyeApprox from pymatgen.core.structure import Structure diff --git a/tests/analysis/test_quasirrho.py b/tests/analysis/test_quasirrho.py index 63cd0015ad4..37829e37a75 100644 --- a/tests/analysis/test_quasirrho.py +++ b/tests/analysis/test_quasirrho.py @@ -3,7 +3,6 @@ from unittest import TestCase import pytest - from pymatgen.analysis.quasirrho import QuasiRRHO, get_avg_mom_inertia from pymatgen.io.gaussian import GaussianOutput from pymatgen.io.qchem.outputs import QCOutput diff --git a/tests/analysis/test_reaction_calculator.py b/tests/analysis/test_reaction_calculator.py index dcb06819574..f8584fd5549 100644 --- a/tests/analysis/test_reaction_calculator.py +++ b/tests/analysis/test_reaction_calculator.py @@ -6,11 +6,10 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.analysis.reaction_calculator import BalancedReaction, ComputedReaction, Reaction, ReactionError from pymatgen.core.composition import Composition from pymatgen.entries.computed_entries import ComputedEntry +from pytest import approx class TestReaction: diff --git a/tests/analysis/test_structure_analyzer.py b/tests/analysis/test_structure_analyzer.py index 12a0c02f991..1639729a56a 100644 --- a/tests/analysis/test_structure_analyzer.py +++ b/tests/analysis/test_structure_analyzer.py @@ -4,8 +4,6 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.structure_analyzer import ( RelaxationAnalyzer, VoronoiAnalyzer, @@ -19,6 +17,7 @@ from pymatgen.core import Element, Lattice, Structure from pymatgen.io.vasp.outputs import Xdatcar from pymatgen.util.testing import VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx class TestVoronoiAnalyzer(PymatgenTest): diff --git a/tests/analysis/test_structure_matcher.py b/tests/analysis/test_structure_matcher.py index c658585cf75..28707ef44de 100644 --- a/tests/analysis/test_structure_matcher.py +++ b/tests/analysis/test_structure_matcher.py @@ -6,8 +6,6 @@ import pytest from monty.json import MontyDecoder from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.analysis.structure_matcher import ( ElementComparator, FrameworkComparator, @@ -18,6 +16,7 @@ from pymatgen.core import Element, Lattice, Structure, SymmOp from pymatgen.util.coord import find_in_coord_list_pbc from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/structure_matcher" diff --git a/tests/analysis/test_surface_analysis.py b/tests/analysis/test_surface_analysis.py index 0e58a2099d5..07195437d80 100644 --- a/tests/analysis/test_surface_analysis.py +++ b/tests/analysis/test_surface_analysis.py @@ -3,12 +3,11 @@ import json from numpy.testing import assert_allclose -from pytest import approx -from sympy import Number, Symbol - from pymatgen.analysis.surface_analysis import NanoscaleStability, SlabEntry, SurfaceEnergyPlotter, WorkFunctionAnalyzer from pymatgen.entries.computed_entries import ComputedStructureEntry from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx +from sympy import Number, Symbol __author__ = "Richard Tran" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/tests/analysis/test_transition_state.py b/tests/analysis/test_transition_state.py index 96841bc482a..0e404e02fc8 100644 --- a/tests/analysis/test_transition_state.py +++ b/tests/analysis/test_transition_state.py @@ -4,7 +4,6 @@ from matplotlib import pyplot as plt from numpy.testing import assert_allclose - from pymatgen.analysis.transition_state import NEBAnalysis, combine_neb_plots from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/analysis/test_wulff.py b/tests/analysis/test_wulff.py index 11109c1e746..c4656a79097 100644 --- a/tests/analysis/test_wulff.py +++ b/tests/analysis/test_wulff.py @@ -2,14 +2,13 @@ import json -from pytest import approx - from pymatgen.analysis.wulff import WulffShape from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.coord import in_coord_list from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx __author__ = "Zihan Xu, Richard Tran, Balachandran Radhakrishnan" __copyright__ = "Copyright 2013, The Materials Virtual Lab" diff --git a/tests/analysis/topological/test_spillage.py b/tests/analysis/topological/test_spillage.py index 39224cdfc52..61652c97696 100644 --- a/tests/analysis/topological/test_spillage.py +++ b/tests/analysis/topological/test_spillage.py @@ -1,9 +1,8 @@ from __future__ import annotations -from pytest import approx - from pymatgen.analysis.topological.spillage import SOCSpillage from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/topological" diff --git a/tests/analysis/xas/test_spectrum.py b/tests/analysis/xas/test_spectrum.py index ccc1c3812a5..25772d15fb7 100644 --- a/tests/analysis/xas/test_spectrum.py +++ b/tests/analysis/xas/test_spectrum.py @@ -6,11 +6,10 @@ import pytest from monty.json import MontyDecoder from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.analysis.xas.spectrum import XAS, site_weighted_spectrum from pymatgen.core import Element from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/spectrum_test" diff --git a/tests/apps/battery/test_analyzer.py b/tests/apps/battery/test_analyzer.py index 61426c65510..a7a22ccf015 100644 --- a/tests/apps/battery/test_analyzer.py +++ b/tests/apps/battery/test_analyzer.py @@ -1,11 +1,10 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.apps.battery.analyzer import BatteryAnalyzer from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx class TestBatteryAnalyzer(PymatgenTest): diff --git a/tests/apps/battery/test_conversion_battery.py b/tests/apps/battery/test_conversion_battery.py index ae17c969353..041702625a4 100644 --- a/tests/apps/battery/test_conversion_battery.py +++ b/tests/apps/battery/test_conversion_battery.py @@ -4,11 +4,10 @@ from unittest import TestCase from monty.json import MontyDecoder -from pytest import approx - from pymatgen.apps.battery.conversion_battery import ConversionElectrode, ConversionVoltagePair from pymatgen.core.composition import Composition from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/apps/battery" diff --git a/tests/apps/battery/test_insertion_battery.py b/tests/apps/battery/test_insertion_battery.py index 4c6b9caa951..06c6ef0802f 100644 --- a/tests/apps/battery/test_insertion_battery.py +++ b/tests/apps/battery/test_insertion_battery.py @@ -4,11 +4,10 @@ from unittest import TestCase from monty.json import MontyDecoder, MontyEncoder -from pytest import approx - from pymatgen.apps.battery.insertion_battery import InsertionElectrode, InsertionVoltagePair from pymatgen.entries.computed_entries import ComputedEntry from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/apps/battery" diff --git a/tests/apps/battery/test_plotter.py b/tests/apps/battery/test_plotter.py index 8b93b20f8bf..c9e14875e25 100644 --- a/tests/apps/battery/test_plotter.py +++ b/tests/apps/battery/test_plotter.py @@ -4,7 +4,6 @@ from unittest import TestCase from monty.json import MontyDecoder - from pymatgen.apps.battery.conversion_battery import ConversionElectrode from pymatgen.apps.battery.insertion_battery import InsertionElectrode from pymatgen.apps.battery.plotter import VoltageProfilePlotter diff --git a/tests/apps/borg/test_hive.py b/tests/apps/borg/test_hive.py index 766086d429b..64b64814c70 100644 --- a/tests/apps/borg/test_hive.py +++ b/tests/apps/borg/test_hive.py @@ -3,8 +3,6 @@ import os from unittest import TestCase -from pytest import approx - from pymatgen.apps.borg.hive import ( GaussianToComputedEntryDrone, SimpleVaspToComputedEntryDrone, @@ -12,6 +10,7 @@ ) from pymatgen.entries.computed_entries import ComputedStructureEntry from pymatgen.util.testing import TEST_FILES_DIR, VASP_OUT_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/apps/borg" diff --git a/tests/apps/borg/test_queen.py b/tests/apps/borg/test_queen.py index b95e660f7f8..14cc20679a9 100644 --- a/tests/apps/borg/test_queen.py +++ b/tests/apps/borg/test_queen.py @@ -1,10 +1,9 @@ from __future__ import annotations -from pytest import approx - from pymatgen.apps.borg.hive import VaspToComputedEntryDrone from pymatgen.apps.borg.queen import BorgQueen from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx __author__ = "Shyue Ping Ong" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/tests/command_line/test_bader_caller.py b/tests/command_line/test_bader_caller.py index c3d9b609946..5bff1599c62 100644 --- a/tests/command_line/test_bader_caller.py +++ b/tests/command_line/test_bader_caller.py @@ -8,10 +8,9 @@ import pytest from monty.shutil import copy_r from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.command_line.bader_caller import BaderAnalysis, bader_analysis_from_path from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/command_line/bader" diff --git a/tests/command_line/test_critic2_caller.py b/tests/command_line/test_critic2_caller.py index 46218f94e8c..4f1a7cf648f 100644 --- a/tests/command_line/test_critic2_caller.py +++ b/tests/command_line/test_critic2_caller.py @@ -4,11 +4,10 @@ from unittest import TestCase import pytest -from pytest import approx - from pymatgen.command_line.critic2_caller import Critic2Analysis, Critic2Caller from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx __author__ = "Matthew Horton" __version__ = "0.1" diff --git a/tests/command_line/test_enumlib_caller.py b/tests/command_line/test_enumlib_caller.py index 010bb0bd1b8..d21b5b67986 100644 --- a/tests/command_line/test_enumlib_caller.py +++ b/tests/command_line/test_enumlib_caller.py @@ -5,14 +5,13 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.command_line.enumlib_caller import EnumError, EnumlibAdaptor from pymatgen.core import Element, Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.transformations.site_transformations import RemoveSitesTransformation from pymatgen.transformations.standard_transformations import SubstitutionTransformation from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx enum_cmd = which("enum.x") or which("multienum.x") makestr_cmd = which("makestr.x") or which("makeStr.x") or which("makeStr.py") diff --git a/tests/command_line/test_gulp_caller.py b/tests/command_line/test_gulp_caller.py index 054d0e7c023..aa0faf1d16e 100644 --- a/tests/command_line/test_gulp_caller.py +++ b/tests/command_line/test_gulp_caller.py @@ -14,7 +14,6 @@ import numpy as np import pytest - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.command_line.gulp_caller import ( BuckinghamPotential, diff --git a/tests/command_line/test_mcsqs_caller.py b/tests/command_line/test_mcsqs_caller.py index 39714b1f4f1..9dd6303a1dc 100644 --- a/tests/command_line/test_mcsqs_caller.py +++ b/tests/command_line/test_mcsqs_caller.py @@ -4,7 +4,6 @@ import pytest from monty.serialization import loadfn - from pymatgen.command_line.mcsqs_caller import run_mcsqs from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/command_line/test_vampire_caller.py b/tests/command_line/test_vampire_caller.py index 19efe2eef1f..e8cc2330f02 100644 --- a/tests/command_line/test_vampire_caller.py +++ b/tests/command_line/test_vampire_caller.py @@ -4,11 +4,10 @@ import pandas as pd import pytest -from pytest import approx - from pymatgen.command_line.vampire_caller import VampireCaller from pymatgen.core.structure import Structure from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/analysis/magnetic_orderings" diff --git a/tests/core/test_bonds.py b/tests/core/test_bonds.py index 777d938619a..9154207aba6 100644 --- a/tests/core/test_bonds.py +++ b/tests/core/test_bonds.py @@ -1,10 +1,9 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.core import Element, Site from pymatgen.core.bonds import CovalentBond, get_bond_length, get_bond_order, obtain_all_bond_lengths +from pytest import approx __author__ = "Shyue Ping Ong" __copyright__ = "Copyright 2012, The Materials Project" diff --git a/tests/core/test_composition.py b/tests/core/test_composition.py index f520438c15b..1cf2c688e31 100644 --- a/tests/core/test_composition.py +++ b/tests/core/test_composition.py @@ -10,11 +10,10 @@ import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core import Composition, DummySpecies, Element, Species from pymatgen.core.composition import ChemicalPotential from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestComposition(PymatgenTest): diff --git a/tests/core/test_interface.py b/tests/core/test_interface.py index 22cd178dd5b..1757265fd27 100644 --- a/tests/core/test_interface.py +++ b/tests/core/test_interface.py @@ -2,13 +2,12 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.interface import GrainBoundary, GrainBoundaryGenerator, Interface from pymatgen.core.structure import Structure from pymatgen.core.surface import SlabGenerator from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/core/grain_boundary" diff --git a/tests/core/test_ion.py b/tests/core/test_ion.py index 16037ec35be..f501287ed6d 100644 --- a/tests/core/test_ion.py +++ b/tests/core/test_ion.py @@ -4,7 +4,6 @@ from unittest import TestCase import pytest - from pymatgen.core import Composition, Element from pymatgen.core.ion import Ion diff --git a/tests/core/test_lattice.py b/tests/core/test_lattice.py index ce5cff053aa..91a84688039 100644 --- a/tests/core/test_lattice.py +++ b/tests/core/test_lattice.py @@ -5,11 +5,10 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core.lattice import Lattice, get_points_in_spheres from pymatgen.core.operations import SymmOp from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestLattice(PymatgenTest): diff --git a/tests/core/test_molecular_orbitals.py b/tests/core/test_molecular_orbitals.py index 4476c856d17..a1699967585 100644 --- a/tests/core/test_molecular_orbitals.py +++ b/tests/core/test_molecular_orbitals.py @@ -1,7 +1,6 @@ from __future__ import annotations import pytest - from pymatgen.core.molecular_orbitals import MolecularOrbitals from pymatgen.util.testing import PymatgenTest diff --git a/tests/core/test_operations.py b/tests/core/test_operations.py index d2dcc26de42..abd9d4357a6 100644 --- a/tests/core/test_operations.py +++ b/tests/core/test_operations.py @@ -2,7 +2,6 @@ import numpy as np from numpy.testing import assert_allclose - from pymatgen.core.operations import MagSymmOp, SymmOp from pymatgen.electronic_structure.core import Magmom from pymatgen.util.testing import PymatgenTest diff --git a/tests/core/test_periodic_table.py b/tests/core/test_periodic_table.py index 90cfdc11eaf..a0ffe06fead 100644 --- a/tests/core/test_periodic_table.py +++ b/tests/core/test_periodic_table.py @@ -8,13 +8,12 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.core import DummySpecies, Element, Species, get_el_sp from pymatgen.core.periodic_table import ElementBase, ElementType from pymatgen.core.units import Ha_to_eV from pymatgen.io.core import ParseError from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestElement(PymatgenTest): diff --git a/tests/core/test_sites.py b/tests/core/test_sites.py index a80689d9dfd..ee3911c29eb 100644 --- a/tests/core/test_sites.py +++ b/tests/core/test_sites.py @@ -5,11 +5,10 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core import Composition, Element, Lattice, PeriodicSite, Site, Species from pymatgen.electronic_structure.core import Magmom from pymatgen.util.testing import PymatgenTest +from pytest import approx class TestSite(PymatgenTest): diff --git a/tests/core/test_spectrum.py b/tests/core/test_spectrum.py index 86fb7e7266b..9f6fe473eba 100644 --- a/tests/core/test_spectrum.py +++ b/tests/core/test_spectrum.py @@ -2,11 +2,10 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx -from scipy import stats - from pymatgen.core.spectrum import Spectrum from pymatgen.util.testing import PymatgenTest +from pytest import approx +from scipy import stats class TestSpectrum(PymatgenTest): diff --git a/tests/core/test_structure.py b/tests/core/test_structure.py index e1d911ad40c..ded309c74aa 100644 --- a/tests/core/test_structure.py +++ b/tests/core/test_structure.py @@ -12,8 +12,6 @@ import pytest from monty.json import MontyDecoder, MontyEncoder from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core import SETTINGS, Composition, Element, Lattice, Species from pymatgen.core.operations import SymmOp from pymatgen.core.structure import ( @@ -30,6 +28,7 @@ from pymatgen.io.cif import CifParser from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, PymatgenTest +from pytest import approx try: from ase.atoms import Atoms diff --git a/tests/core/test_surface.py b/tests/core/test_surface.py index 8548e13d74e..7c386a2c7e2 100644 --- a/tests/core/test_surface.py +++ b/tests/core/test_surface.py @@ -6,10 +6,8 @@ import unittest import numpy as np -from numpy.testing import assert_allclose -from pytest import approx - import pymatgen +from numpy.testing import assert_allclose from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Lattice, Structure from pymatgen.core.surface import ( @@ -26,6 +24,7 @@ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.symmetry.groups import SpaceGroup from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx class TestSlab(PymatgenTest): diff --git a/tests/core/test_tensors.py b/tests/core/test_tensors.py index 28548c3c3ba..0be6d488207 100644 --- a/tests/core/test_tensors.py +++ b/tests/core/test_tensors.py @@ -6,12 +6,11 @@ import pytest from monty.serialization import MontyDecoder, loadfn from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.operations import SymmOp from pymatgen.core.tensors import SquareTensor, Tensor, TensorCollection, TensorMapping, itertools, symmetry_reduce from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx class TestTensor(PymatgenTest): diff --git a/tests/core/test_trajectory.py b/tests/core/test_trajectory.py index 444b476c66b..f767412e7c8 100644 --- a/tests/core/test_trajectory.py +++ b/tests/core/test_trajectory.py @@ -6,7 +6,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Molecule, Structure from pymatgen.core.trajectory import Trajectory diff --git a/tests/core/test_units.py b/tests/core/test_units.py index 2abfd5df8f0..f2c2881b189 100644 --- a/tests/core/test_units.py +++ b/tests/core/test_units.py @@ -4,8 +4,6 @@ import pytest from numpy.testing import assert_array_equal -from pytest import approx - from pymatgen.core.units import ( ArrayWithUnit, Energy, @@ -27,6 +25,7 @@ unitized, ) from pymatgen.util.testing import PymatgenTest +from pytest import approx def test_unit_conversions(): diff --git a/tests/core/test_xcfunc.py b/tests/core/test_xcfunc.py index 1cc64bbebc8..8eba2c2dcc8 100644 --- a/tests/core/test_xcfunc.py +++ b/tests/core/test_xcfunc.py @@ -1,7 +1,6 @@ from __future__ import annotations import pytest - from pymatgen.core.xcfunc import XcFunc from pymatgen.util.testing import PymatgenTest diff --git a/tests/electronic_structure/test_bandstructure.py b/tests/electronic_structure/test_bandstructure.py index a6d9d9dfc41..0daa57fcab2 100644 --- a/tests/electronic_structure/test_bandstructure.py +++ b/tests/electronic_structure/test_bandstructure.py @@ -8,8 +8,6 @@ import pytest from monty.serialization import loadfn from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.lattice import Lattice from pymatgen.electronic_structure.bandstructure import ( BandStructureSymmLine, @@ -21,6 +19,7 @@ from pymatgen.electronic_structure.plotter import BSPlotterProjected from pymatgen.io.vasp import BSVasprun from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/electronic_structure/bandstructure" diff --git a/tests/electronic_structure/test_boltztrap.py b/tests/electronic_structure/test_boltztrap.py index 4d1637dd56d..8bdb69a5e19 100644 --- a/tests/electronic_structure/test_boltztrap.py +++ b/tests/electronic_structure/test_boltztrap.py @@ -6,12 +6,11 @@ import pytest from monty.serialization import loadfn -from pytest import approx - from pymatgen.electronic_structure.bandstructure import BandStructure from pymatgen.electronic_structure.boltztrap import BoltztrapAnalyzer, BoltztrapRunner from pymatgen.electronic_structure.core import OrbitalType, Spin from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx try: from ase.io.cube import read_cube diff --git a/tests/electronic_structure/test_boltztrap2.py b/tests/electronic_structure/test_boltztrap2.py index 35926215e2e..b8660fbe61d 100644 --- a/tests/electronic_structure/test_boltztrap2.py +++ b/tests/electronic_structure/test_boltztrap2.py @@ -5,11 +5,10 @@ import numpy as np import pytest from monty.serialization import loadfn -from pytest import approx - from pymatgen.electronic_structure.core import OrbitalType, Spin from pymatgen.io.vasp import Vasprun from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx try: from pymatgen.electronic_structure.boltztrap2 import ( diff --git a/tests/electronic_structure/test_cohp.py b/tests/electronic_structure/test_cohp.py index 2f9f827ba8b..2b44b6eca1b 100644 --- a/tests/electronic_structure/test_cohp.py +++ b/tests/electronic_structure/test_cohp.py @@ -5,8 +5,6 @@ import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.electronic_structure.cohp import ( Cohp, CompleteCohp, @@ -16,6 +14,7 @@ ) from pymatgen.electronic_structure.core import Orbital, Spin from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/electronic_structure/cohp" diff --git a/tests/electronic_structure/test_core.py b/tests/electronic_structure/test_core.py index 2abad986cc4..243d1bef73b 100644 --- a/tests/electronic_structure/test_core.py +++ b/tests/electronic_structure/test_core.py @@ -3,7 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.core import Lattice from pymatgen.electronic_structure.core import Magmom, Orbital, Spin diff --git a/tests/electronic_structure/test_dos.py b/tests/electronic_structure/test_dos.py index c69cbcc54ba..e515f0411c4 100644 --- a/tests/electronic_structure/test_dos.py +++ b/tests/electronic_structure/test_dos.py @@ -8,12 +8,11 @@ from monty.io import zopen from monty.serialization import loadfn from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core import Element, Structure from pymatgen.electronic_structure.core import Orbital, OrbitalType, Spin from pymatgen.electronic_structure.dos import DOS, CompleteDos, FermiDos, LobsterCompleteDos from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/electronic_structure/dos" diff --git a/tests/electronic_structure/test_plotter.py b/tests/electronic_structure/test_plotter.py index 9728bc8d5d4..304c7216083 100644 --- a/tests/electronic_structure/test_plotter.py +++ b/tests/electronic_structure/test_plotter.py @@ -10,8 +10,6 @@ import pytest from matplotlib import rc from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.structure import Structure from pymatgen.electronic_structure.bandstructure import BandStructureSymmLine from pymatgen.electronic_structure.boltztrap import BoltztrapAnalyzer @@ -31,6 +29,7 @@ ) from pymatgen.io.vasp import Vasprun from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx BAND_TEST_DIR = f"{TEST_FILES_DIR}/electronic_structure/bandstructure" diff --git a/tests/entries/test_compatibility.py b/tests/entries/test_compatibility.py index 850a4234d69..7eeea95e918 100644 --- a/tests/entries/test_compatibility.py +++ b/tests/entries/test_compatibility.py @@ -9,11 +9,9 @@ from typing import TYPE_CHECKING from unittest import TestCase +import pymatgen import pytest from monty.json import MontyDecoder -from pytest import approx - -import pymatgen from pymatgen.core import Element, Species from pymatgen.core.composition import Composition from pymatgen.core.lattice import Lattice @@ -34,6 +32,7 @@ ) from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry, ConstantEnergyAdjustment from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx if TYPE_CHECKING: from pymatgen.util.typing import CompositionLike diff --git a/tests/entries/test_computed_entries.py b/tests/entries/test_computed_entries.py index aee3aade715..a7a00c7f88c 100644 --- a/tests/entries/test_computed_entries.py +++ b/tests/entries/test_computed_entries.py @@ -7,8 +7,6 @@ import pytest from monty.json import MontyDecoder -from pytest import approx - from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.entries.compatibility import MaterialsProject2020Compatibility from pymatgen.entries.computed_entries import ( @@ -23,6 +21,7 @@ ) from pymatgen.io.vasp.outputs import Vasprun from pymatgen.util.testing import TEST_FILES_DIR, VASP_OUT_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/entries" diff --git a/tests/entries/test_correction_calculator.py b/tests/entries/test_correction_calculator.py index 02d0fb517a8..c5b8f90a657 100644 --- a/tests/entries/test_correction_calculator.py +++ b/tests/entries/test_correction_calculator.py @@ -3,7 +3,6 @@ from unittest import TestCase import pytest - from pymatgen.entries.correction_calculator import CorrectionCalculator from pymatgen.util.testing import TEST_FILES_DIR diff --git a/tests/entries/test_entry_tools.py b/tests/entries/test_entry_tools.py index 01f4255d8ff..a939dd4106e 100644 --- a/tests/entries/test_entry_tools.py +++ b/tests/entries/test_entry_tools.py @@ -4,7 +4,6 @@ import pytest from monty.serialization import dumpfn, loadfn - from pymatgen.core import Element from pymatgen.entries.computed_entries import ComputedEntry from pymatgen.entries.entry_tools import EntrySet, group_entries_by_composition, group_entries_by_structure diff --git a/tests/entries/test_exp_entries.py b/tests/entries/test_exp_entries.py index b73be4ad8a4..4ebb14d9d70 100644 --- a/tests/entries/test_exp_entries.py +++ b/tests/entries/test_exp_entries.py @@ -4,10 +4,9 @@ from unittest import TestCase from monty.json import MontyDecoder -from pytest import approx - from pymatgen.entries.exp_entries import ExpEntry from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx class TestExpEntry(TestCase): diff --git a/tests/entries/test_mixing_scheme.py b/tests/entries/test_mixing_scheme.py index 860ab42a0e7..33d71c5a5b8 100644 --- a/tests/entries/test_mixing_scheme.py +++ b/tests/entries/test_mixing_scheme.py @@ -108,7 +108,6 @@ import pytest from monty.json import MontyDecoder from numpy.testing import assert_allclose - from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core.lattice import Lattice diff --git a/tests/ext/test_cod.py b/tests/ext/test_cod.py index 9a3164d03d0..e2bc6ba9143 100644 --- a/tests/ext/test_cod.py +++ b/tests/ext/test_cod.py @@ -6,7 +6,6 @@ import pytest import requests - from pymatgen.ext.cod import COD if "CI" in os.environ: # test is slow and flaky, skip in CI. see diff --git a/tests/ext/test_matproj.py b/tests/ext/test_matproj.py index ec5ee928620..8489cf438c3 100644 --- a/tests/ext/test_matproj.py +++ b/tests/ext/test_matproj.py @@ -7,9 +7,6 @@ import pytest import requests from numpy.testing import assert_allclose -from pytest import approx -from ruamel.yaml import YAML - from pymatgen.analysis.phase_diagram import PhaseDiagram from pymatgen.analysis.pourbaix_diagram import PourbaixDiagram, PourbaixEntry from pymatgen.analysis.reaction_calculator import Reaction @@ -24,6 +21,8 @@ from pymatgen.phonon.bandstructure import PhononBandStructureSymmLine from pymatgen.phonon.dos import CompletePhononDos from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx +from ruamel.yaml import YAML try: skip_mprester_tests = requests.get("https://materialsproject.org", timeout=600).status_code != 200 diff --git a/tests/ext/test_optimade.py b/tests/ext/test_optimade.py index ea351acb8c7..2af457c38af 100644 --- a/tests/ext/test_optimade.py +++ b/tests/ext/test_optimade.py @@ -2,7 +2,6 @@ import pytest import requests - from pymatgen.ext.optimade import OptimadeRester from pymatgen.util.testing import PymatgenTest diff --git a/tests/io/abinit/test_abiobjects.py b/tests/io/abinit/test_abiobjects.py index 8bbfa3a7441..5dcbff0b26d 100644 --- a/tests/io/abinit/test_abiobjects.py +++ b/tests/io/abinit/test_abiobjects.py @@ -3,8 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core.structure import Structure from pymatgen.core.units import Ha_to_eV, bohr_to_ang from pymatgen.io.abinit.abiobjects import ( @@ -20,6 +18,7 @@ structure_to_abivars, ) from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx class TestLatticeFromAbivars(PymatgenTest): diff --git a/tests/io/abinit/test_inputs.py b/tests/io/abinit/test_inputs.py index 6becf8953f8..287bfa68401 100644 --- a/tests/io/abinit/test_inputs.py +++ b/tests/io/abinit/test_inputs.py @@ -6,7 +6,6 @@ import numpy as np import pytest from numpy.testing import assert_array_equal - from pymatgen.core.structure import Structure from pymatgen.io.abinit.inputs import ( BasicAbinitInput, diff --git a/tests/io/abinit/test_netcdf.py b/tests/io/abinit/test_netcdf.py index cd01b0e4b90..45f3343315c 100644 --- a/tests/io/abinit/test_netcdf.py +++ b/tests/io/abinit/test_netcdf.py @@ -3,7 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal - from pymatgen.core.structure import Structure from pymatgen.io.abinit import EtsfReader from pymatgen.io.abinit.netcdf import AbinitHeader diff --git a/tests/io/abinit/test_pseudos.py b/tests/io/abinit/test_pseudos.py index 66b9e22f10e..28a48cfcf1e 100644 --- a/tests/io/abinit/test_pseudos.py +++ b/tests/io/abinit/test_pseudos.py @@ -4,10 +4,9 @@ from collections import defaultdict import pytest -from pytest import approx - from pymatgen.io.abinit.pseudos import Pseudo, PseudoTable from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/abinit" diff --git a/tests/io/aims/conftest.py b/tests/io/aims/conftest.py index 5060ba4b259..177aeb38001 100644 --- a/tests/io/aims/conftest.py +++ b/tests/io/aims/conftest.py @@ -3,7 +3,6 @@ import os import pytest - from pymatgen.core import SETTINGS module_dir = os.path.dirname(__file__) diff --git a/tests/io/aims/test_aims_inputs.py b/tests/io/aims/test_aims_inputs.py index d39baafd277..5ded1722eaf 100644 --- a/tests/io/aims/test_aims_inputs.py +++ b/tests/io/aims/test_aims_inputs.py @@ -8,7 +8,6 @@ import pytest from monty.json import MontyDecoder, MontyEncoder from numpy.testing import assert_allclose - from pymatgen.core import SETTINGS from pymatgen.io.aims.inputs import ( ALLOWED_AIMS_CUBE_TYPES, diff --git a/tests/io/aims/test_aims_outputs.py b/tests/io/aims/test_aims_outputs.py index deb0554f658..1d675dad2a3 100644 --- a/tests/io/aims/test_aims_outputs.py +++ b/tests/io/aims/test_aims_outputs.py @@ -6,7 +6,6 @@ from monty.json import MontyDecoder, MontyEncoder from numpy.testing import assert_allclose - from pymatgen.core import Structure from pymatgen.io.aims.outputs import AimsOutput diff --git a/tests/io/aims/test_aims_parsers.py b/tests/io/aims/test_aims_parsers.py index 1d30799122d..e6cb7d6eb7a 100644 --- a/tests/io/aims/test_aims_parsers.py +++ b/tests/io/aims/test_aims_parsers.py @@ -6,7 +6,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.core.tensors import Tensor from pymatgen.io.aims.parsers import ( EV_PER_A3_TO_KBAR, diff --git a/tests/io/aims/test_sets/test_input_set.py b/tests/io/aims/test_sets/test_input_set.py index fb7acf5011d..f5d87fe3881 100644 --- a/tests/io/aims/test_sets/test_input_set.py +++ b/tests/io/aims/test_sets/test_input_set.py @@ -5,7 +5,6 @@ from pathlib import Path import pytest - from pymatgen.core import Structure from pymatgen.io.aims.sets import AimsInputSet diff --git a/tests/io/cp2k/test_inputs.py b/tests/io/cp2k/test_inputs.py index a4b344cda19..9120a789f6d 100644 --- a/tests/io/cp2k/test_inputs.py +++ b/tests/io/cp2k/test_inputs.py @@ -3,8 +3,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core.structure import Molecule, Structure from pymatgen.io.cp2k.inputs import ( BasisFile, @@ -23,6 +21,7 @@ SectionList, ) from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/cp2k" diff --git a/tests/io/cp2k/test_outputs.py b/tests/io/cp2k/test_outputs.py index cd3214af175..fafe2b8be91 100644 --- a/tests/io/cp2k/test_outputs.py +++ b/tests/io/cp2k/test_outputs.py @@ -4,10 +4,9 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.io.cp2k.outputs import Cp2kOutput from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/cp2k" diff --git a/tests/io/cp2k/test_sets.py b/tests/io/cp2k/test_sets.py index ecb644a82a3..c986f02db6a 100644 --- a/tests/io/cp2k/test_sets.py +++ b/tests/io/cp2k/test_sets.py @@ -1,11 +1,10 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.core.structure import Molecule, Structure from pymatgen.io.cp2k.sets import SETTINGS, Cp2kValidationError, DftSet, GaussianTypeOrbitalBasisSet, GthPotential from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/cp2k" diff --git a/tests/io/exciting/test_inputs.py b/tests/io/exciting/test_inputs.py index a5a221221b3..02528023893 100644 --- a/tests/io/exciting/test_inputs.py +++ b/tests/io/exciting/test_inputs.py @@ -3,7 +3,6 @@ from xml.etree import ElementTree from numpy.testing import assert_allclose - from pymatgen.core import Lattice, Structure from pymatgen.io.exciting import ExcitingInput from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/io/feff/test_inputs.py b/tests/io/feff/test_inputs.py index b95bcc536e1..cf06dd92bf9 100644 --- a/tests/io/feff/test_inputs.py +++ b/tests/io/feff/test_inputs.py @@ -4,11 +4,10 @@ from unittest import TestCase from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core import Molecule, Structure from pymatgen.io.feff.inputs import Atoms, Header, Paths, Potential, Tags from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx FEFF_TEST_DIR = f"{TEST_FILES_DIR}/io/feff" diff --git a/tests/io/feff/test_sets.py b/tests/io/feff/test_sets.py index e57e560996c..059d2422550 100644 --- a/tests/io/feff/test_sets.py +++ b/tests/io/feff/test_sets.py @@ -5,7 +5,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.core.structure import Lattice, Molecule, Structure from pymatgen.io.feff.inputs import Atoms, Header, Potential, Tags from pymatgen.io.feff.sets import FEFFDictSet, MPELNESSet, MPEXAFSSet, MPXANESSet diff --git a/tests/io/lammps/test_data.py b/tests/io/lammps/test_data.py index d2fb6541926..e88732fb033 100644 --- a/tests/io/lammps/test_data.py +++ b/tests/io/lammps/test_data.py @@ -10,12 +10,11 @@ import pytest from monty.json import MontyDecoder, MontyEncoder from numpy.testing import assert_allclose -from pytest import approx -from ruamel.yaml import YAML - from pymatgen.core import Element, Lattice, Molecule, Structure from pymatgen.io.lammps.data import CombinedData, ForceField, LammpsBox, LammpsData, Topology, lattice_2_lmpbox from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx +from ruamel.yaml import YAML TEST_DIR = f"{TEST_FILES_DIR}/io/lammps" diff --git a/tests/io/lammps/test_inputs.py b/tests/io/lammps/test_inputs.py index d56765fdad3..a3abdcceb16 100644 --- a/tests/io/lammps/test_inputs.py +++ b/tests/io/lammps/test_inputs.py @@ -6,7 +6,6 @@ import pandas as pd import pytest - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.io.lammps.data import LammpsData diff --git a/tests/io/lammps/test_outputs.py b/tests/io/lammps/test_outputs.py index 6d5e83c3089..e982cf21f18 100644 --- a/tests/io/lammps/test_outputs.py +++ b/tests/io/lammps/test_outputs.py @@ -7,7 +7,6 @@ import numpy as np import pandas as pd from numpy.testing import assert_allclose - from pymatgen.io.lammps.outputs import LammpsDump, parse_lammps_dumps, parse_lammps_log from pymatgen.util.testing import TEST_FILES_DIR diff --git a/tests/io/lobster/test_inputs.py b/tests/io/lobster/test_inputs.py index 6675eaf70c8..98343d785d0 100644 --- a/tests/io/lobster/test_inputs.py +++ b/tests/io/lobster/test_inputs.py @@ -7,8 +7,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core.structure import Structure from pymatgen.electronic_structure.cohp import IcohpCollection from pymatgen.electronic_structure.core import Orbital, Spin @@ -32,6 +30,7 @@ from pymatgen.io.vasp import Vasprun from pymatgen.io.vasp.inputs import Incar, Kpoints, Potcar from pymatgen.util.testing import FAKE_POTCAR_DIR, TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/electronic_structure/cohp" diff --git a/tests/io/lobster/test_lobsterenv.py b/tests/io/lobster/test_lobsterenv.py index 713c4b17eff..a97f2d5ecdd 100644 --- a/tests/io/lobster/test_lobsterenv.py +++ b/tests/io/lobster/test_lobsterenv.py @@ -5,8 +5,6 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.analysis.graphs import StructureGraph from pymatgen.core import Element from pymatgen.core.structure import Structure @@ -15,6 +13,7 @@ from pymatgen.io.lobster import Charge, Icohplist from pymatgen.io.lobster.lobsterenv import LobsterNeighbors from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx __author__ = "Janine George" __copyright__ = "Copyright 2021, The Materials Project" diff --git a/tests/io/pwmat/test_inputs.py b/tests/io/pwmat/test_inputs.py index 1343ef6832a..8ee41de8552 100644 --- a/tests/io/pwmat/test_inputs.py +++ b/tests/io/pwmat/test_inputs.py @@ -3,7 +3,6 @@ import pytest from monty.io import zopen from numpy.testing import assert_allclose - from pymatgen.core import Composition, Structure from pymatgen.io.pwmat.inputs import ( ACExtractor, diff --git a/tests/io/qchem/test_inputs.py b/tests/io/qchem/test_inputs.py index 6a49daba428..c5c0515fa57 100644 --- a/tests/io/qchem/test_inputs.py +++ b/tests/io/qchem/test_inputs.py @@ -5,7 +5,6 @@ import pytest from monty.serialization import loadfn - from pymatgen.core.structure import Molecule from pymatgen.io.qchem.inputs import QCInput from pymatgen.io.qchem.sets import OptSet diff --git a/tests/io/qchem/test_outputs.py b/tests/io/qchem/test_outputs.py index bd900253583..31e54f67b3f 100644 --- a/tests/io/qchem/test_outputs.py +++ b/tests/io/qchem/test_outputs.py @@ -7,8 +7,6 @@ import pytest from monty.serialization import dumpfn, loadfn from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.structure import Molecule from pymatgen.io.qchem.outputs import ( QCOutput, @@ -18,6 +16,7 @@ orbital_coeffs_parser, ) from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx try: from openbabel import openbabel diff --git a/tests/io/qchem/test_sets.py b/tests/io/qchem/test_sets.py index 041003d4428..aa2037dac17 100644 --- a/tests/io/qchem/test_sets.py +++ b/tests/io/qchem/test_sets.py @@ -3,7 +3,6 @@ import os import pytest - from pymatgen.io.qchem.sets import ( ForceSet, FreqSet, diff --git a/tests/io/qchem/test_utils.py b/tests/io/qchem/test_utils.py index 394ab8c142b..260dd803462 100644 --- a/tests/io/qchem/test_utils.py +++ b/tests/io/qchem/test_utils.py @@ -5,7 +5,6 @@ import pytest from monty.io import zopen - from pymatgen.io.qchem.utils import lower_and_check_unique, process_parsed_hess from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/io/test_adf.py b/tests/io/test_adf.py index d3b6785320f..702fbc87d72 100644 --- a/tests/io/test_adf.py +++ b/tests/io/test_adf.py @@ -1,10 +1,9 @@ from __future__ import annotations -from pytest import approx - from pymatgen.core.structure import Molecule from pymatgen.io.adf import AdfInput, AdfKey, AdfOutput, AdfTask from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx __author__ = "Xin Chen, chenxin13@mails.tsinghua.edu.cn" diff --git a/tests/io/test_ase.py b/tests/io/test_ase.py index daf7b0c77bd..5ed59b6afff 100644 --- a/tests/io/test_ase.py +++ b/tests/io/test_ase.py @@ -3,7 +3,6 @@ import numpy as np import pytest from monty.json import MontyDecoder, jsanitize - from pymatgen.core import Composition, Lattice, Molecule, Structure from pymatgen.core.structure import StructureError from pymatgen.io.ase import AseAtomsAdaptor, MSONAtoms diff --git a/tests/io/test_atat.py b/tests/io/test_atat.py index a9562f92511..6cf13574495 100644 --- a/tests/io/test_atat.py +++ b/tests/io/test_atat.py @@ -1,11 +1,10 @@ from __future__ import annotations from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.structure import Structure from pymatgen.io.atat import Mcsqs from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/atat/mcsqs" diff --git a/tests/io/test_babel.py b/tests/io/test_babel.py index e1724176ec4..589330d94c5 100644 --- a/tests/io/test_babel.py +++ b/tests/io/test_babel.py @@ -4,14 +4,13 @@ from unittest import TestCase import pytest -from pytest import approx - from pymatgen.analysis.graphs import MoleculeGraph from pymatgen.analysis.molecule_matcher import MoleculeMatcher from pymatgen.core.structure import Molecule from pymatgen.io.babel import BabelMolAdaptor from pymatgen.io.xyz import XYZ from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx pybel = pytest.importorskip("openbabel.pybel") diff --git a/tests/io/test_cif.py b/tests/io/test_cif.py index f536a4f4021..8abc5127813 100644 --- a/tests/io/test_cif.py +++ b/tests/io/test_cif.py @@ -2,14 +2,13 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import Composition, DummySpecies, Element, Lattice, Species, Structure from pymatgen.electronic_structure.core import Magmom from pymatgen.io.cif import CifBlock, CifParser, CifWriter from pymatgen.symmetry.structure import SymmetrizedStructure from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, PymatgenTest +from pytest import approx try: import pybtex diff --git a/tests/io/test_core.py b/tests/io/test_core.py index f3f08b46185..7c457339249 100644 --- a/tests/io/test_core.py +++ b/tests/io/test_core.py @@ -6,7 +6,6 @@ import pytest from monty.serialization import MontyDecoder - from pymatgen.core.structure import Structure from pymatgen.io.cif import CifParser, CifWriter from pymatgen.io.core import InputFile, InputSet diff --git a/tests/io/test_gaussian.py b/tests/io/test_gaussian.py index 7dc76b78b1b..fc694fa7f97 100644 --- a/tests/io/test_gaussian.py +++ b/tests/io/test_gaussian.py @@ -3,12 +3,11 @@ from unittest import TestCase import pytest -from pytest import approx - from pymatgen.core.structure import Molecule from pymatgen.electronic_structure.core import Spin from pymatgen.io.gaussian import GaussianInput, GaussianOutput from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/gaussian" diff --git a/tests/io/test_jarvis.py b/tests/io/test_jarvis.py index 75c3c78eb82..00a6b4f88f0 100644 --- a/tests/io/test_jarvis.py +++ b/tests/io/test_jarvis.py @@ -1,7 +1,6 @@ from __future__ import annotations import pytest - from pymatgen.core import Structure from pymatgen.io.jarvis import Atoms, JarvisAtomsAdaptor from pymatgen.util.testing import VASP_IN_DIR diff --git a/tests/io/test_lmto.py b/tests/io/test_lmto.py index 393a0b06bae..d398bd431b3 100644 --- a/tests/io/test_lmto.py +++ b/tests/io/test_lmto.py @@ -4,7 +4,6 @@ import numpy as np from numpy.testing import assert_array_equal - from pymatgen.core.structure import Structure from pymatgen.core.units import Ry_to_eV from pymatgen.electronic_structure.core import Spin diff --git a/tests/io/test_nwchem.py b/tests/io/test_nwchem.py index a81dc751ec0..5f4a13c8b5a 100644 --- a/tests/io/test_nwchem.py +++ b/tests/io/test_nwchem.py @@ -4,11 +4,10 @@ from unittest import TestCase import pytest -from pytest import approx - from pymatgen.core.structure import Molecule from pymatgen.io.nwchem import NwInput, NwInputError, NwOutput, NwTask from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/nwchem" diff --git a/tests/io/test_openff.py b/tests/io/test_openff.py index 2152b3e955e..41057daeb0b 100644 --- a/tests/io/test_openff.py +++ b/tests/io/test_openff.py @@ -5,7 +5,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.analysis.graphs import MoleculeGraph from pymatgen.analysis.local_env import OpenBabelNN from pymatgen.core import Molecule diff --git a/tests/io/test_packmol.py b/tests/io/test_packmol.py index 15d3c86b547..e82f627e310 100644 --- a/tests/io/test_packmol.py +++ b/tests/io/test_packmol.py @@ -6,7 +6,6 @@ from subprocess import TimeoutExpired import pytest - from pymatgen.analysis.molecule_matcher import MoleculeMatcher from pymatgen.core import Molecule from pymatgen.io.packmol import PackmolBoxGen diff --git a/tests/io/test_phonopy.py b/tests/io/test_phonopy.py index f12cce1f0a0..c3bc2e18187 100644 --- a/tests/io/test_phonopy.py +++ b/tests/io/test_phonopy.py @@ -7,8 +7,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core import Element from pymatgen.io.phonopy import ( CompletePhononDos, @@ -29,6 +27,7 @@ get_thermal_displacement_matrices, ) from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx try: from phonopy import Phonopy diff --git a/tests/io/test_pwscf.py b/tests/io/test_pwscf.py index 6666b3c7938..7137f7f97a8 100644 --- a/tests/io/test_pwscf.py +++ b/tests/io/test_pwscf.py @@ -3,10 +3,9 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.io.pwscf import PWInput, PWInputError, PWOutput from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/pwscf" diff --git a/tests/io/test_res.py b/tests/io/test_res.py index 5617ad86492..2e05ee5769a 100644 --- a/tests/io/test_res.py +++ b/tests/io/test_res.py @@ -1,11 +1,10 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.core import Structure from pymatgen.io.res import AirssProvider, ResParseError, ResWriter from pymatgen.util.testing import TEST_FILES_DIR +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/res" diff --git a/tests/io/test_shengbte.py b/tests/io/test_shengbte.py index 67779c458a1..60bc58b2a4a 100644 --- a/tests/io/test_shengbte.py +++ b/tests/io/test_shengbte.py @@ -4,7 +4,6 @@ import pytest from numpy.testing import assert_array_equal - from pymatgen.io.shengbte import Control from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/io/test_template_input.py b/tests/io/test_template_input.py index 3be8ae45374..313fa8fc540 100644 --- a/tests/io/test_template_input.py +++ b/tests/io/test_template_input.py @@ -3,7 +3,6 @@ import os import pytest - from pymatgen.io.template import TemplateInputGen from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/io/test_wannier90.py b/tests/io/test_wannier90.py index ca4704dfe72..32757970eeb 100644 --- a/tests/io/test_wannier90.py +++ b/tests/io/test_wannier90.py @@ -5,10 +5,9 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.io.wannier90 import Unk from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/io/wannier90" diff --git a/tests/io/test_xcrysden.py b/tests/io/test_xcrysden.py index 3a13a2cf7b5..88db940bcf8 100644 --- a/tests/io/test_xcrysden.py +++ b/tests/io/test_xcrysden.py @@ -1,7 +1,6 @@ from __future__ import annotations import numpy as np - from pymatgen.core.structure import Structure from pymatgen.io.xcrysden import XSF from pymatgen.util.testing import PymatgenTest diff --git a/tests/io/test_xyz.py b/tests/io/test_xyz.py index 2314eefdc8e..77e3c582efa 100644 --- a/tests/io/test_xyz.py +++ b/tests/io/test_xyz.py @@ -4,12 +4,11 @@ import pandas as pd import pytest -from pytest import approx - from pymatgen.core import Structure from pymatgen.core.structure import Molecule from pymatgen.io.xyz import XYZ from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR +from pytest import approx class TestXYZ(TestCase): diff --git a/tests/io/test_zeopp.py b/tests/io/test_zeopp.py index 2f38d24086c..3656530cb4e 100644 --- a/tests/io/test_zeopp.py +++ b/tests/io/test_zeopp.py @@ -4,8 +4,6 @@ from unittest import TestCase import pytest -from pytest import approx - from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.core import Molecule, Species, Structure from pymatgen.io.zeopp import ( @@ -16,6 +14,7 @@ get_voronoi_nodes, ) from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR +from pytest import approx try: import zeo diff --git a/tests/io/vasp/test_inputs.py b/tests/io/vasp/test_inputs.py index cfd2769ff5e..aba859bea1f 100644 --- a/tests/io/vasp/test_inputs.py +++ b/tests/io/vasp/test_inputs.py @@ -15,8 +15,6 @@ from monty.io import zopen from monty.serialization import loadfn from numpy.testing import assert_allclose -from pytest import MonkeyPatch, approx - from pymatgen.core import SETTINGS from pymatgen.core.composition import Composition from pymatgen.core.structure import Structure @@ -36,6 +34,7 @@ _gen_potcar_summary_stats, ) from pymatgen.util.testing import FAKE_POTCAR_DIR, TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import MonkeyPatch, approx # make sure _gen_potcar_summary_stats runs and works with all tests in this file _summ_stats = _gen_potcar_summary_stats(append=False, vasp_psp_dir=str(FAKE_POTCAR_DIR), summary_stats_filename=None) diff --git a/tests/io/vasp/test_optics.py b/tests/io/vasp/test_optics.py index 3a2caf7a94a..3e660a58797 100644 --- a/tests/io/vasp/test_optics.py +++ b/tests/io/vasp/test_optics.py @@ -4,7 +4,6 @@ import pytest import scipy.special from numpy.testing import assert_allclose - from pymatgen.io.vasp.optics import DielectricFunctionCalculator, delta_func, delta_methfessel_paxton, step_func from pymatgen.io.vasp.outputs import Vasprun from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/io/vasp/test_outputs.py b/tests/io/vasp/test_outputs.py index a5faadac3ca..fb2a9697842 100644 --- a/tests/io/vasp/test_outputs.py +++ b/tests/io/vasp/test_outputs.py @@ -13,8 +13,6 @@ import pytest from monty.io import zopen from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core import Element from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure @@ -42,6 +40,7 @@ ) from pymatgen.io.wannier90 import Unk from pymatgen.util.testing import FAKE_POTCAR_DIR, TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx try: import h5py diff --git a/tests/io/vasp/test_sets.py b/tests/io/vasp/test_sets.py index ceb2d8e5424..9f1a0c40bbb 100644 --- a/tests/io/vasp/test_sets.py +++ b/tests/io/vasp/test_sets.py @@ -10,8 +10,6 @@ from monty.json import MontyDecoder from monty.serialization import loadfn from numpy.testing import assert_allclose -from pytest import MonkeyPatch, approx, mark - from pymatgen.analysis.structure_matcher import StructureMatcher from pymatgen.core import SETTINGS, Lattice, Species, Structure from pymatgen.core.composition import Composition @@ -55,6 +53,7 @@ ) from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.testing import FAKE_POTCAR_DIR, TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import MonkeyPatch, approx, mark TEST_DIR = f"{TEST_FILES_DIR}/io/vasp" diff --git a/tests/io/xtb/test_outputs.py b/tests/io/xtb/test_outputs.py index c4d3fe69777..dd1afc090af 100644 --- a/tests/io/xtb/test_outputs.py +++ b/tests/io/xtb/test_outputs.py @@ -2,12 +2,11 @@ import os -from pytest import approx - from pymatgen.core.structure import Molecule from pymatgen.io.qchem.outputs import check_for_structure_changes from pymatgen.io.xtb.outputs import CRESTOutput from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx try: from openbabel import openbabel diff --git a/tests/optimization/test_linear_assignment.py b/tests/optimization/test_linear_assignment.py index 961a5032b6d..163a1ac3132 100644 --- a/tests/optimization/test_linear_assignment.py +++ b/tests/optimization/test_linear_assignment.py @@ -4,9 +4,8 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.optimization.linear_assignment import LinearAssignment +from pytest import approx class TestLinearAssignment(TestCase): diff --git a/tests/optimization/test_neighbors.py b/tests/optimization/test_neighbors.py index a6374c1f929..8d66dcd9b91 100644 --- a/tests/optimization/test_neighbors.py +++ b/tests/optimization/test_neighbors.py @@ -1,7 +1,6 @@ from __future__ import annotations import numpy as np - from pymatgen.core.lattice import Lattice from pymatgen.optimization.neighbors import find_points_in_spheres from pymatgen.util.testing import PymatgenTest diff --git a/tests/phonon/test_bandstructure.py b/tests/phonon/test_bandstructure.py index 7f449792544..6f46c3dca38 100644 --- a/tests/phonon/test_bandstructure.py +++ b/tests/phonon/test_bandstructure.py @@ -4,11 +4,10 @@ import json from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.electronic_structure.bandstructure import Kpoint from pymatgen.phonon.bandstructure import PhononBandStructureSymmLine from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/electronic_structure/bandstructure" diff --git a/tests/phonon/test_dos.py b/tests/phonon/test_dos.py index 0fecb44d6fd..013cfa7e8b3 100644 --- a/tests/phonon/test_dos.py +++ b/tests/phonon/test_dos.py @@ -5,11 +5,10 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.core import Element from pymatgen.phonon.dos import CompletePhononDos, PhononDos from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/phonon/dos" diff --git a/tests/phonon/test_gruneisen.py b/tests/phonon/test_gruneisen.py index 5b84acbc33f..321702140af 100644 --- a/tests/phonon/test_gruneisen.py +++ b/tests/phonon/test_gruneisen.py @@ -2,12 +2,11 @@ import matplotlib.pyplot as plt import pytest -from pytest import approx - from pymatgen.io.phonopy import get_gruneisen_ph_bs_symm_line, get_gruneisenparameter from pymatgen.phonon.gruneisen import GruneisenParameter from pymatgen.phonon.plotter import GruneisenPhononBandStructureSymmLine, GruneisenPhononBSPlotter, GruneisenPlotter from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx try: import phonopy diff --git a/tests/phonon/test_ir_spectra.py b/tests/phonon/test_ir_spectra.py index 7a7eb40f4ce..11bae39d9f7 100644 --- a/tests/phonon/test_ir_spectra.py +++ b/tests/phonon/test_ir_spectra.py @@ -1,7 +1,6 @@ from __future__ import annotations from monty.serialization import loadfn - from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/phonon/test_plotter.py b/tests/phonon/test_plotter.py index 01e657e7eaf..2e0c762804b 100644 --- a/tests/phonon/test_plotter.py +++ b/tests/phonon/test_plotter.py @@ -6,7 +6,6 @@ import matplotlib.pyplot as plt import pytest from numpy.testing import assert_allclose - from pymatgen.phonon import CompletePhononDos, PhononBandStructureSymmLine from pymatgen.phonon.plotter import PhononBSPlotter, PhononDosPlotter, ThermoPlotter from pymatgen.util.testing import TEST_FILES_DIR diff --git a/tests/phonon/test_thermal_displacements.py b/tests/phonon/test_thermal_displacements.py index 725c197263f..315680e28d8 100644 --- a/tests/phonon/test_thermal_displacements.py +++ b/tests/phonon/test_thermal_displacements.py @@ -2,11 +2,10 @@ import numpy as np from numpy.testing import assert_allclose -from pytest import approx - from pymatgen.core.structure import Structure from pymatgen.phonon.thermal_displacements import ThermalDisplacementMatrices from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/phonon/thermal_displacement_matrices" diff --git a/tests/symmetry/test_analyzer.py b/tests/symmetry/test_analyzer.py index a1a19edc4ba..cd0d702f307 100644 --- a/tests/symmetry/test_analyzer.py +++ b/tests/symmetry/test_analyzer.py @@ -5,8 +5,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose -from pytest import approx, raises - from pymatgen.core import Lattice, Molecule, PeriodicSite, Site, Species, Structure from pymatgen.io.vasp.outputs import Vasprun from pymatgen.symmetry.analyzer import ( @@ -18,6 +16,7 @@ ) from pymatgen.symmetry.structure import SymmetrizedStructure from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, VASP_OUT_DIR, PymatgenTest +from pytest import approx, raises TEST_DIR = f"{TEST_FILES_DIR}/symmetry/analyzer" diff --git a/tests/symmetry/test_groups.py b/tests/symmetry/test_groups.py index 273a6a6d6c3..cee6793d2e7 100644 --- a/tests/symmetry/test_groups.py +++ b/tests/symmetry/test_groups.py @@ -2,11 +2,10 @@ import numpy as np import pytest -from pytest import approx - from pymatgen.core.lattice import Lattice from pymatgen.core.operations import SymmOp from pymatgen.symmetry.groups import SYMM_DATA, PointGroup, SpaceGroup +from pytest import approx __author__ = "Shyue Ping Ong" __copyright__ = "Copyright 2012, The Materials Virtual Lab" diff --git a/tests/symmetry/test_kpath_hin.py b/tests/symmetry/test_kpath_hin.py index 61d71fd9dd3..63dab783ac2 100644 --- a/tests/symmetry/test_kpath_hin.py +++ b/tests/symmetry/test_kpath_hin.py @@ -1,12 +1,11 @@ from __future__ import annotations import pytest -from pytest import approx - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.symmetry.kpath import KPathSeek from pymatgen.util.testing import PymatgenTest +from pytest import approx try: from seekpath import get_path diff --git a/tests/symmetry/test_kpath_lm.py b/tests/symmetry/test_kpath_lm.py index dbc8187e6a9..e72ff7ef9d7 100644 --- a/tests/symmetry/test_kpath_lm.py +++ b/tests/symmetry/test_kpath_lm.py @@ -1,14 +1,13 @@ from __future__ import annotations import numpy as np -from pytest import approx - from pymatgen.analysis.magnetism.analyzer import CollinearMagneticStructureAnalyzer from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.symmetry.kpath import KPathLatimerMunro from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx class TestKPathLatimerMunro(PymatgenTest): diff --git a/tests/symmetry/test_kpath_sc.py b/tests/symmetry/test_kpath_sc.py index d88c97f79dd..2dd37d0d77e 100644 --- a/tests/symmetry/test_kpath_sc.py +++ b/tests/symmetry/test_kpath_sc.py @@ -1,12 +1,11 @@ from __future__ import annotations import numpy as np -from pytest import approx - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.symmetry.kpath import KPathSetyawanCurtarolo from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest +from pytest import approx TEST_DIR = f"{TEST_FILES_DIR}/symmetry/space_group_structs" diff --git a/tests/symmetry/test_kpaths.py b/tests/symmetry/test_kpaths.py index 2c907e87135..f13ae4b7a9b 100644 --- a/tests/symmetry/test_kpaths.py +++ b/tests/symmetry/test_kpaths.py @@ -4,7 +4,6 @@ import pytest from monty.serialization import loadfn - from pymatgen.core.lattice import Lattice from pymatgen.core.structure import Structure from pymatgen.symmetry.bandstructure import HighSymmKpath diff --git a/tests/symmetry/test_maggroups.py b/tests/symmetry/test_maggroups.py index 72f184d553f..cc4d529b87c 100644 --- a/tests/symmetry/test_maggroups.py +++ b/tests/symmetry/test_maggroups.py @@ -2,7 +2,6 @@ import numpy as np from numpy.testing import assert_allclose - from pymatgen.core.lattice import Lattice from pymatgen.symmetry.groups import SpaceGroup from pymatgen.symmetry.maggroups import MagneticSpaceGroup diff --git a/tests/symmetry/test_settings.py b/tests/symmetry/test_settings.py index 24db5b2b6dc..a6a8ff3e80a 100644 --- a/tests/symmetry/test_settings.py +++ b/tests/symmetry/test_settings.py @@ -4,7 +4,6 @@ import numpy as np from numpy.testing import assert_allclose - from pymatgen.symmetry.settings import JonesFaithfulTransformation, Lattice, SymmOp __author__ = "Matthew Horton" diff --git a/tests/symmetry/test_site_symmetries.py b/tests/symmetry/test_site_symmetries.py index 21972990e8b..c5696b31f46 100644 --- a/tests/symmetry/test_site_symmetries.py +++ b/tests/symmetry/test_site_symmetries.py @@ -4,7 +4,6 @@ import json from monty.json import MontyDecoder - from pymatgen.symmetry import site_symmetries as ss from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest diff --git a/tests/test_cli.py b/tests/test_cli.py index 225664313dc..1893d0a5738 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,7 +4,6 @@ from typing import TYPE_CHECKING import pytest - from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR if TYPE_CHECKING: diff --git a/tests/transformations/test_advanced_transformations.py b/tests/transformations/test_advanced_transformations.py index 5dcc3fae6d4..ea0feed09b8 100644 --- a/tests/transformations/test_advanced_transformations.py +++ b/tests/transformations/test_advanced_transformations.py @@ -7,8 +7,6 @@ import pytest from monty.serialization import loadfn from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.analysis.energy_models import IsingModel, SymmetryModel from pymatgen.analysis.gb.grain import GrainBoundaryGenerator from pymatgen.core import Lattice, Molecule, Species, Structure @@ -41,6 +39,7 @@ SubstitutionTransformation, ) from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR, PymatgenTest +from pytest import approx try: import hiphive diff --git a/tests/transformations/test_site_transformations.py b/tests/transformations/test_site_transformations.py index 3b30670fad3..cddc9bfb53f 100644 --- a/tests/transformations/test_site_transformations.py +++ b/tests/transformations/test_site_transformations.py @@ -6,7 +6,6 @@ import numpy as np import pytest from numpy.testing import assert_allclose - from pymatgen.core.structure import Molecule, Structure from pymatgen.transformations.site_transformations import ( AddSitePropertyTransformation, diff --git a/tests/transformations/test_standard_transformations.py b/tests/transformations/test_standard_transformations.py index 331ed94b9c9..029fad0f8e7 100644 --- a/tests/transformations/test_standard_transformations.py +++ b/tests/transformations/test_standard_transformations.py @@ -9,8 +9,6 @@ import numpy as np import pytest from monty.json import MontyDecoder -from pytest import approx - from pymatgen.core import Element, PeriodicSite from pymatgen.core.lattice import Lattice from pymatgen.symmetry.structure import SymmetrizedStructure @@ -36,6 +34,7 @@ SupercellTransformation, ) from pymatgen.util.testing import TEST_FILES_DIR, VASP_IN_DIR +from pytest import approx enumlib_present = which("enum.x") and which("makestr.x") diff --git a/tests/util/test_coord.py b/tests/util/test_coord.py index 15cfab9d7dc..433831f3c5d 100644 --- a/tests/util/test_coord.py +++ b/tests/util/test_coord.py @@ -6,10 +6,9 @@ import numpy as np import pytest from numpy.testing import assert_allclose, assert_array_equal -from pytest import approx - from pymatgen.core.lattice import Lattice from pymatgen.util import coord +from pytest import approx class TestCoordUtils: diff --git a/tests/util/test_num.py b/tests/util/test_num.py index e391ba92412..8891e37ca0f 100644 --- a/tests/util/test_num.py +++ b/tests/util/test_num.py @@ -1,7 +1,6 @@ from __future__ import annotations import pytest - from pymatgen.util.num import round_to_sigfigs diff --git a/tests/util/test_plotting.py b/tests/util/test_plotting.py index ae0b6eca76b..85d91b457e8 100644 --- a/tests/util/test_plotting.py +++ b/tests/util/test_plotting.py @@ -1,7 +1,6 @@ from __future__ import annotations import matplotlib.pyplot as plt - from pymatgen.util.plotting import periodic_table_heatmap, van_arkel_triangle from pymatgen.util.testing import PymatgenTest diff --git a/tests/util/test_provenance.py b/tests/util/test_provenance.py index 12ebbb2b13b..9019ee50ebd 100644 --- a/tests/util/test_provenance.py +++ b/tests/util/test_provenance.py @@ -7,7 +7,6 @@ import numpy as np import pytest - from pymatgen.core.structure import Molecule, Structure from pymatgen.util.provenance import Author, HistoryNode, StructureNL diff --git a/tests/util/test_string.py b/tests/util/test_string.py index 5c04a1d51ea..1bb0785cef3 100644 --- a/tests/util/test_string.py +++ b/tests/util/test_string.py @@ -2,7 +2,6 @@ import numpy as np import pytest - from pymatgen.core import Structure from pymatgen.util.string import ( Stringify, diff --git a/tests/util/test_typing.py b/tests/util/test_typing.py index ec04deffbb7..7790c437cd4 100644 --- a/tests/util/test_typing.py +++ b/tests/util/test_typing.py @@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, get_args import pytest - from pymatgen.core import Composition, DummySpecies, Element, Species from pymatgen.entries import Entry from pymatgen.util.typing import CompositionLike, EntryLike, PathLike, PbcLike, SpeciesLike diff --git a/tests/vis/test_plotters.py b/tests/vis/test_plotters.py index ca0594b4c2d..ab9c95ad547 100644 --- a/tests/vis/test_plotters.py +++ b/tests/vis/test_plotters.py @@ -6,7 +6,6 @@ import matplotlib.pyplot as plt import numpy as np from monty.json import MontyDecoder - from pymatgen.analysis.xas.spectrum import XAS from pymatgen.util.testing import TEST_FILES_DIR, PymatgenTest from pymatgen.vis.plotters import SpectrumPlotter