Skip to content

Commit a73d390

Browse files
committed
Merge branch 'support-python313' of https://github.com/DanielYang59/pymatgen into support-python313
2 parents 300129b + b04042c commit a73d390

File tree

6 files changed

+213
-159
lines changed

6 files changed

+213
-159
lines changed

.github/workflows/test.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ jobs:
3131
# newest version (currently 3.13) on ubuntu (to get complete coverage on unix).
3232
config:
3333
- os: windows-latest
34-
python: "3.10"
34+
python: "3.13"
3535
resolution: highest
3636
extras: ci,optional
3737
- os: windows-latest
38-
python: "3.12"
38+
python: "3.13"
3939
resolution: highest
4040
extras: ci,optional,numpy-v1 # Test NP1 on Windows (quite buggy ATM)
4141
- os: ubuntu-latest
4242
python: "3.13"
4343
resolution: lowest-direct
4444
extras: ci,optional
4545
- os: macos-latest
46-
python: "3.11"
46+
python: "3.13"
4747
resolution: lowest-direct
4848
extras: ci # test with only required dependencies installed
4949

src/pymatgen/analysis/eos.py

+61-36
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
from pymatgen.util.plotting import add_fig_kwargs, get_ax_fig, pretty_plot
2525

2626
if TYPE_CHECKING:
27-
from typing import ClassVar
27+
from collections.abc import Sequence
28+
from typing import Any, ClassVar
2829

2930
import matplotlib.pyplot as plt
3031

@@ -40,7 +41,11 @@ class EOSBase(ABC):
4041
implementations.
4142
"""
4243

43-
def __init__(self, volumes, energies):
44+
def __init__(
45+
self,
46+
volumes: Sequence[float],
47+
energies: Sequence[float],
48+
) -> None:
4449
"""
4550
Args:
4651
volumes (Sequence[float]): in Ang^3.
@@ -50,18 +55,28 @@ def __init__(self, volumes, energies):
5055
self.energies = np.array(energies)
5156
# minimum energy(e0), buk modulus(b0),
5257
# derivative of bulk modulus w.r.t. pressure(b1), minimum volume(v0)
53-
self._params = None
58+
self._params: Sequence | None = None
5459
# the eos function parameters. It is the same as _params except for
5560
# equation of states that uses polynomial fits(delta_factor and
5661
# numerical_eos)
57-
self.eos_params = None
62+
self.eos_params: Sequence | None = None
5863

59-
def _initial_guess(self):
64+
def __call__(self, volume: float) -> float:
65+
"""
66+
Args:
67+
volume (float | list[float]): volume(s) in Ang^3.
68+
69+
Returns:
70+
Compute EOS with this volume.
71+
"""
72+
return self.func(volume)
73+
74+
def _initial_guess(self) -> tuple[float, float, float, float]:
6075
"""
6176
Quadratic fit to get an initial guess for the parameters.
6277
6378
Returns:
64-
tuple: 4 floats for (e0, b0, b1, v0)
79+
tuple[float, float, float, float]: e0, b0, b1, v0
6580
"""
6681
a, b, c = np.polyfit(self.volumes, self.energies, 2)
6782
self.eos_params = [a, b, c]
@@ -78,7 +93,7 @@ def _initial_guess(self):
7893

7994
return e0, b0, b1, v0
8095

81-
def fit(self):
96+
def fit(self) -> None:
8297
"""
8398
Do the fitting. Does least square fitting. If you want to use custom
8499
fitting, must override this.
@@ -120,24 +135,20 @@ def func(self, volume):
120135
"""
121136
return self._func(np.array(volume), self.eos_params)
122137

123-
def __call__(self, volume: float) -> float:
124-
"""
125-
Args:
126-
volume (float | list[float]): volume(s) in Ang^3.
127-
128-
Returns:
129-
Compute EOS with this volume.
130-
"""
131-
return self.func(volume)
132-
133138
@property
134139
def e0(self) -> float:
135140
"""The min energy."""
141+
if self._params is None:
142+
raise RuntimeError("params have not be initialized.")
143+
136144
return self._params[0]
137145

138146
@property
139147
def b0(self) -> float:
140148
"""The bulk modulus in units of energy/unit of volume^3."""
149+
if self._params is None:
150+
raise RuntimeError("params have not be initialized.")
151+
141152
return self._params[1]
142153

143154
@property
@@ -156,11 +167,18 @@ def v0(self):
156167
return self._params[3]
157168

158169
@property
159-
def results(self):
170+
def results(self) -> dict[str, Any]:
160171
"""A summary dict."""
161172
return {"e0": self.e0, "b0": self.b0, "b1": self.b1, "v0": self.v0}
162173

163-
def plot(self, width=8, height=None, ax: plt.Axes = None, dpi=None, **kwargs):
174+
def plot(
175+
self,
176+
width: float = 8,
177+
height: float | None = None,
178+
ax: plt.Axes = None,
179+
dpi: float | None = None,
180+
**kwargs,
181+
) -> plt.Axes:
164182
"""
165183
Plot the equation of state.
166184
@@ -170,7 +188,7 @@ def plot(self, width=8, height=None, ax: plt.Axes = None, dpi=None, **kwargs):
170188
golden ratio.
171189
ax (plt.Axes): If supplied, changes will be made to the existing Axes.
172190
Otherwise, new Axes will be created.
173-
dpi:
191+
dpi (float): DPI.
174192
kwargs (dict): additional args fed to pyplot.plot.
175193
supported keys: style, color, text, label
176194
@@ -211,16 +229,18 @@ def plot(self, width=8, height=None, ax: plt.Axes = None, dpi=None, **kwargs):
211229
return ax
212230

213231
@add_fig_kwargs
214-
def plot_ax(self, ax: plt.Axes = None, fontsize=12, **kwargs):
232+
def plot_ax(
233+
self,
234+
ax: plt.Axes | None = None,
235+
fontsize: float = 12,
236+
**kwargs,
237+
) -> plt.Figure:
215238
"""
216239
Plot the equation of state on axis `ax`.
217240
218241
Args:
219242
ax: matplotlib Axes or None if a new figure should be created.
220243
fontsize: Legend fontsize.
221-
color (str): plot color.
222-
label (str): Plot label
223-
text (str): Legend text (options)
224244
225245
Returns:
226246
plt.Figure: matplotlib figure.
@@ -270,7 +290,7 @@ def plot_ax(self, ax: plt.Axes = None, fontsize=12, **kwargs):
270290
class Murnaghan(EOSBase):
271291
"""Murnaghan EOS."""
272292

273-
def _func(self, volume, params):
293+
def _func(self, volume, params: tuple[float, float, float, float]):
274294
"""From PRB 28,5480 (1983)."""
275295
e0, b0, b1, v0 = tuple(params)
276296
return e0 + b0 * volume / b1 * (((v0 / volume) ** b1) / (b1 - 1.0) + 1.0) - v0 * b0 / (b1 - 1.0)
@@ -279,7 +299,7 @@ def _func(self, volume, params):
279299
class Birch(EOSBase):
280300
"""Birch EOS."""
281301

282-
def _func(self, volume, params):
302+
def _func(self, volume, params: tuple[float, float, float, float]):
283303
"""From Intermetallic compounds: Principles and Practice, Vol. I:
284304
Principles Chapter 9 pages 195-210 by M. Mehl. B. Klein,
285305
D. Papaconstantopoulos.
@@ -296,7 +316,7 @@ def _func(self, volume, params):
296316
class BirchMurnaghan(EOSBase):
297317
"""BirchMurnaghan EOS."""
298318

299-
def _func(self, volume, params):
319+
def _func(self, volume, params: tuple[float, float, float, float]):
300320
"""BirchMurnaghan equation from PRB 70, 224107."""
301321
e0, b0, b1, v0 = tuple(params)
302322
eta = (v0 / volume) ** (1 / 3)
@@ -306,7 +326,7 @@ def _func(self, volume, params):
306326
class PourierTarantola(EOSBase):
307327
"""Pourier-Tarantola EOS."""
308328

309-
def _func(self, volume, params):
329+
def _func(self, volume, params: tuple[float, float, float, float]):
310330
"""Pourier-Tarantola equation from PRB 70, 224107."""
311331
e0, b0, b1, v0 = tuple(params)
312332
eta = (volume / v0) ** (1 / 3)
@@ -317,7 +337,7 @@ def _func(self, volume, params):
317337
class Vinet(EOSBase):
318338
"""Vinet EOS."""
319339

320-
def _func(self, volume, params):
340+
def _func(self, volume, params: tuple[float, float, float, float]):
321341
"""Vinet equation from PRB 70, 224107."""
322342
e0, b0, b1, v0 = tuple(params)
323343
eta = (volume / v0) ** (1 / 3)
@@ -335,7 +355,7 @@ class PolynomialEOS(EOSBase):
335355
def _func(self, volume, params):
336356
return np.poly1d(list(params))(volume)
337357

338-
def fit(self, order):
358+
def fit(self, order: int) -> None:
339359
"""
340360
Do polynomial fitting and set the parameters. Uses numpy polyfit.
341361
@@ -345,7 +365,7 @@ def fit(self, order):
345365
self.eos_params = np.polyfit(self.volumes, self.energies, order)
346366
self._set_params()
347367

348-
def _set_params(self):
368+
def _set_params(self) -> None:
349369
"""
350370
Use the fit polynomial to compute the parameter e0, b0, b1 and v0
351371
and set to the _params attribute.
@@ -372,7 +392,7 @@ def _func(self, volume, params):
372392
x = volume ** (-2 / 3.0)
373393
return np.poly1d(list(params))(x)
374394

375-
def fit(self, order=3):
395+
def fit(self, order: int = 3) -> None:
376396
"""Overridden since this eos works with volume**(2/3) instead of volume."""
377397
x = self.volumes ** (-2 / 3.0)
378398
self.eos_params = np.polyfit(x, self.energies, order)
@@ -407,7 +427,12 @@ def _set_params(self):
407427
class NumericalEOS(PolynomialEOS):
408428
"""A numerical EOS."""
409429

410-
def fit(self, min_ndata_factor=3, max_poly_order_factor=5, min_poly_order=2):
430+
def fit(
431+
self,
432+
min_ndata_factor: int = 3,
433+
max_poly_order_factor: int = 5,
434+
min_poly_order: int = 2,
435+
) -> None:
411436
"""Fit the input data to the 'numerical eos', the equation of state employed
412437
in the quasiharmonic Debye model described in the paper:
413438
10.1103/PhysRevB.90.174107.
@@ -539,7 +564,7 @@ class EOS:
539564
eos_fit.plot()
540565
"""
541566

542-
MODELS: ClassVar = {
567+
MODELS: ClassVar[dict[str, Any]] = {
543568
"murnaghan": Murnaghan,
544569
"birch": Birch,
545570
"birch_murnaghan": BirchMurnaghan,
@@ -549,7 +574,7 @@ class EOS:
549574
"numerical_eos": NumericalEOS,
550575
}
551576

552-
def __init__(self, eos_name="murnaghan"):
577+
def __init__(self, eos_name: str = "murnaghan") -> None:
553578
"""
554579
Args:
555580
eos_name (str): Type of EOS to fit.
@@ -562,7 +587,7 @@ def __init__(self, eos_name="murnaghan"):
562587
self._eos_name = eos_name
563588
self.model = self.MODELS[eos_name]
564589

565-
def fit(self, volumes, energies):
590+
def fit(self, volumes: Sequence[float], energies: Sequence[float]) -> EOSBase:
566591
"""Fit energies as function of volumes.
567592
568593
Args:

src/pymatgen/analysis/prototypes.py

+28-16
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
from pymatgen.core.structure import Structure
2626

2727
MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
28-
AFLOW_PROTOTYPE_LIBRARY = loadfn(f"{MODULE_DIR}/aflow_prototypes.json")
28+
try:
29+
AFLOW_PROTOTYPE_LIBRARY = loadfn(f"{MODULE_DIR}/aflow_prototypes.json")
30+
except RuntimeError as exc:
31+
raise ImportError("pybtex is needed to load AFLOW library") from exc
2932

3033

3134
@due.dcite(
@@ -48,22 +51,27 @@ class AflowPrototypeMatcher:
4851
https://doi.org/10.1016/j.commatsci.2017.01.017
4952
"""
5053

51-
def __init__(self, initial_ltol=0.2, initial_stol=0.3, initial_angle_tol=5):
54+
def __init__(
55+
self,
56+
initial_ltol: float = 0.2,
57+
initial_stol: float = 0.3,
58+
initial_angle_tol: float = 5,
59+
) -> None:
5260
"""
5361
Tolerances as defined in StructureMatcher. Tolerances will be
5462
gradually decreased until only a single match is found (if possible).
5563
5664
Args:
57-
initial_ltol: fractional length tolerance
58-
initial_stol: site tolerance
59-
initial_angle_tol: angle tolerance
65+
initial_ltol (float): fractional length tolerance.
66+
initial_stol (float): site tolerance.
67+
initial_angle_tol (float): angle tolerance.
6068
"""
6169
self.initial_ltol = initial_ltol
6270
self.initial_stol = initial_stol
6371
self.initial_angle_tol = initial_angle_tol
6472

6573
# Preprocess AFLOW prototypes
66-
self._aflow_prototype_library = []
74+
self._aflow_prototype_library: list[tuple[Structure, dict]] = []
6775
for dct in AFLOW_PROTOTYPE_LIBRARY:
6876
structure: Structure = dct["snl"].structure
6977
reduced_structure = self._preprocess_structure(structure)
@@ -73,7 +81,11 @@ def __init__(self, initial_ltol=0.2, initial_stol=0.3, initial_angle_tol=5):
7381
def _preprocess_structure(structure: Structure) -> Structure:
7482
return structure.get_reduced_structure(reduction_algo="niggli").get_primitive_structure()
7583

76-
def _match_prototype(self, structure_matcher: StructureMatcher, reduced_structure: Structure):
84+
def _match_prototype(
85+
self,
86+
structure_matcher: StructureMatcher,
87+
reduced_structure: Structure,
88+
) -> list[dict]:
7789
tags = []
7890
for aflow_reduced_structure, dct in self._aflow_prototype_library:
7991
# Since both structures are already reduced, we can skip the structure reduction step
@@ -84,7 +96,7 @@ def _match_prototype(self, structure_matcher: StructureMatcher, reduced_structur
8496
tags.append(dct)
8597
return tags
8698

87-
def _match_single_prototype(self, structure: Structure):
99+
def _match_single_prototype(self, structure: Structure) -> list[dict]:
88100
sm = StructureMatcher(
89101
ltol=self.initial_ltol,
90102
stol=self.initial_stol,
@@ -102,23 +114,23 @@ def _match_single_prototype(self, structure: Structure):
102114
break
103115
return tags
104116

105-
def get_prototypes(self, structure: Structure) -> list | None:
117+
def get_prototypes(self, structure: Structure) -> list[dict] | None:
106118
"""Get prototype(s) structures for a given input structure. If you use this method in
107119
your work, please cite the appropriate AFLOW publication:
108120
109-
Mehl, M. J., Hicks, D., Toher, C., Levy, O., Hanson, R. M., Hart, G., & Curtarolo,
110-
S. (2017). The AFLOW library of crystallographic prototypes: part 1. Computational
111-
Materials Science, 136, S1-S828. https://doi.org/10.1016/j.commatsci.2017.01.017
121+
Mehl, M. J., Hicks, D., Toher, C., Levy, O., Hanson, R. M., Hart, G., & Curtarolo,
122+
S. (2017). The AFLOW library of crystallographic prototypes: part 1. Computational
123+
Materials Science, 136, S1-S828. https://doi.org/10.1016/j.commatsci.2017.01.017
112124
113125
Args:
114-
structure: structure to match
126+
structure (Structure): structure to match
115127
116128
Returns:
117-
list | None: A list of dicts with keys 'snl' for the matched prototype and
118-
'tags', a dict of tags ('mineral', 'strukturbericht' and 'aflow') of that
129+
list[dict] | None: A list of dicts with keys "snl" for the matched prototype and
130+
"tags", a dict of tags ("mineral", "strukturbericht" and "aflow") of that
119131
prototype. This should be a list containing just a single entry, but it is
120132
possible a material can match multiple prototypes.
121133
"""
122-
tags = self._match_single_prototype(structure)
134+
tags: list[dict] = self._match_single_prototype(structure)
123135

124136
return tags or None

0 commit comments

Comments
 (0)