10
10
import warnings
11
11
from collections import defaultdict
12
12
from fractions import Fraction
13
- from functools import reduce
13
+ from functools import cached_property , reduce
14
14
from typing import TYPE_CHECKING
15
15
16
16
import numpy as np
22
22
23
23
if TYPE_CHECKING :
24
24
from collections .abc import Iterator
25
+ from typing import Literal
25
26
26
27
from numpy .typing import ArrayLike , NDArray
27
28
from typing_extensions import Self
@@ -74,17 +75,6 @@ def __init__(
74
75
75
76
self .pbc = pbc
76
77
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
-
88
78
def __repr__ (self ) -> str :
89
79
return "\n " .join (
90
80
[
@@ -148,6 +138,22 @@ def __format__(self, fmt_spec: str = "") -> str:
148
138
return fmt .format (* (format (c , fmt_spec ) for row in matrix for c in row ))
149
139
150
140
@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
151
157
def lengths (self ) -> tuple [float , float , float ]:
152
158
"""Lattice lengths.
153
159
@@ -156,7 +162,7 @@ def lengths(self) -> tuple[float, float, float]:
156
162
"""
157
163
return tuple (np .sqrt (np .sum (self ._matrix ** 2 , axis = 1 )).tolist ()) # type: ignore[return-value]
158
164
159
- @property
165
+ @cached_property
160
166
def angles (self ) -> tuple [float , float , float ]:
161
167
"""Lattice angles.
162
168
@@ -172,16 +178,17 @@ def angles(self) -> tuple[float, float, float]:
172
178
angles = np .arccos (angles ) * 180.0 / np .pi # type: ignore[assignment]
173
179
return tuple (angles .tolist ()) # type: ignore[return-value]
174
180
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
+
175
187
@property
176
188
def is_orthogonal (self ) -> bool :
177
189
"""Whether all angles are 90 degrees."""
178
190
return all (abs (a - 90 ) < 1e-5 for a in self .angles )
179
191
180
- @property
181
- def matrix (self ) -> NDArray [np .float64 ]:
182
- """Copy of matrix representing the Lattice."""
183
- return self ._matrix
184
-
185
192
@property
186
193
def is_3d_periodic (self ) -> bool :
187
194
"""True if the Lattice is periodic in all directions."""
@@ -507,12 +514,6 @@ def gamma(self) -> float:
507
514
"""Angle gamma of lattice in degrees."""
508
515
return self .angles [2 ]
509
516
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
-
516
517
@property
517
518
def parameters (self ) -> tuple [float , float , float , float , float , float ]:
518
519
"""6-tuple of floats (a, b, c, alpha, beta, gamma)."""
@@ -521,7 +522,7 @@ def parameters(self) -> tuple[float, float, float, float, float, float]:
521
522
@property
522
523
def params_dict (self ) -> dict [str , float ]:
523
524
"""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 ))
525
526
526
527
@property
527
528
def reciprocal_lattice (self ) -> Self :
@@ -897,11 +898,11 @@ def selling_dist(self, other: Self) -> float:
897
898
898
899
return min (np .linalg .norm (reflection - selling2 ) for reflection in all_reflections ) # type: ignore[return-value, type-var]
899
900
900
- def as_dict (self , verbosity : int = 0 ) -> dict :
901
+ def as_dict (self , verbosity : Literal [ 0 , 1 ] = 0 ) -> dict :
901
902
"""MSONable dict representation of the Lattice.
902
903
903
904
Args:
904
- verbosity (int ): Default of 0 only includes the matrix representation.
905
+ verbosity (0 | 1 ): Default of 0 only includes the matrix representation.
905
906
Set to 1 to include the lattice parameters.
906
907
"""
907
908
dct = {
@@ -910,7 +911,15 @@ def as_dict(self, verbosity: int = 0) -> dict:
910
911
"matrix" : self ._matrix .tolist (),
911
912
"pbc" : self .pbc ,
912
913
}
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`
914
923
dct |= self .params_dict
915
924
dct ["volume" ] = self .volume
916
925
@@ -1282,11 +1291,11 @@ def scale(self, new_volume: float) -> Self:
1282
1291
Returns:
1283
1292
New lattice with desired volume.
1284
1293
"""
1285
- versors = self .matrix / self .abc
1294
+ versors = self .matrix / self .lengths
1286
1295
1287
1296
geo_factor = abs (np .dot (np .cross (versors [0 ], versors [1 ]), versors [2 ]))
1288
1297
1289
- ratios = np .array (self .abc ) / self .c
1298
+ ratios = np .array (self .lengths ) / self .c
1290
1299
1291
1300
new_c = (new_volume / (geo_factor * np .prod (ratios ))) ** (1 / 3.0 )
1292
1301
@@ -1541,7 +1550,7 @@ def get_points_in_sphere_old(
1541
1550
# TODO: refactor to use lll matrix (nmax will be smaller)
1542
1551
# Determine the maximum number of supercells in each direction
1543
1552
# 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 )
1545
1554
nmax = float (r ) * recp_len + 0.01
1546
1555
1547
1556
# Get the fractional coordinates of the center of the sphere
@@ -1849,7 +1858,7 @@ def get_points_in_spheres(
1849
1858
if np .any (_pbc ):
1850
1859
if lattice is None :
1851
1860
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 )
1853
1862
maxr = np .ceil ((r + 0.15 ) * recp_len / (2 * math .pi ))
1854
1863
frac_coords = lattice .get_fractional_coords (center_coords )
1855
1864
nmin_temp = np .floor (np .min (frac_coords , axis = 0 )) - maxr
0 commit comments