Skip to content

Commit 0d47074

Browse files
authored
Merge pull request #91 from materialsproject/master
sync
2 parents 176a400 + af567bf commit 0d47074

File tree

5 files changed

+65
-45
lines changed

5 files changed

+65
-45
lines changed

src/pymatgen/core/lattice.py

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import warnings
1111
from collections import defaultdict
1212
from fractions import Fraction
13-
from functools import reduce
13+
from functools import cached_property, reduce
1414
from typing import TYPE_CHECKING
1515

1616
import numpy as np
@@ -22,6 +22,7 @@
2222

2323
if TYPE_CHECKING:
2424
from collections.abc import Iterator
25+
from typing import Literal
2526

2627
from numpy.typing import ArrayLike, NDArray
2728
from typing_extensions import Self
@@ -74,17 +75,6 @@ def __init__(
7475

7576
self.pbc = pbc
7677

77-
@property
78-
def pbc(self) -> tuple[bool, bool, bool]:
79-
"""Tuple defining the periodicity of the Lattice."""
80-
return self._pbc # type:ignore[return-value]
81-
82-
@pbc.setter
83-
def pbc(self, pbc: tuple[bool, bool, bool]) -> None:
84-
if len(pbc) != 3 or any(item not in {True, False} for item in pbc):
85-
raise ValueError(f"pbc must be a tuple of three True/False values, got {pbc}")
86-
self._pbc = tuple(bool(item) for item in pbc)
87-
8878
def __repr__(self) -> str:
8979
return "\n".join(
9080
[
@@ -148,6 +138,22 @@ def __format__(self, fmt_spec: str = "") -> str:
148138
return fmt.format(*(format(c, fmt_spec) for row in matrix for c in row))
149139

150140
@property
141+
def pbc(self) -> tuple[bool, bool, bool]:
142+
"""Tuple defining the periodicity of the Lattice."""
143+
return self._pbc # type:ignore[return-value]
144+
145+
@pbc.setter
146+
def pbc(self, pbc: tuple[bool, bool, bool]) -> None:
147+
if len(pbc) != 3 or any(item not in {True, False} for item in pbc):
148+
raise ValueError(f"pbc must be a tuple of three True/False values, got {pbc}")
149+
self._pbc = tuple(bool(item) for item in pbc)
150+
151+
@property
152+
def matrix(self) -> NDArray[np.float64]:
153+
"""Copy of matrix representing the Lattice."""
154+
return self._matrix
155+
156+
@cached_property
151157
def lengths(self) -> tuple[float, float, float]:
152158
"""Lattice lengths.
153159
@@ -156,7 +162,7 @@ def lengths(self) -> tuple[float, float, float]:
156162
"""
157163
return tuple(np.sqrt(np.sum(self._matrix**2, axis=1)).tolist()) # type: ignore[return-value]
158164

159-
@property
165+
@cached_property
160166
def angles(self) -> tuple[float, float, float]:
161167
"""Lattice angles.
162168
@@ -172,16 +178,17 @@ def angles(self) -> tuple[float, float, float]:
172178
angles = np.arccos(angles) * 180.0 / np.pi # type: ignore[assignment]
173179
return tuple(angles.tolist()) # type: ignore[return-value]
174180

181+
@cached_property
182+
def volume(self) -> float:
183+
"""Volume of the unit cell in Angstrom^3."""
184+
matrix = self._matrix
185+
return float(abs(np.dot(np.cross(matrix[0], matrix[1]), matrix[2])))
186+
175187
@property
176188
def is_orthogonal(self) -> bool:
177189
"""Whether all angles are 90 degrees."""
178190
return all(abs(a - 90) < 1e-5 for a in self.angles)
179191

180-
@property
181-
def matrix(self) -> NDArray[np.float64]:
182-
"""Copy of matrix representing the Lattice."""
183-
return self._matrix
184-
185192
@property
186193
def is_3d_periodic(self) -> bool:
187194
"""True if the Lattice is periodic in all directions."""
@@ -507,12 +514,6 @@ def gamma(self) -> float:
507514
"""Angle gamma of lattice in degrees."""
508515
return self.angles[2]
509516

510-
@property
511-
def volume(self) -> float:
512-
"""Volume of the unit cell in Angstrom^3."""
513-
matrix = self._matrix
514-
return float(abs(np.dot(np.cross(matrix[0], matrix[1]), matrix[2])))
515-
516517
@property
517518
def parameters(self) -> tuple[float, float, float, float, float, float]:
518519
"""6-tuple of floats (a, b, c, alpha, beta, gamma)."""
@@ -521,7 +522,7 @@ def parameters(self) -> tuple[float, float, float, float, float, float]:
521522
@property
522523
def params_dict(self) -> dict[str, float]:
523524
"""Dictionary of lattice parameters."""
524-
return dict(zip("a b c alpha beta gamma".split(), self.parameters, strict=True))
525+
return dict(zip(("a", "b", "c", "alpha", "beta", "gamma"), self.parameters, strict=True))
525526

526527
@property
527528
def reciprocal_lattice(self) -> Self:
@@ -897,11 +898,11 @@ def selling_dist(self, other: Self) -> float:
897898

898899
return min(np.linalg.norm(reflection - selling2) for reflection in all_reflections) # type: ignore[return-value, type-var]
899900

900-
def as_dict(self, verbosity: int = 0) -> dict:
901+
def as_dict(self, verbosity: Literal[0, 1] = 0) -> dict:
901902
"""MSONable dict representation of the Lattice.
902903
903904
Args:
904-
verbosity (int): Default of 0 only includes the matrix representation.
905+
verbosity (0 | 1): Default of 0 only includes the matrix representation.
905906
Set to 1 to include the lattice parameters.
906907
"""
907908
dct = {
@@ -910,7 +911,15 @@ def as_dict(self, verbosity: int = 0) -> dict:
910911
"matrix": self._matrix.tolist(),
911912
"pbc": self.pbc,
912913
}
913-
if verbosity > 0:
914+
915+
if verbosity not in {0, 1}:
916+
warnings.warn(
917+
f"`verbosity={verbosity}` is deprecated and will be disallowed in a future version. "
918+
"Please use 0 (silent) or 1 (verbose) explicitly.",
919+
DeprecationWarning,
920+
stacklevel=2,
921+
)
922+
if verbosity > 0: # TODO: explicit check `verbosity == 1`
914923
dct |= self.params_dict
915924
dct["volume"] = self.volume
916925

@@ -1282,11 +1291,11 @@ def scale(self, new_volume: float) -> Self:
12821291
Returns:
12831292
New lattice with desired volume.
12841293
"""
1285-
versors = self.matrix / self.abc
1294+
versors = self.matrix / self.lengths
12861295

12871296
geo_factor = abs(np.dot(np.cross(versors[0], versors[1]), versors[2]))
12881297

1289-
ratios = np.array(self.abc) / self.c
1298+
ratios = np.array(self.lengths) / self.c
12901299

12911300
new_c = (new_volume / (geo_factor * np.prod(ratios))) ** (1 / 3.0)
12921301

@@ -1541,7 +1550,7 @@ def get_points_in_sphere_old(
15411550
# TODO: refactor to use lll matrix (nmax will be smaller)
15421551
# Determine the maximum number of supercells in each direction
15431552
# required to contain a sphere of radius n
1544-
recp_len = np.array(self.reciprocal_lattice.abc) / (2 * np.pi)
1553+
recp_len = np.array(self.reciprocal_lattice.lengths) / (2 * np.pi)
15451554
nmax = float(r) * recp_len + 0.01
15461555

15471556
# Get the fractional coordinates of the center of the sphere
@@ -1849,7 +1858,7 @@ def get_points_in_spheres(
18491858
if np.any(_pbc):
18501859
if lattice is None:
18511860
raise ValueError("Lattice needs to be supplied when considering periodic boundary")
1852-
recp_len = np.array(lattice.reciprocal_lattice.abc)
1861+
recp_len = np.array(lattice.reciprocal_lattice.lengths)
18531862
maxr = np.ceil((r + 0.15) * recp_len / (2 * math.pi))
18541863
frac_coords = lattice.get_fractional_coords(center_coords)
18551864
nmin_temp = np.floor(np.min(frac_coords, axis=0)) - maxr

src/pymatgen/core/sites.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import collections
66
import json
7+
import warnings
78
from typing import TYPE_CHECKING, cast
89

910
import numpy as np
@@ -16,7 +17,7 @@
1617
from pymatgen.util.misc import is_np_dict_equal
1718

1819
if TYPE_CHECKING:
19-
from typing import Any
20+
from typing import Any, Literal
2021

2122
from numpy.typing import ArrayLike, NDArray
2223
from typing_extensions import Self
@@ -254,7 +255,7 @@ def as_dict(self) -> dict:
254255
dct = {
255256
"name": self.species_string,
256257
"species": species,
257-
"xyz": [float(c) for c in self.coords],
258+
"xyz": self.coords.astype(float).tolist(),
258259
"properties": self.properties,
259260
"@module": type(self).__module__,
260261
"@class": type(self).__name__,
@@ -531,7 +532,7 @@ def distance_and_image_from_frac_coords(
531532
Args:
532533
fcoords (3x1 array): fractional coordinates to get distance from.
533534
jimage (3x1 array): Specific periodic image in terms of
534-
lattice translations, e.g. [1,0,0] implies to take periodic
535+
lattice translations, e.g. [1, 0, 0] implies to take periodic
535536
image that is one a-lattice vector away. If jimage is None,
536537
the image that is nearest to the site is found.
537538
@@ -556,7 +557,7 @@ def distance_and_image(
556557
Args:
557558
other (PeriodicSite): Other site to get distance from.
558559
jimage (3x1 array): Specific periodic image in terms of lattice
559-
translations, e.g. [1,0,0] implies to take periodic image
560+
translations, e.g. [1, 0, 0] implies to take periodic image
560561
that is one a-lattice vector away. If jimage is None,
561562
the image that is nearest to the site is found.
562563
@@ -576,7 +577,7 @@ def distance(
576577
Args:
577578
other (PeriodicSite): Other site to get distance from.
578579
jimage (3x1 array): Specific periodic image in terms of lattice
579-
translations, e.g. [1,0,0] implies to take periodic image
580+
translations, e.g. [1, 0, 0] implies to take periodic image
580581
that is one a-lattice vector away. If jimage is None,
581582
the image that is nearest to the site is found.
582583
@@ -585,11 +586,11 @@ def distance(
585586
"""
586587
return self.distance_and_image(other, jimage)[0]
587588

588-
def as_dict(self, verbosity: int = 0) -> dict:
589+
def as_dict(self, verbosity: Literal[0, 1] = 0) -> dict:
589590
"""JSON-serializable dict representation of PeriodicSite.
590591
591592
Args:
592-
verbosity (int): Verbosity level. Default of 0 only includes the matrix
593+
verbosity (0 | 1): Verbosity level. Default of 0 only includes the matrix
593594
representation. Set to 1 for more details such as Cartesian coordinates, etc.
594595
"""
595596
species = []
@@ -602,16 +603,23 @@ def as_dict(self, verbosity: int = 0) -> dict:
602603

603604
dct = {
604605
"species": species,
605-
"abc": [float(c) for c in self._frac_coords],
606+
"abc": self._frac_coords.astype(float).tolist(),
606607
"lattice": self._lattice.as_dict(verbosity=verbosity),
607608
"@module": type(self).__module__,
608609
"@class": type(self).__name__,
609610
"properties": self.properties,
610611
"label": self.label,
611612
}
612613

613-
if verbosity > 0:
614-
dct["xyz"] = [float(c) for c in self.coords]
614+
if verbosity not in {0, 1}:
615+
warnings.warn(
616+
f"`verbosity={verbosity}` is deprecated and will be disallowed in a future version. "
617+
"Please use 0 (silent) or 1 (verbose) explicitly.",
618+
DeprecationWarning,
619+
stacklevel=2,
620+
)
621+
if verbosity > 0: # TODO: explicitly check `verbosity == 1`
622+
dct["xyz"] = self.coords.astype(float).tolist()
615623

616624
return dct
617625

src/pymatgen/core/structure.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,14 +2813,14 @@ def get_orderings(
28132813

28142814
def as_dict(
28152815
self,
2816-
verbosity: int = 1,
2816+
verbosity: Literal[0, 1] = 1,
28172817
fmt: Literal["abivars"] | None = None,
28182818
**kwargs,
28192819
) -> dict[str, Any]:
28202820
"""Dict representation of Structure.
28212821
28222822
Args:
2823-
verbosity (int): Verbosity level. Default of 1 includes both
2823+
verbosity (0 | 1): Verbosity level. Default of 1 includes both
28242824
direct and Cartesian coordinates for all sites, lattice
28252825
parameters, etc. Useful for reading and for insertion into a
28262826
database. Set to 0 for an extremely lightweight version
@@ -2857,7 +2857,10 @@ def as_dict(
28572857
del site_dict["lattice"]
28582858
del site_dict["@module"]
28592859
del site_dict["@class"]
2860+
if verbosity == 0:
2861+
del site_dict["label"]
28602862
sites.append(site_dict)
2863+
28612864
dct["sites"] = sites
28622865
return dct
28632866

tests/core/test_lattice.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ def test_static_methods(self):
148148
assert lattice.angles == approx(expected_angles)
149149

150150
def test_attributes(self):
151-
"""Docstring for test_attributes."""
152151
lattice = Lattice.cubic(10.0)
153152
assert lattice.a == approx(10)
154153
assert lattice.b == approx(10)

tests/core/test_structure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ def test_as_dict(self):
268268
dct = struct.as_dict(0)
269269
assert "volume" not in dct["lattice"]
270270
assert "xyz" not in dct["sites"][0]
271+
assert "label" not in dct["sites"][0]
271272

272273
def test_from_dict(self):
273274
dct = self.propertied_structure.as_dict()

0 commit comments

Comments
 (0)