Skip to content

Commit

Permalink
CodeRabbit formatting suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
kavanase committed Jan 23, 2025
1 parent d375640 commit 038e3e3
Show file tree
Hide file tree
Showing 27 changed files with 812 additions and 772 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ v.3.0.0
states etc), with a number of convenience functions (e.g. for scanning temperature / chemical potentials
etc, optimising output properties over many-dimensional chemical potential spaces etc). Usage
demonstrated in https://doped.readthedocs.io/en/latest/fermisolver_tutorial.html.
- Add ``is_shallow`` function, and ``DefectThermodynamics._get_in_gap_fermi_level_stability_window``
- Add ``is_shallow`` ``DefectEntry`` property, and ``DefectThermodynamics._get_in_gap_fermi_level_stability_window``
method. Shallow defect states now automatically excluded from formation energy diagram plots for cleaner
outputs, controllable with the ``unstable_entries`` kwarg. Large charge correction errors for
shallow/unstable defects (typically higher and a common indication of 'false charge state' behaviour)
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"python": ("https://docs.python.org/3.9", None),
"python": ("https://docs.python.org/3.12", None),
"numpy": ("http://docs.scipy.org/doc/numpy/", None),
"pymatgen": ("http://pymatgen.org/", None),
"matplotlib": ("http://matplotlib.org", None),
Expand Down
17 changes: 11 additions & 6 deletions doped/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,14 @@

import contextlib
import inspect
import multiprocessing
import warnings
from importlib.metadata import PackageNotFoundError, version

from packaging.version import parse
from pymatgen.io.vasp.inputs import UnknownPotcarWarning
from pymatgen.io.vasp.sets import BadInputSetWarning

# if date.today().weekday() in [5, 6]:
# print("""Working on the weekend, like usual...\n""")
# if date.today().weekday() == 5:
# print("Seriously though, everyone knows Saturday's for the boys/girls...\n")
# Killed by multiprocessing, # never forget


def _check_pmg_compatibility():
try:
Expand Down Expand Up @@ -90,3 +85,13 @@ def _doped_obj_properties_methods(obj):
methods.add(k)
properties = {name for name, value in inspect.getmembers(type(obj)) if isinstance(value, property)}
return attrs | properties, methods


def get_mp_context():
"""
Get a multiprocessing context that is compatible with the current OS.
"""
try:
return multiprocessing.get_context("forkserver")
except ValueError: # forkserver not available on Windows OS
return multiprocessing.get_context("spawn")
122 changes: 62 additions & 60 deletions doped/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
"""

import contextlib
import multiprocessing
import os
import warnings
from multiprocessing import cpu_count
from typing import TYPE_CHECKING, Optional, Union
from typing import TYPE_CHECKING, Union

import numpy as np
from monty.json import MontyDecoder
Expand All @@ -29,9 +27,14 @@
from pymatgen.util.typing import PathLike
from tqdm import tqdm

from doped import _doped_obj_properties_methods, _ignore_pmg_warnings
from doped import _doped_obj_properties_methods, _ignore_pmg_warnings, get_mp_context
from doped.core import DefectEntry, guess_and_set_oxi_states_with_timeout
from doped.generation import get_defect_name_from_defect, get_defect_name_from_entry, name_defect_entries
from doped.generation import (
get_defect_name_from_defect,
get_defect_name_from_entry,
name_defect_entries,
sort_defect_entries,
)
from doped.thermodynamics import DefectThermodynamics
from doped.utils.efficiency import _parse_site_species_str, get_voronoi_nodes
from doped.utils.parsing import (
Expand Down Expand Up @@ -65,15 +68,13 @@
if TYPE_CHECKING:
from easyunfold.procar import Procar as EasyunfoldProcar

mp = multiprocessing.get_context("forkserver") # https://github.com/python/cpython/pull/100229


def _custom_formatwarning(
message: Union[Warning, str],
message: Warning | str,
category: type[Warning],
filename: str,
lineno: int,
line: Optional[str] = None,
line: str | None = None,
) -> str:
"""
Reformat warnings to just print the warning message, and add two newlines
Expand Down Expand Up @@ -102,7 +103,7 @@ def _custom_formatwarning(

def _convert_dielectric_to_tensor(dielectric):
# check if dielectric in required 3x3 matrix format
if not isinstance(dielectric, (float, int)):
if not isinstance(dielectric, float | int):
dielectric = np.array(dielectric)
if dielectric.shape == (3,):
dielectric = np.diag(dielectric)
Expand All @@ -119,7 +120,7 @@ def _convert_dielectric_to_tensor(dielectric):


def check_and_set_defect_entry_name(
defect_entry: DefectEntry, possible_defect_name: str = "", bulk_symm_ops: Optional[list] = None
defect_entry: DefectEntry, possible_defect_name: str = "", bulk_symm_ops: list | None = None
) -> None:
"""
Check that ``possible_defect_name`` is a recognised format by doped (i.e.
Expand Down Expand Up @@ -172,7 +173,7 @@ def defect_from_structures(
bulk_supercell: Structure,
defect_supercell: Structure,
return_all_info: bool = False,
bulk_voronoi_node_dict: Optional[dict] = None,
bulk_voronoi_node_dict: dict | None = None,
skip_atom_mapping_check: bool = False,
**kwargs,
):
Expand Down Expand Up @@ -421,6 +422,7 @@ def cos_dissimilarity(vec1, vec2):
defect_supercell.lattice.get_cartesian_coords(
np.array(list(cos_diss_frac_coords_dict.values()))
),
strict=False,
)
)
return np.average( # weighted centre of mass
Expand Down Expand Up @@ -455,12 +457,12 @@ def defect_name_from_structures(bulk_structure: Structure, defect_structure: Str
def defect_entry_from_paths(
defect_path: PathLike,
bulk_path: PathLike,
dielectric: Optional[Union[float, int, np.ndarray, list]] = None,
charge_state: Optional[int] = None,
initial_defect_structure_path: Optional[PathLike] = None,
dielectric: float | np.ndarray | list | None = None,
charge_state: int | None = None,
initial_defect_structure_path: PathLike | None = None,
skip_corrections: bool = False,
error_tolerance: float = 0.05,
bulk_band_gap_vr: Optional[Union[PathLike, Vasprun]] = None,
bulk_band_gap_vr: PathLike | Vasprun | None = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -551,15 +553,15 @@ class DefectsParser:
def __init__(
self,
output_path: PathLike = ".",
dielectric: Optional[Union[float, int, np.ndarray, list]] = None,
subfolder: Optional[PathLike] = None,
bulk_path: Optional[PathLike] = None,
dielectric: float | np.ndarray | list | None = None,
subfolder: PathLike | None = None,
bulk_path: PathLike | None = None,
skip_corrections: bool = False,
error_tolerance: float = 0.05,
bulk_band_gap_vr: Optional[Union[PathLike, Vasprun]] = None,
processes: Optional[int] = None,
json_filename: Optional[Union[PathLike, bool]] = None,
parse_projected_eigen: Optional[bool] = None,
bulk_band_gap_vr: PathLike | Vasprun | None = None,
processes: int | None = None,
json_filename: PathLike | bool | None = None,
parse_projected_eigen: bool | None = None,
**kwargs,
):
r"""
Expand Down Expand Up @@ -851,7 +853,7 @@ def __init__(

# try parsing the bulk oxidation states first, for later assigning defect "oxi_state"s (i.e.
# fully ionised charge states):
self._bulk_oxi_states: Union[Structure, Composition, dict, bool] = False
self._bulk_oxi_states: Structure | Composition | dict | bool = False
if bulk_struct_w_oxi := guess_and_set_oxi_states_with_timeout(
self.bulk_vr.final_structure, break_early_if_expensive=True
):
Expand All @@ -865,9 +867,10 @@ def __init__(
parsed_defect_entries = []
parsing_warnings = []

mp = get_mp_context() # https://github.com/python/cpython/pull/100229
if self.processes is None: # multiprocessing?
self.processes = min(max(1, cpu_count() - 1), len(self.defect_folders) - 1) # only
# multiprocess as much as makes sense, if only a handful of defect folders
# only multiprocess as much as makes sense, if only a handful of defect folders:
self.processes = min(max(1, mp.cpu_count() - 1), len(self.defect_folders) - 1)

if self.processes <= 1: # no multiprocessing
with tqdm(self.defect_folders, desc="Parsing defect calculations") as pbar:
Expand Down Expand Up @@ -1182,18 +1185,17 @@ def _mention_bulk_path_subfolder_for_correction_warnings(warning: str) -> str:
self.defect_dict.update(
{defect_entry.name: defect_entry for defect_entry in new_named_defect_entries_dict.values()}
)
self.defect_dict = sort_defect_entries(self.defect_dict) # sort defect entries

FNV_correction_errors = []
eFNV_correction_errors = []
defect_thermo = self.get_defect_thermodynamics(check_compatibility=False, skip_vbm_check=True)
for name, defect_entry in self.defect_dict.items():
from doped.utils.eigenvalues import is_shallow

# first check if it's a stable defect:
fermi_stability_window = defect_thermo._get_in_gap_fermi_level_stability_window(defect_entry)

if fermi_stability_window < 0 or ( # Note we avoid the prune_to_stable_entries() method here
is_shallow(defect_entry) # as this would require two ``DefectThermodynamics`` inits...
defect_entry.is_shallow # as this would require two ``DefectThermodynamics`` inits...
and fermi_stability_window
< kwargs.get(
"shallow_charge_stability_tolerance",
Expand Down Expand Up @@ -1375,7 +1377,7 @@ def _update_pbar_and_return_warnings_from_parsing(
self,
defect_entry: DefectEntry,
warnings_string: str = "",
defect_folder: Optional[str] = None,
defect_folder: str | None = None,
pbar: tqdm = None,
):
if pbar:
Expand Down Expand Up @@ -1477,13 +1479,13 @@ def _parse_single_defect(self, defect_folder):

def get_defect_thermodynamics(
self,
chempots: Optional[dict] = None,
el_refs: Optional[dict] = None,
vbm: Optional[float] = None,
band_gap: Optional[float] = None,
chempots: dict | None = None,
el_refs: dict | None = None,
vbm: float | None = None,
band_gap: float | None = None,
dist_tol: float = 1.5,
check_compatibility: bool = True,
bulk_dos: Optional[FermiDos] = None,
bulk_dos: FermiDos | None = None,
skip_vbm_check: bool = False,
) -> DefectThermodynamics:
r"""
Expand Down Expand Up @@ -1620,8 +1622,8 @@ def __repr__(self):

def _parse_vr_and_poss_procar(
vr_path: PathLike,
parse_projected_eigen: Optional[bool] = None,
output_path: Optional[PathLike] = None,
parse_projected_eigen: bool | None = None,
output_path: PathLike | None = None,
label: str = "bulk",
parse_procar: bool = True,
):
Expand Down Expand Up @@ -1664,11 +1666,11 @@ class DefectParser:
def __init__(
self,
defect_entry: DefectEntry,
defect_vr: Optional[Vasprun] = None,
bulk_vr: Optional[Vasprun] = None,
defect_vr: Vasprun | None = None,
bulk_vr: Vasprun | None = None,
skip_corrections: bool = False,
error_tolerance: float = 0.05,
parse_projected_eigen: Optional[bool] = None,
parse_projected_eigen: bool | None = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -1725,16 +1727,16 @@ def __init__(
def from_paths(
cls,
defect_path: PathLike,
bulk_path: Optional[PathLike] = None,
bulk_vr: Optional[Vasprun] = None,
bulk_procar: Optional[Union["EasyunfoldProcar", Procar]] = None,
dielectric: Optional[Union[float, int, np.ndarray, list]] = None,
charge_state: Optional[int] = None,
initial_defect_structure_path: Optional[PathLike] = None,
bulk_path: PathLike | None = None,
bulk_vr: Vasprun | None = None,
bulk_procar: Union["EasyunfoldProcar", Procar] | None = None,
dielectric: float | np.ndarray | list | None = None,
charge_state: int | None = None,
initial_defect_structure_path: PathLike | None = None,
skip_corrections: bool = False,
error_tolerance: float = 0.05,
bulk_band_gap_vr: Optional[Union[PathLike, Vasprun]] = None,
parse_projected_eigen: Optional[bool] = None,
bulk_band_gap_vr: PathLike | Vasprun | None = None,
parse_projected_eigen: bool | None = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -2257,7 +2259,7 @@ def _convert_anisotropic_dielectric_to_isotropic_harmonic_mean(

return skip_corrections

def load_FNV_data(self, bulk_locpot_dict: Optional[dict] = None):
def load_FNV_data(self, bulk_locpot_dict: dict | None = None):
"""
Load metadata required for performing Freysoldt correction (i.e. LOCPOT
planar-averaged potential dictionary).
Expand Down Expand Up @@ -2314,7 +2316,7 @@ def load_FNV_data(self, bulk_locpot_dict: Optional[dict] = None):

return bulk_locpot_dict

def load_eFNV_data(self, bulk_site_potentials: Optional[list] = None):
def load_eFNV_data(self, bulk_site_potentials: list | None = None):
"""
Load metadata required for performing Kumagai correction (i.e. atomic
site potentials from the OUTCAR files).
Expand Down Expand Up @@ -2483,10 +2485,10 @@ def _get_vr_dict_without_proj_eigenvalues(vr):

def load_bulk_gap_data(
self,
bulk_band_gap_vr: Optional[Union[PathLike, Vasprun]] = None,
bulk_band_gap_vr: PathLike | Vasprun | None = None,
use_MP: bool = False,
mpid: Optional[str] = None,
api_key: Optional[str] = None,
mpid: str | None = None,
api_key: str | None = None,
):
r"""
Load the ``"gap"`` and ``"vbm"`` values for the parsed
Expand Down Expand Up @@ -2557,8 +2559,8 @@ def load_bulk_gap_data(

if use_MP and mpid is None:
try:
with MPRester(api_key=api_key) as mp:
tmp_mplist = mp.get_entries_in_chemsys(list(bulk_sc_structure.symbol_set))
with MPRester(api_key=api_key) as mpr:
tmp_mplist = mpr.get_entries_in_chemsys(list(bulk_sc_structure.symbol_set))
mplist = [
mp_ent.entry_id
for mp_ent in tmp_mplist
Expand All @@ -2573,8 +2575,8 @@ def load_bulk_gap_data(

mpid_fit_list = []
for trial_mpid in mplist:
with MPRester(api_key=api_key) as mp:
mpstruct = mp.get_structure_by_material_id(trial_mpid)
with MPRester(api_key=api_key) as mpr:
mpstruct = mpr.get_structure_by_material_id(trial_mpid)
if StructureMatcher(
primitive_cell=True,
scale=False,
Expand All @@ -2587,7 +2589,7 @@ def load_bulk_gap_data(
mpid = mpid_fit_list[0]
print(f"Single mp-id found for bulk structure:{mpid}.")
elif len(mpid_fit_list) > 1:
num_mpid_list = [int(mp.split("-")[1]) for mp in mpid_fit_list]
num_mpid_list = [int(mpid.split("-")[1]) for mpid in mpid_fit_list]
num_mpid_list.sort()
mpid = f"mp-{num_mpid_list[0]!s}"
print(
Expand All @@ -2603,8 +2605,8 @@ def load_bulk_gap_data(

if mpid is not None:
print(f"Using user-provided mp-id for bulk structure: {mpid}.")
with MPRester(api_key=api_key) as mp:
bs = mp.get_bandstructure_by_material_id(mpid)
with MPRester(api_key=api_key) as mpr:
bs = mpr.get_bandstructure_by_material_id(mpid)
if bs:
cbm = bs.get_cbm()["energy"]
vbm = bs.get_vbm()["energy"]
Expand Down
Loading

0 comments on commit 038e3e3

Please sign in to comment.