structuretoolkit 0.0.43__tar.gz → 0.0.45__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/PKG-INFO +5 -5
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/pyproject.toml +5 -5
- structuretoolkit-0.0.45/src/structuretoolkit/_version.py +24 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/dscribe.py +1 -1
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/neighbors.py +48 -49
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/phonopy.py +2 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/pyscal.py +2 -2
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/snap.py +11 -11
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/spatial.py +20 -17
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/strain.py +20 -10
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/symmetry.py +13 -12
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/__init__.py +5 -3
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/compound.py +2 -2
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/geometry.py +3 -6
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/materialsproject.py +4 -2
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/mesh.py +10 -8
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/sqs/_interface.py +42 -30
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/sqs/_types.py +5 -4
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/common/helper.py +11 -11
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/visualize.py +8 -3
- structuretoolkit-0.0.43/src/structuretoolkit/_version.py +0 -34
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/.gitignore +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/LICENSE +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/README.md +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/__init__.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/__init__.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/distance.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/aimsgb.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/sqs/__init__.py +10 -10
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/surface.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/common/__init__.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/common/error.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/common/phonopy.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/common/pymatgen.py +0 -0
- {structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/common/pyscal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structuretoolkit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.45
|
|
4
4
|
Summary: build, analyse and visualise atomistic structures for materials science
|
|
5
5
|
Project-URL: Homepage, https://github.com/pyiron/structuretoolkit
|
|
6
6
|
Project-URL: Documentation, https://github.com/pyiron/structuretoolkit
|
|
@@ -46,8 +46,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
46
46
|
Classifier: Programming Language :: Python :: 3.13
|
|
47
47
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
48
48
|
Requires-Python: <3.15,>=3.10
|
|
49
|
-
Requires-Dist: ase<=3.
|
|
50
|
-
Requires-Dist: numpy<=2.4.
|
|
49
|
+
Requires-Dist: ase<=3.29.0,>=3.24.0
|
|
50
|
+
Requires-Dist: numpy<=2.4.6,>=1.23.5
|
|
51
51
|
Requires-Dist: scipy<=1.17.1,>=1.15.0
|
|
52
52
|
Provides-Extra: clusters
|
|
53
53
|
Requires-Dist: scikit-learn==1.8.0; extra == 'clusters'
|
|
@@ -64,10 +64,10 @@ Requires-Dist: pymatgen==2026.3.23; extra == 'mp-api'
|
|
|
64
64
|
Provides-Extra: nglview
|
|
65
65
|
Requires-Dist: nglview<=4.0.1,>=2.7.7; extra == 'nglview'
|
|
66
66
|
Provides-Extra: phonopy
|
|
67
|
-
Requires-Dist: phonopy<=
|
|
67
|
+
Requires-Dist: phonopy<=4.0.1,>=2.21.2; extra == 'phonopy'
|
|
68
68
|
Requires-Dist: spglib==2.7.0; extra == 'phonopy'
|
|
69
69
|
Provides-Extra: plotly
|
|
70
|
-
Requires-Dist: plotly<=6.
|
|
70
|
+
Requires-Dist: plotly<=6.8.0,>=6.0.0; extra == 'plotly'
|
|
71
71
|
Provides-Extra: pyscal
|
|
72
72
|
Requires-Dist: pyscal3<=3.3.2,>=3.2.5; extra == 'pyscal'
|
|
73
73
|
Provides-Extra: surface
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["ase", "numpy", "scipy", "hatchling==1.
|
|
2
|
+
requires = ["ase", "numpy", "scipy", "hatchling==1.30.1", "hatch-vcs==0.5.0"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -23,8 +23,8 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: 3.13",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"ase>=3.24.0,<=3.
|
|
27
|
-
"numpy>=1.23.5,<=2.4.
|
|
26
|
+
"ase>=3.24.0,<=3.29.0",
|
|
27
|
+
"numpy>=1.23.5,<=2.4.6",
|
|
28
28
|
"scipy>=1.15.0,<=1.17.1",
|
|
29
29
|
]
|
|
30
30
|
dynamic = ["version"]
|
|
@@ -43,7 +43,7 @@ grainboundary = [
|
|
|
43
43
|
pyscal = ["pyscal3>=3.2.5,<=3.3.2"]
|
|
44
44
|
nglview = ["nglview>=2.7.7,<=4.0.1"]
|
|
45
45
|
matplotlib = ["matplotlib==3.10.9"]
|
|
46
|
-
plotly = ["plotly>=6.0.0,<=6.
|
|
46
|
+
plotly = ["plotly>=6.0.0,<=6.8.0"]
|
|
47
47
|
clusters = ["scikit-learn==1.8.0"]
|
|
48
48
|
symmetry = ["spglib==2.7.0"]
|
|
49
49
|
surface = [
|
|
@@ -51,7 +51,7 @@ surface = [
|
|
|
51
51
|
"pymatgen==2026.3.23",
|
|
52
52
|
]
|
|
53
53
|
phonopy = [
|
|
54
|
-
"phonopy>=2.21.2,<=
|
|
54
|
+
"phonopy>=2.21.2,<=4.0.1",
|
|
55
55
|
"spglib==2.7.0",
|
|
56
56
|
]
|
|
57
57
|
mp-api = [
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.0.45'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 45)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -11,7 +11,7 @@ def soap_descriptor_per_atom(
|
|
|
11
11
|
rbf: str = "gto",
|
|
12
12
|
weighting: np.ndarray | None = None,
|
|
13
13
|
average: str = "off",
|
|
14
|
-
compression: dict = None,
|
|
14
|
+
compression: dict | None = None,
|
|
15
15
|
species: list | None = None,
|
|
16
16
|
periodic: bool = True,
|
|
17
17
|
sparse: bool = False,
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/neighbors.py
RENAMED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import itertools
|
|
5
5
|
import warnings
|
|
6
|
+
from typing import Any, cast
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
from ase.atoms import Atoms
|
|
@@ -56,18 +57,18 @@ class Tree:
|
|
|
56
57
|
Args:
|
|
57
58
|
ref_structure (ase.atoms.Atoms): Reference structure.
|
|
58
59
|
"""
|
|
59
|
-
self._distances = None
|
|
60
|
-
self._vectors = None
|
|
61
|
-
self._indices = None
|
|
60
|
+
self._distances: Any = None
|
|
61
|
+
self._vectors: Any = None
|
|
62
|
+
self._indices: Any = None
|
|
62
63
|
self._mode = {"filled": True, "ragged": False, "flattened": False}
|
|
63
|
-
self._extended_positions = None
|
|
64
|
-
self._positions = None
|
|
65
|
-
self._wrapped_indices = None
|
|
66
|
-
self._extended_indices = None
|
|
64
|
+
self._extended_positions: Any = None
|
|
65
|
+
self._positions: Any = None
|
|
66
|
+
self._wrapped_indices: Any = None
|
|
67
|
+
self._extended_indices: Any = None
|
|
67
68
|
self._ref_structure = ref_structure.copy()
|
|
68
69
|
self.wrap_positions = False
|
|
69
|
-
self._tree = None
|
|
70
|
-
self.num_neighbors = None
|
|
70
|
+
self._tree: Any = None
|
|
71
|
+
self.num_neighbors: int | None = None
|
|
71
72
|
self.cutoff_radius = np.inf
|
|
72
73
|
self._norm_order = 2
|
|
73
74
|
|
|
@@ -91,6 +92,7 @@ class Tree:
|
|
|
91
92
|
for k, v in self._mode.items():
|
|
92
93
|
if v:
|
|
93
94
|
return k
|
|
95
|
+
raise ValueError("No neighbor representation mode is active")
|
|
94
96
|
|
|
95
97
|
def _set_mode(self, new_mode: str) -> None:
|
|
96
98
|
"""
|
|
@@ -172,6 +174,7 @@ class Tree:
|
|
|
172
174
|
return self._contract(value, ref_vector=ref_vector)
|
|
173
175
|
elif key == "flattened":
|
|
174
176
|
return value[self._distances < np.inf]
|
|
177
|
+
raise ValueError(f"Unknown neighbor representation mode: {key}")
|
|
175
178
|
|
|
176
179
|
@property
|
|
177
180
|
def distances(self) -> np.ndarray:
|
|
@@ -257,7 +260,7 @@ class Tree:
|
|
|
257
260
|
+ " with the correct norm_order value"
|
|
258
261
|
)
|
|
259
262
|
|
|
260
|
-
def _get_max_length(self, ref_vector: np.ndarray | None = None) -> int:
|
|
263
|
+
def _get_max_length(self, ref_vector: np.ndarray | None = None) -> int | None:
|
|
261
264
|
"""
|
|
262
265
|
Get the maximum length of the reference vector.
|
|
263
266
|
|
|
@@ -277,9 +280,7 @@ class Tree:
|
|
|
277
280
|
return None
|
|
278
281
|
return max(len(dd[dd < np.inf]) for dd in ref_vector)
|
|
279
282
|
|
|
280
|
-
def _contract(
|
|
281
|
-
self, value: np.ndarray, ref_vector: np.ndarray | None = None
|
|
282
|
-
) -> np.ndarray:
|
|
283
|
+
def _contract(self, value: np.ndarray, ref_vector: np.ndarray | None = None):
|
|
283
284
|
"""
|
|
284
285
|
Contract the given value based on the specified reference vector.
|
|
285
286
|
|
|
@@ -297,7 +298,7 @@ class Tree:
|
|
|
297
298
|
for vv, dist in zip(value, self.filled.distances, strict=True)
|
|
298
299
|
]
|
|
299
300
|
|
|
300
|
-
def _allow_ragged_to_mode(self, new_bool: bool) -> str:
|
|
301
|
+
def _allow_ragged_to_mode(self, new_bool: bool | None) -> str:
|
|
301
302
|
"""
|
|
302
303
|
Set the representation mode based on the value of new_bool.
|
|
303
304
|
|
|
@@ -504,6 +505,8 @@ class Tree:
|
|
|
504
505
|
if self.num_neighbors is None:
|
|
505
506
|
self.num_neighbors = num_neighbors
|
|
506
507
|
self.cutoff_radius = cutoff_radius
|
|
508
|
+
assert num_neighbors is not None
|
|
509
|
+
assert self.num_neighbors is not None
|
|
507
510
|
if num_neighbors > self.num_neighbors:
|
|
508
511
|
warnings.warn(
|
|
509
512
|
"Taking a larger search area after initialization has the risk of "
|
|
@@ -582,7 +585,7 @@ class Tree:
|
|
|
582
585
|
def _get_neighborhood(
|
|
583
586
|
self,
|
|
584
587
|
positions: np.ndarray,
|
|
585
|
-
num_neighbors: int = 12,
|
|
588
|
+
num_neighbors: int | None = 12,
|
|
586
589
|
cutoff_radius: float = np.inf,
|
|
587
590
|
exclude_self: bool = False,
|
|
588
591
|
width_buffer: float = 1.2,
|
|
@@ -611,7 +614,7 @@ class Tree:
|
|
|
611
614
|
cutoff_radius=cutoff_radius,
|
|
612
615
|
width_buffer=width_buffer,
|
|
613
616
|
)
|
|
614
|
-
if num_neighbors is not None:
|
|
617
|
+
if num_neighbors is not None and self.num_neighbors is not None:
|
|
615
618
|
self.num_neighbors -= 1
|
|
616
619
|
max_column = np.sum(distances < np.inf, axis=-1).max()
|
|
617
620
|
self._distances = distances[..., start_column:max_column]
|
|
@@ -620,7 +623,7 @@ class Tree:
|
|
|
620
623
|
self._positions = positions
|
|
621
624
|
return self
|
|
622
625
|
|
|
623
|
-
def _check_width(self, width: float, pbc:
|
|
626
|
+
def _check_width(self, width: float, pbc: np.ndarray) -> bool:
|
|
624
627
|
"""
|
|
625
628
|
Check if the width of the layer exceeds the specified value.
|
|
626
629
|
|
|
@@ -706,26 +709,19 @@ class Tree:
|
|
|
706
709
|
radius. For automated uses, see Atoms.analyse.pyscal_steinhardt_parameter()
|
|
707
710
|
"""
|
|
708
711
|
random_rotation = Rotation.from_mrp(np.random.random(3)).as_matrix()
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
l=l,
|
|
718
|
-
m=m,
|
|
719
|
-
cutoff_radius=cutoff_radius,
|
|
720
|
-
rotation=random_rotation,
|
|
721
|
-
)
|
|
722
|
-
)
|
|
723
|
-
** 2
|
|
724
|
-
for m in np.arange(-l, l + 1)
|
|
725
|
-
],
|
|
726
|
-
axis=0,
|
|
712
|
+
harmonic_values: list[np.ndarray] = [
|
|
713
|
+
np.absolute(
|
|
714
|
+
self.get_spherical_harmonics(
|
|
715
|
+
l=l,
|
|
716
|
+
m=m,
|
|
717
|
+
cutoff_radius=cutoff_radius,
|
|
718
|
+
rotation=random_rotation,
|
|
719
|
+
)
|
|
727
720
|
)
|
|
728
|
-
|
|
721
|
+
** 2
|
|
722
|
+
for m in np.arange(-l, l + 1)
|
|
723
|
+
]
|
|
724
|
+
return np.sqrt(4 * np.pi / (2 * l + 1) * np.sum(harmonic_values, axis=0))
|
|
729
725
|
|
|
730
726
|
@staticmethod
|
|
731
727
|
def _get_all_possible_pairs(l: int) -> np.ndarray:
|
|
@@ -827,8 +823,8 @@ class Neighbors(Tree):
|
|
|
827
823
|
"""
|
|
828
824
|
super().__init__(ref_structure=ref_structure)
|
|
829
825
|
self._tolerance = tolerance
|
|
830
|
-
self._cluster_vecs = None
|
|
831
|
-
self._cluster_dist = None
|
|
826
|
+
self._cluster_vecs: Any = None
|
|
827
|
+
self._cluster_dist: Any = None
|
|
832
828
|
|
|
833
829
|
def __repr__(self):
|
|
834
830
|
"""
|
|
@@ -1072,7 +1068,7 @@ class Neighbors(Tree):
|
|
|
1072
1068
|
|
|
1073
1069
|
def find_neighbors_by_vector(
|
|
1074
1070
|
self, vector: np.ndarray, return_deviation: bool = False
|
|
1075
|
-
) -> np.ndarray:
|
|
1071
|
+
) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
|
|
1076
1072
|
"""
|
|
1077
1073
|
Args:
|
|
1078
1074
|
vector (list/np.ndarray): vector by which positions are translated (and neighbors are searched)
|
|
@@ -1323,9 +1319,9 @@ class Neighbors(Tree):
|
|
|
1323
1319
|
ind_shell = []
|
|
1324
1320
|
for d, i in zip(dist, ind, strict=True):
|
|
1325
1321
|
id_list = get_cluster(d[d < radius], i[d < radius])
|
|
1326
|
-
ia_shells_dict = {}
|
|
1322
|
+
ia_shells_dict: dict[str, list[list[int]]] = {}
|
|
1327
1323
|
for i_shell_list in id_list:
|
|
1328
|
-
ia_shell_dict = {}
|
|
1324
|
+
ia_shell_dict: dict[str, list[int]] = {}
|
|
1329
1325
|
for i_s in i_shell_list:
|
|
1330
1326
|
el = el_list[i_s]
|
|
1331
1327
|
if el not in ia_shell_dict:
|
|
@@ -1382,14 +1378,17 @@ def get_neighbors(
|
|
|
1382
1378
|
Returns:
|
|
1383
1379
|
Neighbors: An instance of the Neighbors class with the neighbor indices, distances, and vectors.
|
|
1384
1380
|
"""
|
|
1385
|
-
neigh =
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1381
|
+
neigh = cast(
|
|
1382
|
+
Neighbors,
|
|
1383
|
+
_get_neighbors(
|
|
1384
|
+
structure=structure,
|
|
1385
|
+
num_neighbors=num_neighbors,
|
|
1386
|
+
tolerance=tolerance,
|
|
1387
|
+
id_list=id_list,
|
|
1388
|
+
cutoff_radius=cutoff_radius,
|
|
1389
|
+
width_buffer=width_buffer,
|
|
1390
|
+
norm_order=norm_order,
|
|
1391
|
+
),
|
|
1393
1392
|
)
|
|
1394
1393
|
neigh._set_mode(mode)
|
|
1395
1394
|
return neigh
|
|
@@ -27,7 +27,7 @@ def get_steinhardt_parameters(
|
|
|
27
27
|
n_clusters: int | None = 2,
|
|
28
28
|
q: tuple | None = None,
|
|
29
29
|
averaged: bool = False,
|
|
30
|
-
) ->
|
|
30
|
+
) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
|
|
31
31
|
"""
|
|
32
32
|
Calculate Steinhardts parameters
|
|
33
33
|
|
|
@@ -162,7 +162,7 @@ def get_diamond_structure_descriptors(
|
|
|
162
162
|
|
|
163
163
|
def get_adaptive_cna_descriptors(
|
|
164
164
|
structure: Atoms, mode: str = "total", ovito_compatibility: bool = False
|
|
165
|
-
) -> np.ndarray:
|
|
165
|
+
) -> dict | np.ndarray:
|
|
166
166
|
"""
|
|
167
167
|
Use common neighbor analysis
|
|
168
168
|
|
|
@@ -63,7 +63,7 @@ def get_snap_descriptors_per_atom(
|
|
|
63
63
|
structure: Atoms,
|
|
64
64
|
atom_types: list[str],
|
|
65
65
|
twojmax: int = 6,
|
|
66
|
-
element_radius: list[
|
|
66
|
+
element_radius: list[float] | None = None,
|
|
67
67
|
rcutfac: float = 1.0,
|
|
68
68
|
rfac0: float = 0.99363,
|
|
69
69
|
rmin0: float = 0.0,
|
|
@@ -114,7 +114,7 @@ def get_snap_descriptor_derivatives(
|
|
|
114
114
|
structure: Atoms,
|
|
115
115
|
atom_types: list[str],
|
|
116
116
|
twojmax: int = 6,
|
|
117
|
-
element_radius: list[
|
|
117
|
+
element_radius: list[float] | None = None,
|
|
118
118
|
rcutfac: float = 1.0,
|
|
119
119
|
rfac0: float = 0.99363,
|
|
120
120
|
rmin0: float = 0.0,
|
|
@@ -161,7 +161,7 @@ def get_snap_descriptor_derivatives(
|
|
|
161
161
|
)
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def get_snap_descriptor_names(twojmax: int) ->
|
|
164
|
+
def get_snap_descriptor_names(twojmax: int) -> list[list[float]]:
|
|
165
165
|
"""
|
|
166
166
|
Get names of the SNAP descriptors
|
|
167
167
|
|
|
@@ -268,8 +268,8 @@ def _set_ase_structure(lmp, structure: Atoms):
|
|
|
268
268
|
elem_all = np.array([el_dict[el] + 1 for el in structure.get_chemical_symbols()])
|
|
269
269
|
lmp.create_atoms(
|
|
270
270
|
n=len(structure),
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
atomid=None,
|
|
272
|
+
atype=(len(elem_all) * c_int)(*elem_all),
|
|
273
273
|
x=(len(positions) * c_double)(*positions),
|
|
274
274
|
v=None,
|
|
275
275
|
image=None,
|
|
@@ -547,16 +547,16 @@ def _extract_computes_snap(
|
|
|
547
547
|
Returns:
|
|
548
548
|
np.ndarray: Output of the LAMMPS compute command
|
|
549
549
|
"""
|
|
550
|
-
lmp_atom_ids = lmp.numpy.
|
|
551
|
-
cond = np.all(lmp_atom_ids == 1 + np.arange(num_atoms))
|
|
550
|
+
lmp_atom_ids = lmp.numpy.extract_atom(name="id", nelem=num_atoms).flatten()
|
|
551
|
+
cond = bool(np.all(lmp_atom_ids == 1 + np.arange(num_atoms)))
|
|
552
552
|
assert cond, "LAMMPS seems to have lost atoms"
|
|
553
553
|
|
|
554
554
|
# Extract types
|
|
555
|
-
lmp_types = lmp.numpy.
|
|
555
|
+
lmp_types = lmp.numpy.extract_atom(name="type", nelem=num_atoms).flatten()
|
|
556
556
|
lmp_volume = lmp.get_thermo("vol")
|
|
557
557
|
|
|
558
558
|
# Extract Bsum
|
|
559
|
-
_extract_compute_np(lmp, "b_sum", 0, 1, (n_coeff))
|
|
559
|
+
_extract_compute_np(lmp, "b_sum", 0, 1, (n_coeff,))
|
|
560
560
|
|
|
561
561
|
# Extract B
|
|
562
562
|
lmp_barr = _extract_compute_np(lmp, "b", 1, 2, (num_atoms, n_coeff))
|
|
@@ -571,13 +571,13 @@ def _extract_computes_snap(
|
|
|
571
571
|
|
|
572
572
|
lmp_dbarr = _extract_compute_np(lmp, "db", 1, 2, (num_atoms, num_types, 3, n_coeff))
|
|
573
573
|
lmp_dbsum = _extract_compute_np(lmp, "db_sum", 0, 1, (num_types, 3, n_coeff))
|
|
574
|
-
cond = np.allclose(lmp_dbsum, lmp_dbarr.sum(axis=0), rtol=1e-12, atol=1e-12)
|
|
574
|
+
cond = bool(np.allclose(lmp_dbsum, lmp_dbarr.sum(axis=0), rtol=1e-12, atol=1e-12))
|
|
575
575
|
assert cond, "db_sum doesn't match sum of db"
|
|
576
576
|
db_atom = np.transpose(lmp_dbarr, (0, 2, 1, 3))
|
|
577
577
|
|
|
578
578
|
lmp_vbarr = _extract_compute_np(lmp, "vb", 1, 2, (num_atoms, num_types, 6, n_coeff))
|
|
579
579
|
lmp_vbsum = _extract_compute_np(lmp, "vb_sum", 0, 1, (num_types, 6, n_coeff))
|
|
580
|
-
cond = np.allclose(lmp_vbsum, lmp_vbarr.sum(axis=0), rtol=1e-12, atol=1e-12)
|
|
580
|
+
cond = bool(np.allclose(lmp_vbsum, lmp_vbarr.sum(axis=0), rtol=1e-12, atol=1e-12))
|
|
581
581
|
assert cond, "vb_sum doesn't match sum of vb"
|
|
582
582
|
vb_sum = np.transpose(lmp_vbsum, (1, 0, 2)) / lmp_volume * eV_div_A3_to_bar
|
|
583
583
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Distributed under the terms of "New BSD License", see the LICENSE file.
|
|
3
3
|
|
|
4
4
|
from collections.abc import Callable
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
from ase.atoms import Atoms
|
|
@@ -74,10 +75,10 @@ def create_gridpoints(
|
|
|
74
75
|
"""
|
|
75
76
|
cell = get_vertical_length(structure=structure)
|
|
76
77
|
n_points = (n_gridpoints_per_angstrom * cell).astype(int)
|
|
77
|
-
|
|
78
|
+
grids = np.meshgrid(
|
|
78
79
|
*[np.linspace(0, 1, n_points[i], endpoint=False) for i in range(3)]
|
|
79
80
|
)
|
|
80
|
-
positions = np.stack(
|
|
81
|
+
positions = np.stack(grids, axis=-1).reshape(-1, 3)
|
|
81
82
|
return np.einsum("ji,nj->ni", structure.cell, positions)
|
|
82
83
|
|
|
83
84
|
|
|
@@ -226,7 +227,7 @@ class Interstitials:
|
|
|
226
227
|
q_eps: float = 0.3,
|
|
227
228
|
var_ratio: float = 5.0,
|
|
228
229
|
min_samples: int | None = None,
|
|
229
|
-
neigh_args: dict = None,
|
|
230
|
+
neigh_args: dict | None = None,
|
|
230
231
|
**kwargs,
|
|
231
232
|
):
|
|
232
233
|
"""
|
|
@@ -266,7 +267,7 @@ class Interstitials:
|
|
|
266
267
|
self._neigh = get_neighbors(
|
|
267
268
|
structure=structure, num_neighbors=num_neighbors, **neigh_args
|
|
268
269
|
)
|
|
269
|
-
self.workflow = [
|
|
270
|
+
self.workflow: list[dict[str, Any]] = [
|
|
270
271
|
{
|
|
271
272
|
"f": remove_too_close,
|
|
272
273
|
"kwargs": {"structure": structure, "min_distance": min_distance},
|
|
@@ -290,7 +291,7 @@ class Interstitials:
|
|
|
290
291
|
},
|
|
291
292
|
},
|
|
292
293
|
]
|
|
293
|
-
self._positions = None
|
|
294
|
+
self._positions: np.ndarray | None = None
|
|
294
295
|
self.structure = structure
|
|
295
296
|
|
|
296
297
|
def run_workflow(
|
|
@@ -312,7 +313,8 @@ class Interstitials:
|
|
|
312
313
|
if positions is None:
|
|
313
314
|
positions = self.initial_positions.copy()
|
|
314
315
|
for ii, ww in enumerate(self.workflow):
|
|
315
|
-
|
|
316
|
+
f: Callable[..., np.ndarray] = ww["f"]
|
|
317
|
+
positions = f(positions=positions, **ww["kwargs"])
|
|
316
318
|
if ii == steps:
|
|
317
319
|
return positions
|
|
318
320
|
return positions
|
|
@@ -338,6 +340,7 @@ class Interstitials:
|
|
|
338
340
|
if self._positions is None:
|
|
339
341
|
self._positions = self.run_workflow()
|
|
340
342
|
self._neigh = self.neigh.get_neighborhood(self._positions)
|
|
343
|
+
assert self._positions is not None
|
|
341
344
|
return self._positions
|
|
342
345
|
|
|
343
346
|
@property
|
|
@@ -408,7 +411,7 @@ def get_interstitials(
|
|
|
408
411
|
q_eps: float = 0.3,
|
|
409
412
|
var_ratio: float = 5.0,
|
|
410
413
|
min_samples: int | None = None,
|
|
411
|
-
neigh_args: dict = None,
|
|
414
|
+
neigh_args: dict | None = None,
|
|
412
415
|
**kwargs,
|
|
413
416
|
) -> Interstitials:
|
|
414
417
|
"""
|
|
@@ -449,9 +452,9 @@ def get_interstitials(
|
|
|
449
452
|
)
|
|
450
453
|
|
|
451
454
|
|
|
452
|
-
get_interstitials.__doc__ = (
|
|
453
|
-
|
|
454
|
-
)
|
|
455
|
+
get_interstitials.__doc__ = (Interstitials.__doc__ or "").replace(
|
|
456
|
+
"Class", "Function"
|
|
457
|
+
) + (Interstitials.__init__.__doc__ or "")
|
|
455
458
|
|
|
456
459
|
|
|
457
460
|
def get_layers(
|
|
@@ -459,8 +462,8 @@ def get_layers(
|
|
|
459
462
|
distance_threshold: float = 0.01,
|
|
460
463
|
id_list: list[int] | None = None,
|
|
461
464
|
wrap_atoms: bool = True,
|
|
462
|
-
planes: np.ndarray = None,
|
|
463
|
-
cluster_method:
|
|
465
|
+
planes: np.ndarray | None = None,
|
|
466
|
+
cluster_method: Any | None = None,
|
|
464
467
|
) -> np.ndarray:
|
|
465
468
|
"""
|
|
466
469
|
Get an array of layer numbers.
|
|
@@ -512,10 +515,10 @@ def get_layers(
|
|
|
512
515
|
structure=structure, width=distance_threshold, return_indices=True
|
|
513
516
|
)
|
|
514
517
|
if id_list is not None:
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
positions = positions[
|
|
518
|
-
indices = indices[
|
|
518
|
+
id_arr = np.arange(len(structure))[np.array(id_list)]
|
|
519
|
+
id_mask = np.any(id_arr[:, np.newaxis] == indices[np.newaxis, :], axis=0)
|
|
520
|
+
positions = positions[id_mask]
|
|
521
|
+
indices = indices[id_mask]
|
|
519
522
|
else:
|
|
520
523
|
positions = structure.positions
|
|
521
524
|
if id_list is not None:
|
|
@@ -694,7 +697,7 @@ def get_cluster_positions(
|
|
|
694
697
|
eps: float = 1.0,
|
|
695
698
|
buffer_width: float | None = None,
|
|
696
699
|
return_labels: bool = False,
|
|
697
|
-
) -> np.ndarray:
|
|
700
|
+
) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
|
|
698
701
|
"""
|
|
699
702
|
Cluster positions according to the distances. Clustering algorithm uses DBSCAN:
|
|
700
703
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
from ase.atoms import Atoms
|
|
3
5
|
from scipy.spatial.transform import Rotation
|
|
@@ -47,18 +49,19 @@ class Strain:
|
|
|
47
49
|
"""
|
|
48
50
|
self.structure = structure
|
|
49
51
|
self.ref_structure = ref_structure
|
|
50
|
-
self._num_neighbors = num_neighbors
|
|
52
|
+
self._num_neighbors: int | None = num_neighbors
|
|
51
53
|
self.only_bulk_type = only_bulk_type
|
|
52
|
-
self._crystal_phase = None
|
|
53
|
-
self._ref_coord = None
|
|
54
|
-
self._coords = None
|
|
55
|
-
self._rotations = None
|
|
54
|
+
self._crystal_phase: str | None = None
|
|
55
|
+
self._ref_coord: np.ndarray | None = None
|
|
56
|
+
self._coords: np.ndarray | None = None
|
|
57
|
+
self._rotations: np.ndarray | None = None
|
|
56
58
|
|
|
57
59
|
@property
|
|
58
60
|
def num_neighbors(self) -> int:
|
|
59
61
|
"""Number of neighbors to consider the local frame. Should be the coordination number."""
|
|
60
62
|
if self._num_neighbors is None:
|
|
61
63
|
self._num_neighbors = self._get_number_of_neighbors(self.crystal_phase)
|
|
64
|
+
assert self._num_neighbors is not None
|
|
62
65
|
return self._num_neighbors
|
|
63
66
|
|
|
64
67
|
@property
|
|
@@ -66,6 +69,7 @@ class Strain:
|
|
|
66
69
|
"""Majority crystal phase calculated via common neighbor analysis."""
|
|
67
70
|
if self._crystal_phase is None:
|
|
68
71
|
self._crystal_phase = self._get_majority_phase(self.ref_structure)
|
|
72
|
+
assert self._crystal_phase is not None
|
|
69
73
|
return self._crystal_phase
|
|
70
74
|
|
|
71
75
|
@property
|
|
@@ -79,7 +83,8 @@ class Strain:
|
|
|
79
83
|
than the bulk.
|
|
80
84
|
"""
|
|
81
85
|
return np.array(
|
|
82
|
-
self.structure
|
|
86
|
+
get_adaptive_cna_descriptors(structure=self.structure, mode="str")
|
|
87
|
+
!= self.crystal_phase
|
|
83
88
|
)
|
|
84
89
|
|
|
85
90
|
def _get_perpendicular_unit_vectors(
|
|
@@ -197,6 +202,7 @@ class Strain:
|
|
|
197
202
|
w_second = self.ref_coord[dist.argmin(axis=1)].copy()
|
|
198
203
|
second_rot = self._get_rotation_from_vectors(v, w_second, all_vecs[:, 0])
|
|
199
204
|
self._rotations = np.einsum("nij,njk->nik", second_rot, first_rot)
|
|
205
|
+
assert self._rotations is not None
|
|
200
206
|
return self._rotations
|
|
201
207
|
|
|
202
208
|
@staticmethod
|
|
@@ -219,7 +225,7 @@ class Strain:
|
|
|
219
225
|
return np.argmin(distances, axis=-1)
|
|
220
226
|
|
|
221
227
|
@staticmethod
|
|
222
|
-
def _get_majority_phase(structure: Atoms) ->
|
|
228
|
+
def _get_majority_phase(structure: Atoms) -> str:
|
|
223
229
|
"""
|
|
224
230
|
Get the majority crystal phase in the structure based on the common neighbor analysis (CNA) descriptors.
|
|
225
231
|
|
|
@@ -227,10 +233,12 @@ class Strain:
|
|
|
227
233
|
structure (ase.atoms.Atoms): The structure to analyze.
|
|
228
234
|
|
|
229
235
|
Returns:
|
|
230
|
-
|
|
236
|
+
str: The crystal phase with the highest count.
|
|
231
237
|
"""
|
|
232
238
|
cna = get_adaptive_cna_descriptors(structure=structure)
|
|
233
|
-
|
|
239
|
+
if not isinstance(cna, dict):
|
|
240
|
+
raise TypeError("Expected CNA descriptors as a dictionary")
|
|
241
|
+
return str(np.asarray(list(cna.keys()))[np.argmax(list(cna.values()))])
|
|
234
242
|
|
|
235
243
|
@staticmethod
|
|
236
244
|
def _get_number_of_neighbors(crystal_phase: str) -> int:
|
|
@@ -265,6 +273,7 @@ class Strain:
|
|
|
265
273
|
self._ref_coord = get_neighbors(
|
|
266
274
|
structure=self.ref_structure, num_neighbors=self.num_neighbors
|
|
267
275
|
).vecs[0]
|
|
276
|
+
assert self._ref_coord is not None
|
|
268
277
|
return self._ref_coord
|
|
269
278
|
|
|
270
279
|
@property
|
|
@@ -279,6 +288,7 @@ class Strain:
|
|
|
279
288
|
self._coords = get_neighbors(
|
|
280
289
|
structure=self.structure, num_neighbors=self.num_neighbors
|
|
281
290
|
).vecs
|
|
291
|
+
assert self._coords is not None
|
|
282
292
|
return self._coords
|
|
283
293
|
|
|
284
294
|
@property
|
|
@@ -320,7 +330,7 @@ def get_strain(
|
|
|
320
330
|
num_neighbors: int | None = None,
|
|
321
331
|
only_bulk_type: bool = False,
|
|
322
332
|
return_object: bool = False,
|
|
323
|
-
):
|
|
333
|
+
) -> Union[np.ndarray, "Strain"]:
|
|
324
334
|
"""
|
|
325
335
|
Calculate local strain of each atom following the Lagrangian strain tensor:
|
|
326
336
|
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/symmetry.py
RENAMED
|
@@ -63,7 +63,7 @@ class Symmetry(dict):
|
|
|
63
63
|
self._symprec = symprec
|
|
64
64
|
self._angle_tolerance = angle_tolerance
|
|
65
65
|
self.epsilon = epsilon
|
|
66
|
-
self._permutations = None
|
|
66
|
+
self._permutations: np.ndarray | None = None
|
|
67
67
|
for k, v in self._get_symmetry(
|
|
68
68
|
symprec=symprec, angle_tolerance=angle_tolerance
|
|
69
69
|
).items():
|
|
@@ -209,6 +209,7 @@ class Symmetry(dict):
|
|
|
209
209
|
if np.ptp(distances) > self._symprec:
|
|
210
210
|
raise AssertionError("Neighbor search failed")
|
|
211
211
|
self._permutations = self._permutations.argsort(axis=-1)
|
|
212
|
+
assert self._permutations is not None
|
|
212
213
|
return self._permutations
|
|
213
214
|
|
|
214
215
|
def symmetrize_vectors(
|
|
@@ -369,12 +370,12 @@ class Symmetry(dict):
|
|
|
369
370
|
)
|
|
370
371
|
if space_group is None:
|
|
371
372
|
raise SymmetryError(spglib.error.get_error_message())
|
|
372
|
-
|
|
373
|
-
if len(
|
|
374
|
-
return {"Number": ast.literal_eval(
|
|
373
|
+
parts = space_group.split()
|
|
374
|
+
if len(parts) == 1:
|
|
375
|
+
return {"Number": ast.literal_eval(parts[0])}
|
|
375
376
|
return {
|
|
376
|
-
"InternationalTableSymbol":
|
|
377
|
-
"Number": ast.literal_eval(
|
|
377
|
+
"InternationalTableSymbol": parts[0],
|
|
378
|
+
"Number": ast.literal_eval(parts[1]),
|
|
378
379
|
}
|
|
379
380
|
|
|
380
381
|
def get_primitive_cell(
|
|
@@ -442,7 +443,7 @@ class Symmetry(dict):
|
|
|
442
443
|
mesh: np.ndarray,
|
|
443
444
|
is_shift: np.ndarray = np.zeros(3, dtype="intc"),
|
|
444
445
|
is_time_reversal: bool = True,
|
|
445
|
-
) -> np.ndarray:
|
|
446
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
446
447
|
"""
|
|
447
448
|
Get the irreducible reciprocal mesh points.
|
|
448
449
|
|
|
@@ -452,21 +453,21 @@ class Symmetry(dict):
|
|
|
452
453
|
is_time_reversal (bool, optional): Whether to consider time reversal symmetry. Defaults to True.
|
|
453
454
|
|
|
454
455
|
Returns:
|
|
455
|
-
|
|
456
|
+
tuple: The irreducible reciprocal mesh points (mapping, grid_points).
|
|
456
457
|
|
|
457
458
|
Raises:
|
|
458
459
|
SymmetryError: If the irreducible reciprocal mesh points cannot be obtained.
|
|
459
460
|
"""
|
|
460
|
-
|
|
461
|
+
result = spglib.get_ir_reciprocal_mesh(
|
|
461
462
|
mesh=mesh,
|
|
462
463
|
cell=self._get_spglib_cell(),
|
|
463
464
|
is_shift=is_shift,
|
|
464
465
|
is_time_reversal=is_time_reversal,
|
|
465
466
|
symprec=self._symprec,
|
|
466
467
|
)
|
|
467
|
-
if
|
|
468
|
+
if result is None:
|
|
468
469
|
raise SymmetryError(spglib.error.get_error_message())
|
|
469
|
-
return
|
|
470
|
+
return result
|
|
470
471
|
|
|
471
472
|
|
|
472
473
|
def _get_inner_slicer(n: int, i: int) -> tuple:
|
|
@@ -481,7 +482,7 @@ def _get_inner_slicer(n: int, i: int) -> tuple:
|
|
|
481
482
|
tuple: Inner slicer tuple.
|
|
482
483
|
|
|
483
484
|
"""
|
|
484
|
-
s = [None for _ in range(n)]
|
|
485
|
+
s: list[None | slice] = [None for _ in range(n)]
|
|
485
486
|
s[0] = slice(None)
|
|
486
487
|
s[i] = slice(None)
|
|
487
488
|
return tuple(s)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from structuretoolkit.build.aimsgb import get_grainboundary_info, grainboundary
|
|
2
2
|
from structuretoolkit.build.compound import B2, C14, C15, C36, D03
|
|
3
|
-
from structuretoolkit.build.mesh import create_mesh
|
|
4
|
-
from structuretoolkit.build.sqs import sqs_structures
|
|
5
3
|
from structuretoolkit.build.materialsproject import (
|
|
6
|
-
search as materialsproject_search,
|
|
7
4
|
by_id as materialsproject_by_id,
|
|
8
5
|
)
|
|
6
|
+
from structuretoolkit.build.materialsproject import (
|
|
7
|
+
search as materialsproject_search,
|
|
8
|
+
)
|
|
9
|
+
from structuretoolkit.build.mesh import create_mesh
|
|
10
|
+
from structuretoolkit.build.sqs import sqs_structures
|
|
9
11
|
from structuretoolkit.build.surface import (
|
|
10
12
|
get_high_index_surface_info,
|
|
11
13
|
high_index_surface,
|
|
@@ -6,7 +6,7 @@ from ase.spacegroup import crystal
|
|
|
6
6
|
from structuretoolkit.analyse.neighbors import get_neighbors
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def B2(element_a: str, element_b: str, a: float | None = None):
|
|
9
|
+
def B2(element_a: str, element_b: str, a: float | None = None) -> Atoms:
|
|
10
10
|
"""
|
|
11
11
|
Builds a cubic $AB$ B2 structure of interpenetrating simple cubic lattices.
|
|
12
12
|
|
|
@@ -207,7 +207,7 @@ def D03(element_a: str, element_b: str, a: float | None = None) -> Atoms:
|
|
|
207
207
|
)
|
|
208
208
|
|
|
209
209
|
|
|
210
|
-
def _bcc_lattice_constant_from_nn_distance(element:
|
|
210
|
+
def _bcc_lattice_constant_from_nn_distance(element: str) -> float:
|
|
211
211
|
"""
|
|
212
212
|
Build a BCC lattice constant by making the BCC have the same nearest neighbour distance as the regular cell.
|
|
213
213
|
|
|
@@ -51,8 +51,7 @@ def repulse(
|
|
|
51
51
|
"""
|
|
52
52
|
if not inplace:
|
|
53
53
|
structure = structure.copy()
|
|
54
|
-
if axis is None
|
|
55
|
-
axis = slice(None)
|
|
54
|
+
ax: int | slice = axis if axis is not None else slice(None)
|
|
56
55
|
for _ in range(iterations):
|
|
57
56
|
neigh = get_neighbors(structure, num_neighbors=1)
|
|
58
57
|
dd = neigh.distances[:, 0]
|
|
@@ -78,7 +77,7 @@ def repulse(
|
|
|
78
77
|
disp = np.clip(min_dist - dd[I], 0, step_size)
|
|
79
78
|
|
|
80
79
|
displacement = disp[:, None] * vv # (N_close, 3)
|
|
81
|
-
structure.positions[I,
|
|
80
|
+
structure.positions[I, ax] -= displacement[:, ax]
|
|
82
81
|
|
|
83
82
|
else:
|
|
84
83
|
raise RuntimeError(f"repulse did not converge within {iterations} iterations")
|
|
@@ -86,9 +85,7 @@ def repulse(
|
|
|
86
85
|
return structure
|
|
87
86
|
|
|
88
87
|
|
|
89
|
-
def merge(
|
|
90
|
-
structure: "ase.Atoms", cutoff: float = 1.8, iterations: int = 10
|
|
91
|
-
) -> "ase.Atoms":
|
|
88
|
+
def merge(structure: Atoms, cutoff: float = 1.8, iterations: int = 10) -> Atoms:
|
|
92
89
|
"""Merge pairs of atoms that are closer than ``cutoff`` by collapsing each
|
|
93
90
|
pair to their midpoint and deleting one of the two atoms.
|
|
94
91
|
|
|
@@ -12,7 +12,7 @@ class MeshInputError(ValueError):
|
|
|
12
12
|
|
|
13
13
|
def create_mesh(
|
|
14
14
|
cell: ase.atoms.Atoms | np.ndarray | list | float,
|
|
15
|
-
n_mesh: int | list[int
|
|
15
|
+
n_mesh: int | list[int] | None = 10,
|
|
16
16
|
density: float | None = None,
|
|
17
17
|
endpoint: bool = False,
|
|
18
18
|
):
|
|
@@ -32,20 +32,22 @@ def create_mesh(
|
|
|
32
32
|
Returns:
|
|
33
33
|
(3, n, n, n)-array: mesh
|
|
34
34
|
"""
|
|
35
|
-
cell = get_cell(cell)
|
|
35
|
+
cell = np.asarray(get_cell(cell), dtype=float)
|
|
36
|
+
n_arr: np.ndarray
|
|
36
37
|
if n_mesh is None:
|
|
37
38
|
if density is None:
|
|
38
39
|
raise MeshInputError("either n_mesh or density must be specified")
|
|
39
|
-
|
|
40
|
+
n_arr = np.rint(np.linalg.norm(cell, axis=-1) / density).astype(int)
|
|
40
41
|
elif density is not None:
|
|
41
42
|
raise MeshInputError(
|
|
42
43
|
"You cannot set n_mesh at density at the same time. Set one of them to None"
|
|
43
44
|
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
else:
|
|
46
|
+
n_arr = np.atleast_1d(n_mesh).astype(int)
|
|
47
|
+
if len(n_arr) == 1:
|
|
48
|
+
n_arr = np.repeat(n_arr, 3)
|
|
49
|
+
elif len(n_arr) != 3:
|
|
48
50
|
raise MeshInputError("n_mesh must be a 3-dim vector")
|
|
49
|
-
linspace = [np.linspace(0, 1, nn, endpoint=endpoint) for nn in
|
|
51
|
+
linspace = [np.linspace(0, 1, nn, endpoint=endpoint) for nn in n_arr]
|
|
50
52
|
x_mesh = np.meshgrid(*linspace, indexing="ij")
|
|
51
53
|
return np.einsum("ixyz,ij->jxyz", x_mesh, cell)
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/sqs/_interface.py
RENAMED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterator
|
|
3
4
|
from threading import Event, Thread
|
|
4
|
-
from typing import
|
|
5
|
+
from typing import Any, Generic, Literal, TypeVar, cast, overload
|
|
6
|
+
|
|
7
|
+
from ase.atoms import Atoms
|
|
5
8
|
|
|
6
9
|
from ._types import (
|
|
7
10
|
Composition,
|
|
8
|
-
ShellWeights,
|
|
9
|
-
ShellRadii,
|
|
10
|
-
SublatticeMode,
|
|
11
11
|
IterationMode,
|
|
12
|
+
LogLevel,
|
|
12
13
|
Prec,
|
|
14
|
+
ShellRadii,
|
|
15
|
+
ShellWeights,
|
|
13
16
|
SqsResultInteract,
|
|
14
17
|
SqsResultSplit,
|
|
15
|
-
|
|
18
|
+
SublatticeMode,
|
|
16
19
|
)
|
|
17
20
|
|
|
18
21
|
R = TypeVar("R", SqsResultInteract, SqsResultSplit)
|
|
@@ -21,12 +24,12 @@ T = TypeVar("T")
|
|
|
21
24
|
|
|
22
25
|
class _SqsResultProxy(Generic[R]):
|
|
23
26
|
def __init__(self, result: R):
|
|
24
|
-
self._result = result
|
|
27
|
+
self._result: R = result
|
|
25
28
|
|
|
26
29
|
def atoms(self) -> Atoms:
|
|
27
30
|
from sqsgenerator import to_ase
|
|
28
31
|
|
|
29
|
-
return to_ase(self._result.structure())
|
|
32
|
+
return to_ase(cast(Any, self._result).structure())
|
|
30
33
|
|
|
31
34
|
def __getattr__(self, item: str) -> Any:
|
|
32
35
|
return getattr(self._result, item)
|
|
@@ -186,24 +189,28 @@ def sqs_structures(
|
|
|
186
189
|
|
|
187
190
|
from sqsgenerator import parse_config
|
|
188
191
|
from sqsgenerator.core import (
|
|
189
|
-
ParseError,
|
|
190
192
|
LogLevel as SqsLogLevel,
|
|
193
|
+
)
|
|
194
|
+
from sqsgenerator.core import (
|
|
195
|
+
ParseError,
|
|
191
196
|
SqsCallbackContext,
|
|
197
|
+
)
|
|
198
|
+
from sqsgenerator.core import (
|
|
192
199
|
optimize as sqs_optimize,
|
|
193
200
|
)
|
|
194
201
|
|
|
195
|
-
config =
|
|
196
|
-
prec
|
|
197
|
-
iteration_mode
|
|
198
|
-
sublattice_mode
|
|
199
|
-
structure
|
|
200
|
-
lattice
|
|
201
|
-
coords
|
|
202
|
-
species
|
|
203
|
-
|
|
204
|
-
iterations
|
|
205
|
-
max_results_per_objective
|
|
206
|
-
|
|
202
|
+
config: dict[str, Any] = {
|
|
203
|
+
"prec": precision,
|
|
204
|
+
"iteration_mode": iteration_mode,
|
|
205
|
+
"sublattice_mode": sublattice_mode,
|
|
206
|
+
"structure": {
|
|
207
|
+
"lattice": structure.cell.array.tolist(),
|
|
208
|
+
"coords": structure.get_scaled_positions().tolist(),
|
|
209
|
+
"species": structure.get_atomic_numbers().tolist(),
|
|
210
|
+
},
|
|
211
|
+
"iterations": iterations,
|
|
212
|
+
"max_results_per_objective": max_results_per_objective,
|
|
213
|
+
}
|
|
207
214
|
if atol is not None:
|
|
208
215
|
config["atol"] = atol
|
|
209
216
|
if rtol is not None:
|
|
@@ -216,7 +223,7 @@ def sqs_structures(
|
|
|
216
223
|
f"Invalid supercell: {supercell}. All dimensions must be positive integers."
|
|
217
224
|
)
|
|
218
225
|
|
|
219
|
-
def _preprocess_for_mode(v: T | list[T] | None) -> list[T] | None:
|
|
226
|
+
def _preprocess_for_mode(v: T | list[T] | None) -> T | list[T] | None:
|
|
220
227
|
match sublattice_mode:
|
|
221
228
|
case "interact":
|
|
222
229
|
return v
|
|
@@ -227,15 +234,20 @@ def sqs_structures(
|
|
|
227
234
|
f"Invalid sublattice mode: {sublattice_mode}. Use 'interact' or 'split'."
|
|
228
235
|
)
|
|
229
236
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if
|
|
235
|
-
config["
|
|
237
|
+
composition_processed = cast(Any, _preprocess_for_mode(composition))
|
|
238
|
+
if composition_processed is not None:
|
|
239
|
+
config["composition"] = composition_processed
|
|
240
|
+
shell_weights_processed = cast(Any, _preprocess_for_mode(shell_weights))
|
|
241
|
+
if shell_weights_processed is not None:
|
|
242
|
+
config["shell_weights"] = shell_weights_processed
|
|
243
|
+
shell_radii_processed = cast(Any, _preprocess_for_mode(shell_radii))
|
|
244
|
+
if shell_radii_processed is not None:
|
|
245
|
+
config["shell_radii"] = shell_radii_processed
|
|
236
246
|
if objective is None:
|
|
237
|
-
objective =
|
|
238
|
-
|
|
247
|
+
objective = (
|
|
248
|
+
0.0 if sublattice_mode == "interact" else [0.0] * len(composition_processed)
|
|
249
|
+
)
|
|
250
|
+
config["target_objective"] = cast(Any, _preprocess_for_mode(objective))
|
|
239
251
|
|
|
240
252
|
if num_threads is not None:
|
|
241
253
|
if num_threads > 0:
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/sqs/_types.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
from typing import Literal, Protocol, TypeAlias, overload
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
from ase import Atoms
|
|
3
|
-
from typing import Literal, TypeAlias, Protocol, overload, Union
|
|
4
5
|
|
|
5
6
|
Shell: TypeAlias = int
|
|
6
7
|
|
|
@@ -124,11 +125,11 @@ Element = Literal[
|
|
|
124
125
|
"Fl",
|
|
125
126
|
]
|
|
126
127
|
|
|
127
|
-
Site =
|
|
128
|
+
Site = str | list[int]
|
|
128
129
|
|
|
129
130
|
Prec = Literal["single", "double"]
|
|
130
131
|
|
|
131
|
-
Composition = dict[Element | Literal["sites"],
|
|
132
|
+
Composition = dict[Element | Literal["sites"], int | Site]
|
|
132
133
|
|
|
133
134
|
ShellWeights = dict[Shell, float]
|
|
134
135
|
|
|
@@ -137,7 +138,7 @@ ShellRadii = list[float]
|
|
|
137
138
|
LogLevel = Literal["warn", "info", "debug", "error", "trace"]
|
|
138
139
|
|
|
139
140
|
|
|
140
|
-
class SroParameter:
|
|
141
|
+
class SroParameter(Protocol):
|
|
141
142
|
@property
|
|
142
143
|
def i(self) -> int: ...
|
|
143
144
|
|
|
@@ -53,8 +53,8 @@ def get_extended_positions(
|
|
|
53
53
|
width /= get_vertical_length(structure=structure, norm_order=norm_order)
|
|
54
54
|
rep = 2 * np.ceil(width).astype(int) * structure.pbc + 1
|
|
55
55
|
rep = [np.arange(r) - int(r / 2) for r in rep]
|
|
56
|
-
|
|
57
|
-
meshgrid = np.stack(
|
|
56
|
+
rep_grids = np.meshgrid(rep[0], rep[1], rep[2])
|
|
57
|
+
meshgrid = np.stack(rep_grids, axis=-1).reshape(-1, 3)
|
|
58
58
|
v_repeated = np.einsum("ni,ij->nj", meshgrid, structure.cell)
|
|
59
59
|
v_repeated = v_repeated[:, np.newaxis, :] + positions[np.newaxis, :, :]
|
|
60
60
|
v_repeated = v_repeated.reshape(-1, 3)
|
|
@@ -184,7 +184,7 @@ def set_indices(structure: Atoms, indices: np.ndarray) -> Atoms:
|
|
|
184
184
|
return structure
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
def get_average_of_unique_labels(labels: np.ndarray, values: np.ndarray) ->
|
|
187
|
+
def get_average_of_unique_labels(labels: np.ndarray, values: np.ndarray) -> np.ndarray:
|
|
188
188
|
"""
|
|
189
189
|
|
|
190
190
|
This function returns the average values of those elements, which share the same labels
|
|
@@ -251,20 +251,20 @@ def apply_strain(
|
|
|
251
251
|
If `lagrangian`, epsilon is given by `(F^T * F - 1) / 2`. It raises an error if
|
|
252
252
|
the strain is not symmetric (if the shear components are given).
|
|
253
253
|
"""
|
|
254
|
-
|
|
255
|
-
if len(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if
|
|
254
|
+
eps: np.ndarray = np.array([epsilon]).flatten()
|
|
255
|
+
if len(eps) == 3 or len(eps) == 1:
|
|
256
|
+
eps = eps * np.eye(3)
|
|
257
|
+
eps = eps.reshape(3, 3)
|
|
258
|
+
if eps.min() < -1.0:
|
|
259
259
|
raise ValueError("Strain value too negative")
|
|
260
260
|
structure_copy = structure.copy() if return_box else structure
|
|
261
261
|
cell = structure_copy.cell.copy()
|
|
262
262
|
if mode == "linear":
|
|
263
|
-
F =
|
|
263
|
+
F = eps + np.eye(3)
|
|
264
264
|
elif mode == "lagrangian":
|
|
265
|
-
if not np.allclose(
|
|
265
|
+
if not np.allclose(eps, eps.T):
|
|
266
266
|
raise ValueError("Strain must be symmetric if `mode = 'lagrangian'`")
|
|
267
|
-
E, V = np.linalg.eigh(2 *
|
|
267
|
+
E, V = np.linalg.eigh(2 * eps + np.eye(3))
|
|
268
268
|
F = np.einsum("ik,k,jk->ij", V, np.sqrt(E), V)
|
|
269
269
|
else:
|
|
270
270
|
raise ValueError("mode must be `linear` or `lagrangian`")
|
|
@@ -153,7 +153,7 @@ def plot3d(
|
|
|
153
153
|
spacefill=spacefill,
|
|
154
154
|
particle_size=particle_size,
|
|
155
155
|
background=background,
|
|
156
|
-
color_scheme=color_scheme,
|
|
156
|
+
color_scheme=color_scheme if color_scheme is not None else "element",
|
|
157
157
|
)
|
|
158
158
|
else:
|
|
159
159
|
raise ValueError("plot method not recognized")
|
|
@@ -453,15 +453,20 @@ def _plot3d(
|
|
|
453
453
|
vector_field is not None and vector_field is not None
|
|
454
454
|
): # WARNING: There must be a bug here...
|
|
455
455
|
try:
|
|
456
|
-
if
|
|
456
|
+
if (
|
|
457
|
+
vector_color is not None
|
|
458
|
+
and vector_color.shape != np.ones((len(structure), 3)).shape
|
|
459
|
+
):
|
|
457
460
|
vector_color = np.outer(
|
|
458
461
|
np.ones(len(structure)),
|
|
459
462
|
vector_color / np.linalg.norm(vector_color),
|
|
460
463
|
)
|
|
461
464
|
except AttributeError:
|
|
462
|
-
vector_color
|
|
465
|
+
if vector_color is not None:
|
|
466
|
+
vector_color = np.ones((len(structure), 3)) * vector_color
|
|
463
467
|
|
|
464
468
|
if vector_field is not None:
|
|
469
|
+
assert vector_color is not None
|
|
465
470
|
for arr, pos, col in zip(vector_field, positions, vector_color, strict=True):
|
|
466
471
|
view.shape.add_arrow(list(pos), list(pos + arr), list(col), 0.2)
|
|
467
472
|
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# file generated by setuptools-scm
|
|
2
|
-
# don't change, don't track in version control
|
|
3
|
-
|
|
4
|
-
__all__ = [
|
|
5
|
-
"__version__",
|
|
6
|
-
"__version_tuple__",
|
|
7
|
-
"version",
|
|
8
|
-
"version_tuple",
|
|
9
|
-
"__commit_id__",
|
|
10
|
-
"commit_id",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
TYPE_CHECKING = False
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from typing import Tuple
|
|
16
|
-
from typing import Union
|
|
17
|
-
|
|
18
|
-
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
-
COMMIT_ID = Union[str, None]
|
|
20
|
-
else:
|
|
21
|
-
VERSION_TUPLE = object
|
|
22
|
-
COMMIT_ID = object
|
|
23
|
-
|
|
24
|
-
version: str
|
|
25
|
-
__version__: str
|
|
26
|
-
__version_tuple__: VERSION_TUPLE
|
|
27
|
-
version_tuple: VERSION_TUPLE
|
|
28
|
-
commit_id: COMMIT_ID
|
|
29
|
-
__commit_id__: COMMIT_ID
|
|
30
|
-
|
|
31
|
-
__version__ = version = '0.0.43'
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0, 43)
|
|
33
|
-
|
|
34
|
-
__commit_id__ = commit_id = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/__init__.py
RENAMED
|
File without changes
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/analyse/distance.py
RENAMED
|
File without changes
|
|
File without changes
|
{structuretoolkit-0.0.43 → structuretoolkit-0.0.45}/src/structuretoolkit/build/sqs/__init__.py
RENAMED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
from ._interface import sqs_structures
|
|
1
2
|
from ._types import (
|
|
2
|
-
|
|
3
|
-
SqsResult,
|
|
4
|
-
SqsResultInteract,
|
|
5
|
-
SublatticeMode,
|
|
6
|
-
IterationMode,
|
|
7
|
-
Site,
|
|
3
|
+
Composition,
|
|
8
4
|
Element,
|
|
5
|
+
IterationMode,
|
|
6
|
+
Prec,
|
|
7
|
+
Shell,
|
|
9
8
|
ShellRadii,
|
|
10
9
|
ShellWeights,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
Site,
|
|
11
|
+
SqsResult,
|
|
12
|
+
SqsResultInteract,
|
|
13
|
+
SqsResultSplit,
|
|
14
14
|
SroParameter,
|
|
15
|
+
SublatticeMode,
|
|
15
16
|
)
|
|
16
|
-
from ._interface import sqs_structures
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
19
19
|
"Composition",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|