Skip to content

Commit

Permalink
Add missing parenthesis to `electronic_structure.boltztrap.BoltztrapA…
Browse files Browse the repository at this point in the history
…nalyzer.get_extreme.is_isotropic` (#4271)

* add missing parenthesis

* minor cleanup of type, comment and var name

---------

Co-authored-by: Shyue Ping Ong <[email protected]>
  • Loading branch information
DanielYang59 and shyuep authored Jan 28, 2025
1 parent 90a1ef7 commit ed80c87
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 48 deletions.
96 changes: 49 additions & 47 deletions src/pymatgen/electronic_structure/boltztrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from pymatgen.symmetry.bandstructure import HighSymmKpath

if TYPE_CHECKING:
from typing import Literal
from typing import Any, Literal

from numpy.typing import ArrayLike
from typing_extensions import Self
Expand Down Expand Up @@ -1426,96 +1426,98 @@ def get_complexity_factor(

def get_extreme(
self,
target_prop,
maximize=True,
min_temp=None,
max_temp=None,
min_doping=None,
max_doping=None,
isotropy_tolerance=0.05,
use_average=True,
):
target_prop: Literal["seebeck", "power factor", "conductivity", "kappa", "zt"],
maximize: bool = True,
min_temp: float | None = None,
max_temp: float | None = None,
min_doping: float | None = None,
max_doping: float | None = None,
isotropy_tolerance: float = 0.05,
use_average: bool = True,
) -> dict[Literal["p", "n", "best"], dict[str, Any]]:
"""Use eigenvalues over a range of carriers, temperatures, and doping levels, to estimate the "best"
value that can be achieved for the given target_property. Note that
this method searches the doping dict only, not the full mu dict.
Args:
target_prop: target property, i.e. "seebeck", "power factor", "conductivity", "kappa", or "zt"
maximize: True to maximize, False to minimize (e.g. kappa)
min_temp: minimum temperature allowed
max_temp: maximum temperature allowed
min_doping: minimum doping allowed (e.g., 1E18)
max_doping: maximum doping allowed (e.g., 1E20)
isotropy_tolerance: tolerance for isotropic (0.05 = 5%)
use_average: True for avg of eigenval, False for max eigenval
target_prop ("seebeck", "power factor", "conductivity", "kappa", "zt"): target property.
maximize (bool): True to maximize, False to minimize (e.g. kappa)
min_temp (float): minimum temperature allowed
max_temp (float): maximum temperature allowed
min_doping (float): minimum doping allowed (e.g., 1E18)
max_doping (float): maximum doping allowed (e.g., 1E20)
isotropy_tolerance (float): tolerance for isotropic (0.05 = 5%)
use_average (bool): True for average of eigenval, False for max eigenval.
Returns:
A dictionary with keys {"p", "n", "best"} with sub-keys:
{"value", "temperature", "doping", "isotropic"}
A dictionary with the following keys: {"p", "n", "best"}.
Each key maps to a sub-dictionary with the following keys:
{"value", "temperature", "doping", "isotropic", "carrier_type"}.
"""

def is_isotropic(x, isotropy_tolerance) -> bool:
"""Internal method to tell you if 3-vector "x" is isotropic.
def is_isotropic(x, isotropy_tolerance: float) -> bool:
"""Helper function to determine if 3D vector is isotropic.
Args:
x: the vector to determine isotropy for
isotropy_tolerance: tolerance, e.g. 0.05 is 5%
isotropy_tolerance (float): tolerance, e.g. 0.05 is 5%
"""
if len(x) != 3:
raise ValueError("Invalid input to is_isotropic!")
raise ValueError("Invalid vector length to is_isotropic!")

st = sorted(x)

return bool(
all([st[0], st[1], st[2]])
and (abs((st[1] - st[0]) / st[1]) <= isotropy_tolerance)
and (abs(st[2] - st[0]) / st[2] <= isotropy_tolerance)
and (abs((st[2] - st[0]) / st[2]) <= isotropy_tolerance)
and (abs((st[2] - st[1]) / st[2]) <= isotropy_tolerance)
)

if target_prop.lower() == "seebeck":
d = self.get_seebeck(output="eigs", doping_levels=True)
dct = self.get_seebeck(output="eigs", doping_levels=True)

elif target_prop.lower() == "power factor":
d = self.get_power_factor(output="eigs", doping_levels=True)
dct = self.get_power_factor(output="eigs", doping_levels=True)

elif target_prop.lower() == "conductivity":
d = self.get_conductivity(output="eigs", doping_levels=True)
dct = self.get_conductivity(output="eigs", doping_levels=True)

elif target_prop.lower() == "kappa":
d = self.get_thermal_conductivity(output="eigs", doping_levels=True)
dct = self.get_thermal_conductivity(output="eigs", doping_levels=True)
elif target_prop.lower() == "zt":
d = self.get_zt(output="eigs", doping_levels=True)
dct = self.get_zt(output="eigs", doping_levels=True)

else:
raise ValueError(f"Unrecognized {target_prop=}")

abs_val = True # take the absolute value of properties
abs_val: bool = True # take the absolute value of properties

x_val = x_temp = x_doping = x_isotropic = None
output = {}
output: dict[Literal["p", "n", "best"], dict[str, Any]] = {}

min_temp = min_temp or 0
max_temp = max_temp or float("inf")
min_doping = min_doping or 0
max_doping = max_doping or float("inf")

for pn in ("p", "n"):
for t in d[pn]: # temperatures
if min_temp <= float(t) <= max_temp:
for didx, evs in enumerate(d[pn][t]):
doping_lvl = self.doping[pn][didx]
for pn_type in ("p", "n"):
for temperature in dct[pn_type]:
if min_temp <= float(temperature) <= max_temp:
for idx, eig_vals in enumerate(dct[pn_type][temperature]):
doping_lvl = self.doping[pn_type][idx]
if min_doping <= doping_lvl <= max_doping:
isotropic = is_isotropic(evs, isotropy_tolerance)
isotropic: bool = is_isotropic(eig_vals, isotropy_tolerance)
if abs_val:
evs = [abs(x) for x in evs]
val = float(sum(evs)) / len(evs) if use_average else max(evs)
eig_vals = [abs(x) for x in eig_vals]
val = float(sum(eig_vals)) / len(eig_vals) if use_average else max(eig_vals)
if x_val is None or (val > x_val and maximize) or (val < x_val and not maximize):
x_val = val
x_temp = t
x_temp = temperature
x_doping = doping_lvl
x_isotropic = isotropic

output[pn] = {
output[pn_type] = {
"value": x_val,
"temperature": x_temp,
"doping": x_doping,
Expand All @@ -1524,7 +1526,7 @@ def is_isotropic(x, isotropy_tolerance) -> bool:
x_val = None

if maximize:
max_type = "p" if output["p"]["value"] >= output["n"]["value"] else "n"
max_type: Literal["p", "n"] = "p" if output["p"]["value"] >= output["n"]["value"] else "n"
else:
max_type = "p" if output["p"]["value"] <= output["n"]["value"] else "n"

Expand Down Expand Up @@ -1589,10 +1591,10 @@ def get_complete_dos(self, structure: Structure, analyzer_for_second_spin=None):
Example of use in case of spin polarized case:
BoltztrapRunner(bs=bs,nelec=10,run_type="DOS",spin=1).run(path_dir='dos_up/')
an_up=BoltztrapAnalyzer.from_files("dos_up/boltztrap/",dos_spin=1)
an_up=BoltztrapAnalyzer.from_files("dos_up/boltztrap/", dos_spin=1)
BoltztrapRunner(bs=bs,nelec=10,run_type="DOS",spin=-1).run(path_dir='dos_dw/')
an_dw=BoltztrapAnalyzer.from_files("dos_dw/boltztrap/",dos_spin=-1)
an_dw=BoltztrapAnalyzer.from_files("dos_dw/boltztrap/", dos_spin=-1)
cdos=an_up.get_complete_dos(bs.structure,an_dw)
"""
Expand All @@ -1602,10 +1604,10 @@ def get_complete_dos(self, structure: Structure, analyzer_for_second_spin=None):

if analyzer_for_second_spin:
if not np.all(self.dos.energies == analyzer_for_second_spin.dos.energies):
raise BoltztrapError("Dos merging error: energies of the two dos are different")
raise BoltztrapError("DOS merging error: energies of the two DOS are different")

if spin_1 == spin_2:
raise BoltztrapError("Dos merging error: spin component are the same")
raise BoltztrapError("DOS merging error: spin component are the same")

for s in self._dos_partial:
idx = int(s)
Expand Down
2 changes: 1 addition & 1 deletion tests/electronic_structure/test_boltztrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def test_extreme(self):
extreme = self.bz.get_extreme("seebeck")
assert extreme["best"]["carrier_type"] == "n"
assert extreme["p"]["value"] == approx(1255.365, abs=1e-2)
assert extreme["n"]["isotropic"]
assert extreme["n"]["isotropic"] is False
assert extreme["n"]["temperature"] == 600

extreme = self.bz.get_extreme("kappa", maximize=False, min_temp=400, min_doping=1e20)
Expand Down

0 comments on commit ed80c87

Please sign in to comment.