structuretoolkit 0.0.26__tar.gz → 0.0.27__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.
Files changed (53) hide show
  1. {structuretoolkit-0.0.26/structuretoolkit.egg-info → structuretoolkit-0.0.27}/PKG-INFO +4 -4
  2. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/pyproject.toml +3 -3
  3. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/_version.py +3 -3
  4. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/pyscal.py +20 -48
  5. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/symmetry.py +99 -0
  6. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/common/pyscal.py +2 -6
  7. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27/structuretoolkit.egg-info}/PKG-INFO +4 -4
  8. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit.egg-info/requires.txt +3 -3
  9. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_pyscal.py +5 -8
  10. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_symmetry.py +62 -8
  11. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/LICENSE +0 -0
  12. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/MANIFEST.in +0 -0
  13. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/README.md +0 -0
  14. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/setup.cfg +0 -0
  15. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/setup.py +0 -0
  16. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/__init__.py +0 -0
  17. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/__init__.py +0 -0
  18. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/distance.py +0 -0
  19. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/dscribe.py +0 -0
  20. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/neighbors.py +0 -0
  21. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/phonopy.py +0 -0
  22. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/snap.py +0 -0
  23. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/spatial.py +0 -0
  24. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/analyse/strain.py +0 -0
  25. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/__init__.py +0 -0
  26. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/aimsgb.py +0 -0
  27. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/compound.py +0 -0
  28. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/mesh.py +0 -0
  29. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/random.py +0 -0
  30. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/sqs.py +0 -0
  31. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/build/surface.py +0 -0
  32. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/common/__init__.py +0 -0
  33. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/common/error.py +0 -0
  34. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/common/helper.py +0 -0
  35. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/common/phonopy.py +0 -0
  36. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/common/pymatgen.py +0 -0
  37. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit/visualize.py +0 -0
  38. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit.egg-info/SOURCES.txt +0 -0
  39. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit.egg-info/dependency_links.txt +0 -0
  40. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/structuretoolkit.egg-info/top_level.txt +0 -0
  41. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_aimsgb.py +0 -0
  42. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_analyse.py +0 -0
  43. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_compound.py +0 -0
  44. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_dscribe.py +0 -0
  45. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_helpers.py +0 -0
  46. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_high_index_surface.py +0 -0
  47. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_mesh.py +0 -0
  48. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_neighbors.py +0 -0
  49. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_pymatgen.py +0 -0
  50. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_pyxtal.py +0 -0
  51. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_snap.py +0 -0
  52. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_strain.py +0 -0
  53. {structuretoolkit-0.0.26 → structuretoolkit-0.0.27}/tests/test_visualize.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: structuretoolkit
3
- Version: 0.0.26
3
+ Version: 0.0.27
4
4
  Summary: build, analyse and visualise atomistic structures for materials science
5
5
  Author-email: Jan Janssen <janssen@mpie.de>
6
6
  License: BSD 3-Clause License
@@ -58,7 +58,7 @@ Provides-Extra: grainboundary
58
58
  Requires-Dist: aimsgb<=1.1.1,>=1.0.2; extra == "grainboundary"
59
59
  Requires-Dist: pymatgen<=2024.6.10,>=2022.2.1; extra == "grainboundary"
60
60
  Provides-Extra: pyscal
61
- Requires-Dist: pyscal2==2.10.18; extra == "pyscal"
61
+ Requires-Dist: pyscal3==3.2.5; extra == "pyscal"
62
62
  Provides-Extra: nglview
63
63
  Requires-Dist: nglview<=3.1.2,>=2.7.7; extra == "nglview"
64
64
  Provides-Extra: matplotlib
@@ -66,14 +66,14 @@ Requires-Dist: matplotlib==3.8.4; extra == "matplotlib"
66
66
  Provides-Extra: plotly
67
67
  Requires-Dist: plotly<=5.22.0,>=4.14.3; extra == "plotly"
68
68
  Provides-Extra: clusters
69
- Requires-Dist: scikit-learn==1.5.0; extra == "clusters"
69
+ Requires-Dist: scikit-learn==1.5.1; extra == "clusters"
70
70
  Provides-Extra: symmetry
71
71
  Requires-Dist: spglib<=2.4.0,>=1.16.5; extra == "symmetry"
72
72
  Provides-Extra: surface
73
73
  Requires-Dist: spglib<=2.4.0,>=1.16.5; extra == "surface"
74
74
  Requires-Dist: pymatgen<=2024.6.10,>=2022.2.1; extra == "surface"
75
75
  Provides-Extra: phonopy
76
- Requires-Dist: phonopy<=2.24.3,>=2.16.2; extra == "phonopy"
76
+ Requires-Dist: phonopy<=2.26.3,>=2.16.2; extra == "phonopy"
77
77
  Requires-Dist: spglib<=2.4.0,>=1.16.5; extra == "phonopy"
78
78
  Provides-Extra: pyxtal
79
79
  Requires-Dist: pyxtal<=0.6.7,>=0.5.5; extra == "pyxtal"
@@ -41,18 +41,18 @@ grainboundary = [
41
41
  "aimsgb>=1.0.2,<=1.1.1",
42
42
  "pymatgen>=2022.2.1,<=2024.6.10",
43
43
  ]
44
- pyscal = ["pyscal2==2.10.18"]
44
+ pyscal = ["pyscal3==3.2.5"]
45
45
  nglview = ["nglview>=2.7.7,<=3.1.2"]
46
46
  matplotlib = ["matplotlib==3.8.4"]
47
47
  plotly = ["plotly>=4.14.3,<=5.22.0"]
48
- clusters = ["scikit-learn==1.5.0"]
48
+ clusters = ["scikit-learn==1.5.1"]
49
49
  symmetry = ["spglib>=1.16.5,<=2.4.0"]
50
50
  surface = [
51
51
  "spglib>=1.16.5,<=2.4.0",
52
52
  "pymatgen>=2022.2.1,<=2024.6.10",
53
53
  ]
54
54
  phonopy = [
55
- "phonopy>=2.16.2,<=2.24.3",
55
+ "phonopy>=2.16.2,<=2.26.3",
56
56
  "spglib>=1.16.5,<=2.4.0",
57
57
  ]
58
58
  pyxtal = ["pyxtal>=0.5.5,<=0.6.7"]
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-06-25T10:24:48+0200",
11
+ "date": "2024-07-10T10:21:34+0200",
12
12
  "dirty": true,
13
13
  "error": null,
14
- "full-revisionid": "ad534b234cdbd638aeea242cefa4077059fd2ed8",
15
- "version": "0.0.26"
14
+ "full-revisionid": "bf6acd3da4ce39b1035efcb21b1f1027cca9d388",
15
+ "version": "0.0.27"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -47,11 +47,8 @@ def get_steinhardt_parameters(
47
47
  sys = ase_to_pyscal(structure)
48
48
  q = (4, 6) if q is None else q
49
49
 
50
- sys.find_neighbors(method=neighbor_method, cutoff=cutoff)
51
-
52
- sys.calculate_q(q, averaged=averaged)
53
-
54
- sysq = np.array(sys.get_qvals(q, averaged=averaged))
50
+ sys.find.neighbors(method=neighbor_method, cutoff=cutoff)
51
+ sysq = np.array(sys.calculate.steinhardt_parameter(q, averaged=averaged))
55
52
 
56
53
  if n_clusters is not None:
57
54
  from sklearn import cluster
@@ -78,7 +75,7 @@ def get_centro_symmetry_descriptors(
78
75
  csm (list) : list of centrosymmetry parameter
79
76
  """
80
77
  sys = ase_to_pyscal(structure)
81
- return np.array(sys.calculate_centrosymmetry(nmax=num_neighbors))
78
+ return np.array(sys.calculate.centrosymmetry(nmax=num_neighbors))
82
79
 
83
80
 
84
81
  def get_diamond_structure_descriptors(
@@ -101,23 +98,19 @@ def get_diamond_structure_descriptors(
101
98
  (depends on `mode`)
102
99
  """
103
100
  sys = ase_to_pyscal(structure)
104
- diamond_dict = sys.identify_diamond()
101
+ diamond_dict = sys.analyze.diamond_structure()
105
102
 
106
103
  ovito_identifiers = [
104
+ "Other",
107
105
  "Cubic diamond",
108
106
  "Cubic diamond (1st neighbor)",
109
107
  "Cubic diamond (2nd neighbor)",
110
108
  "Hexagonal diamond",
111
109
  "Hexagonal diamond (1st neighbor)",
112
110
  "Hexagonal diamond (2nd neighbor)",
113
- "Other",
114
111
  ]
115
112
  pyscal_identifiers = [
116
113
  "others",
117
- "fcc",
118
- "hcp",
119
- "bcc",
120
- "ico",
121
114
  "cubic diamond",
122
115
  "cubic diamond 1NN",
123
116
  "cubic diamond 2NN",
@@ -125,19 +118,6 @@ def get_diamond_structure_descriptors(
125
118
  "hex diamond 1NN",
126
119
  "hex diamond 2NN",
127
120
  ]
128
- convert_to_ovito = {
129
- 0: 6,
130
- 1: 6,
131
- 2: 6,
132
- 3: 6,
133
- 4: 6,
134
- 5: 0,
135
- 6: 1,
136
- 7: 2,
137
- 8: 3,
138
- 9: 4,
139
- 10: 5,
140
- }
141
121
 
142
122
  if mode == "total":
143
123
  if not ovito_compatibility:
@@ -158,26 +138,22 @@ def get_diamond_structure_descriptors(
158
138
  "IdentifyDiamond.counts.HEX_DIAMOND_SECOND_NEIGHBOR": diamond_dict[
159
139
  "hex diamond 2NN"
160
140
  ],
161
- "IdentifyDiamond.counts.OTHER": diamond_dict["others"]
162
- + diamond_dict["fcc"]
163
- + diamond_dict["hcp"]
164
- + diamond_dict["bcc"]
165
- + diamond_dict["ico"],
141
+ "IdentifyDiamond.counts.OTHER": diamond_dict["others"],
166
142
  }
167
143
  elif mode == "numeric":
168
144
  if not ovito_compatibility:
169
- return np.array([atom.structure for atom in sys.atoms])
145
+ return np.array(sys.atoms.structure)
170
146
  else:
171
- return np.array([convert_to_ovito[atom.structure] for atom in sys.atoms])
147
+ return np.array([6 if x == 0 else x - 1 for x in sys.atoms.structure])
148
+
172
149
  elif mode == "str":
173
150
  if not ovito_compatibility:
174
- return np.array([pyscal_identifiers[atom.structure] for atom in sys.atoms])
151
+ return np.array(
152
+ [pyscal_identifiers[structure] for structure in sys.atoms.structure]
153
+ )
175
154
  else:
176
155
  return np.array(
177
- [
178
- ovito_identifiers[convert_to_ovito[atom.structure]]
179
- for atom in sys.atoms
180
- ]
156
+ [ovito_identifiers[structure] for structure in sys.atoms.structure]
181
157
  )
182
158
  else:
183
159
  raise ValueError(
@@ -217,7 +193,7 @@ def get_adaptive_cna_descriptors(
217
193
  "CommonNeighborAnalysis.counts.ICO",
218
194
  ]
219
195
 
220
- cna = sys.calculate_cna()
196
+ cna = sys.analyze.common_neighbor_analysis()
221
197
 
222
198
  if mode == "total":
223
199
  if not ovito_compatibility:
@@ -225,8 +201,7 @@ def get_adaptive_cna_descriptors(
225
201
  else:
226
202
  return {o: cna[p] for o, p in zip(ovito_parameter, pyscal_parameter)}
227
203
  else:
228
- structure = sys.atoms
229
- cnalist = np.array([atom.structure for atom in structure])
204
+ cnalist = np.array(sys.atoms.structure)
230
205
  if mode == "numeric":
231
206
  return cnalist
232
207
  elif mode == "str":
@@ -250,9 +225,8 @@ def get_voronoi_volumes(structure: Atoms) -> np.ndarray:
250
225
  structure : (ase.atoms.Atoms): The structure to analyze.
251
226
  """
252
227
  sys = ase_to_pyscal(structure)
253
- sys.find_neighbors(method="voronoi")
254
- structure = sys.atoms
255
- return np.array([atom.volume for atom in structure])
228
+ sys.find.neighbors(method="voronoi")
229
+ return np.array(sys.atoms.voronoi.volume)
256
230
 
257
231
 
258
232
  def find_solids(
@@ -287,8 +261,8 @@ def find_solids(
287
261
  pyscal system: pyscal system when return_sys=True
288
262
  """
289
263
  sys = ase_to_pyscal(structure)
290
- sys.find_neighbors(method=neighbor_method, cutoff=cutoff)
291
- sys.find_solids(
264
+ sys.find.neighbors(method=neighbor_method, cutoff=cutoff)
265
+ sys.find.solids(
292
266
  bonds=bonds,
293
267
  threshold=threshold,
294
268
  avgthreshold=avgthreshold,
@@ -299,6 +273,4 @@ def find_solids(
299
273
  )
300
274
  if return_sys:
301
275
  return sys
302
- structure = sys.atoms
303
- solids = [atom for atom in structure if atom.solid]
304
- return len(solids)
276
+ return np.sum(sys.atoms.solid)
@@ -9,6 +9,8 @@ import numpy as np
9
9
  import spglib
10
10
  from scipy.spatial import cKDTree
11
11
  from typing import Optional
12
+ import string
13
+ from functools import cached_property
12
14
 
13
15
  import structuretoolkit.common.helper
14
16
  from structuretoolkit.common.error import SymmetryError
@@ -230,6 +232,33 @@ class Symmetry(dict):
230
232
  np.einsum("ijk->jki", v_reshaped)[self.permutations],
231
233
  ).reshape(np.shape(vectors)) / len(self["rotations"])
232
234
 
235
+ def symmetrize_tensor(self, tensor: np.ndarray) -> np.ndarray:
236
+ """
237
+ Symmetrization of any tensor. The tensor is defined by a matrix with a
238
+ shape of `n * (n_atoms, 3)`. For example, if the structure has 100
239
+ atoms, the vector can have a shape of (100, 3), (100, 3, 100, 3),
240
+ (100, 3, 100, 3, 100, 3) etc. Additionally, you can also have an array
241
+ of tensors, i.e. in this example you can have a shape like (4, 100, 3)
242
+ or (2, 100, 3, 100, 3). When the shape is (n, n_atoms, 3), the function
243
+ works in the same way as `symmetrize_vectors`, which might be somewhat
244
+ faster.
245
+
246
+ This function can be useful for the symmetrization of Hessian tensors,
247
+ or any other tensors which should be symmetric.
248
+
249
+ Args:
250
+ tensors (ndarray): n * (n_atoms, 3) tensor to symmetrize
251
+
252
+ Returns
253
+ (np.ndarray) symmetrized tensor of the same shape
254
+ """
255
+ return _SymmetrizeTensor(
256
+ tensor=tensor,
257
+ structure=self._structure,
258
+ rotations=self.rotations,
259
+ permutations=self.permutations,
260
+ ).result
261
+
233
262
  def _get_spglib_cell(
234
263
  self, use_elements: Optional[bool] = None, use_magmoms: Optional[bool] = None
235
264
  ) -> tuple:
@@ -389,3 +418,73 @@ class Symmetry(dict):
389
418
  if mesh is None:
390
419
  raise SymmetryError(spglib.spglib.spglib_error.message)
391
420
  return mesh
421
+
422
+
423
+ class _SymmetrizeTensor:
424
+ def __init__(self, tensor, structure, rotations, permutations):
425
+ self._tensor = np.array(tensor)
426
+ self._structure = structure
427
+ self._rotations = rotations
428
+ self._permutations = permutations
429
+
430
+ @cached_property
431
+ def order(self):
432
+ order = len(self._tensor.shape) // 2
433
+ if self._tensor.shape[-2 * order :] != order * self._structure.positions.shape:
434
+ raise ValueError(
435
+ "Tensor must have a shape of a multiple of (n_atoms, 3). See"
436
+ " docstring for more info"
437
+ )
438
+ return order
439
+
440
+ @cached_property
441
+ def ij(self):
442
+ return string.ascii_lowercase[: 2 * self.order]
443
+
444
+ @property
445
+ def IJ(self):
446
+ return self.ij.upper()
447
+
448
+ @property
449
+ def ij_reorder(self):
450
+ return "".join(
451
+ [self.ij[ii] for ii in np.arange(2 * self.order).reshape(-1, 2).T.flatten()]
452
+ )
453
+
454
+ @property
455
+ def IJ_reorder(self):
456
+ return "".join(
457
+ [self.IJ[ii] for ii in np.arange(2 * self.order).reshape(2, -1).T.flatten()]
458
+ )
459
+
460
+ @cached_property
461
+ def t_t(self):
462
+ return np.einsum("...{}->{}...".format(self.ij, self.ij_reorder), self._tensor)
463
+
464
+ @cached_property
465
+ def str_einsum(self):
466
+ return (
467
+ ",".join(
468
+ [I + i for i, I in zip(self.ij[-self.order :], self.IJ[-self.order :])]
469
+ )
470
+ + ","
471
+ + self.IJ[: self.order]
472
+ + self.ij[self.order :]
473
+ + "...->..."
474
+ + self.IJ_reorder
475
+ )
476
+
477
+ @property
478
+ def result(self):
479
+ return np.mean(
480
+ [
481
+ np.einsum(
482
+ self.str_einsum,
483
+ *self.order * (rot,),
484
+ self.t_t[tuple(np.meshgrid(*self.order * (perm,), indexing="ij"))],
485
+ optimize=True,
486
+ )
487
+ for rot, perm in zip(self._rotations, self._permutations)
488
+ ],
489
+ axis=0,
490
+ )
@@ -12,11 +12,7 @@ def ase_to_pyscal(structure: Atoms):
12
12
  Returns:
13
13
  Pyscal system: See the pyscal documentation.
14
14
  """
15
- import pyscal.core as pc
15
+ import pyscal3 as pc
16
16
 
17
- sys = pc.System()
18
- sys.read_inputfile(
19
- filename=structure,
20
- format="ase",
21
- )
17
+ sys = pc.System(structure, format="ase")
22
18
  return sys
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: structuretoolkit
3
- Version: 0.0.26
3
+ Version: 0.0.27
4
4
  Summary: build, analyse and visualise atomistic structures for materials science
5
5
  Author-email: Jan Janssen <janssen@mpie.de>
6
6
  License: BSD 3-Clause License
@@ -58,7 +58,7 @@ Provides-Extra: grainboundary
58
58
  Requires-Dist: aimsgb<=1.1.1,>=1.0.2; extra == "grainboundary"
59
59
  Requires-Dist: pymatgen<=2024.6.10,>=2022.2.1; extra == "grainboundary"
60
60
  Provides-Extra: pyscal
61
- Requires-Dist: pyscal2==2.10.18; extra == "pyscal"
61
+ Requires-Dist: pyscal3==3.2.5; extra == "pyscal"
62
62
  Provides-Extra: nglview
63
63
  Requires-Dist: nglview<=3.1.2,>=2.7.7; extra == "nglview"
64
64
  Provides-Extra: matplotlib
@@ -66,14 +66,14 @@ Requires-Dist: matplotlib==3.8.4; extra == "matplotlib"
66
66
  Provides-Extra: plotly
67
67
  Requires-Dist: plotly<=5.22.0,>=4.14.3; extra == "plotly"
68
68
  Provides-Extra: clusters
69
- Requires-Dist: scikit-learn==1.5.0; extra == "clusters"
69
+ Requires-Dist: scikit-learn==1.5.1; extra == "clusters"
70
70
  Provides-Extra: symmetry
71
71
  Requires-Dist: spglib<=2.4.0,>=1.16.5; extra == "symmetry"
72
72
  Provides-Extra: surface
73
73
  Requires-Dist: spglib<=2.4.0,>=1.16.5; extra == "surface"
74
74
  Requires-Dist: pymatgen<=2024.6.10,>=2022.2.1; extra == "surface"
75
75
  Provides-Extra: phonopy
76
- Requires-Dist: phonopy<=2.24.3,>=2.16.2; extra == "phonopy"
76
+ Requires-Dist: phonopy<=2.26.3,>=2.16.2; extra == "phonopy"
77
77
  Requires-Dist: spglib<=2.4.0,>=1.16.5; extra == "phonopy"
78
78
  Provides-Extra: pyxtal
79
79
  Requires-Dist: pyxtal<=0.6.7,>=0.5.5; extra == "pyxtal"
@@ -3,7 +3,7 @@ numpy<=1.26.4,>=1.23.5
3
3
  scipy<=1.14.0,>=1.9.3
4
4
 
5
5
  [clusters]
6
- scikit-learn==1.5.0
6
+ scikit-learn==1.5.1
7
7
 
8
8
  [dscribe]
9
9
  dscribe==2.1.1
@@ -19,14 +19,14 @@ matplotlib==3.8.4
19
19
  nglview<=3.1.2,>=2.7.7
20
20
 
21
21
  [phonopy]
22
- phonopy<=2.24.3,>=2.16.2
22
+ phonopy<=2.26.3,>=2.16.2
23
23
  spglib<=2.4.0,>=1.16.5
24
24
 
25
25
  [plotly]
26
26
  plotly<=5.22.0,>=4.14.3
27
27
 
28
28
  [pyscal]
29
- pyscal2==2.10.18
29
+ pyscal3==3.2.5
30
30
 
31
31
  [pyxtal]
32
32
  pyxtal<=0.6.7,>=0.5.5
@@ -9,7 +9,7 @@ from ase.atoms import Atoms
9
9
  import structuretoolkit as stk
10
10
 
11
11
  try:
12
- import pyscal
12
+ import pyscal3 as pyscal
13
13
 
14
14
  skip_pyscal_test = False
15
15
  except ImportError:
@@ -17,7 +17,7 @@ except ImportError:
17
17
 
18
18
 
19
19
  @unittest.skipIf(
20
- skip_pyscal_test, "pyscal is not installed, so the pyscal tests are skipped."
20
+ skip_pyscal_test, "pyscal3 is not installed, so the pyscal3 tests are skipped."
21
21
  )
22
22
  class Testpyscal(unittest.TestCase):
23
23
  @classmethod
@@ -394,10 +394,6 @@ class Testpyscalatoms(unittest.TestCase):
394
394
  def test_analyse_pyscal_diamond_structure(self):
395
395
  pyscal_keys = [
396
396
  "others",
397
- "fcc",
398
- "hcp",
399
- "bcc",
400
- "ico",
401
397
  "cubic diamond",
402
398
  "cubic diamond 1NN",
403
399
  "cubic diamond 2NN",
@@ -438,10 +434,11 @@ class Testpyscalatoms(unittest.TestCase):
438
434
  res_dict_total = stk.analyse.get_diamond_structure_descriptors(
439
435
  structure=self.si_dia, mode="total", ovito_compatibility=False
440
436
  )
437
+
441
438
  self.assertEqual(
442
439
  sum([k in res_dict_total.keys() for k in pyscal_keys]), len(pyscal_keys)
443
440
  )
444
- self.assertEqual(res_dict_total[pyscal_keys[5]], len(self.si_dia))
441
+ self.assertEqual(res_dict_total[pyscal_keys[1]], len(self.si_dia))
445
442
 
446
443
  res_numeric = stk.analyse.get_diamond_structure_descriptors(
447
444
  structure=self.al_fcc, mode="numeric", ovito_compatibility=False
@@ -462,7 +459,7 @@ class Testpyscalatoms(unittest.TestCase):
462
459
  structure=self.si_dia, mode="numeric", ovito_compatibility=False
463
460
  )
464
461
  self.assertEqual(len(res_numeric), len(self.si_dia))
465
- self.assertTrue(all([v == 5 for v in res_numeric]))
462
+ self.assertTrue(all([v == 1 for v in res_numeric]))
466
463
 
467
464
  res_str = stk.analyse.get_diamond_structure_descriptors(
468
465
  structure=self.al_fcc, mode="str", ovito_compatibility=False
@@ -18,6 +18,7 @@ except ImportError:
18
18
 
19
19
  try:
20
20
  import spglib
21
+ from structuretoolkit.analyse.symmetry import _SymmetrizeTensor
21
22
 
22
23
  skip_spglib_test = False
23
24
  except ImportError:
@@ -108,20 +109,22 @@ class TestAtoms(unittest.TestCase):
108
109
  "AlAl", scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)], cell=cell, pbc=True
109
110
  )
110
111
  v = np.random.rand(6).reshape(-1, 3)
112
+ sym = stk.analyse.get_symmetry(structure=Al)
111
113
  self.assertAlmostEqual(
112
- np.linalg.norm(
113
- stk.analyse.get_symmetry(structure=Al).symmetrize_vectors(v)
114
- ),
114
+ np.linalg.norm(sym.symmetrize_vectors(v)),
115
115
  0,
116
116
  )
117
117
  vv = np.random.rand(12).reshape(2, 2, 3)
118
- for vvv in stk.analyse.get_symmetry(structure=Al).symmetrize_vectors(vv):
118
+ for vvv in sym.symmetrize_vectors(vv):
119
119
  self.assertAlmostEqual(np.linalg.norm(vvv), 0)
120
120
  Al.positions[0, 0] += 0.01
121
- w = stk.analyse.get_symmetry(structure=Al).symmetrize_vectors(v)
121
+ w = sym.symmetrize_vectors(v)
122
122
  self.assertAlmostEqual(
123
123
  np.absolute(w[:, 0]).sum(), np.linalg.norm(w, axis=-1).sum()
124
124
  )
125
+ self.assertAlmostEqual(
126
+ np.linalg.norm(sym.symmetrize_vectors(v) - sym.symmetrize_tensor(v)), 0
127
+ )
125
128
 
126
129
  def test_get_symmetry_dataset(self):
127
130
  cell = 2.2 * np.identity(3)
@@ -155,7 +158,7 @@ class TestAtoms(unittest.TestCase):
155
158
  )
156
159
 
157
160
  def test_get_primitive_cell_hex(self):
158
- elements = ['Fe', 'Fe', 'Fe', 'Fe', 'O', 'O', 'O', 'O', 'O', 'O']
161
+ elements = ["Fe", "Fe", "Fe", "Fe", "O", "O", "O", "O", "O", "O"]
159
162
  positions = [
160
163
  [0.0, 0.0, 4.89],
161
164
  [0.0, 0.0, 11.78],
@@ -174,8 +177,7 @@ class TestAtoms(unittest.TestCase):
174
177
  sym = stk.analyse.get_symmetry(structure=structure_repeat)
175
178
  structure_prim_base = sym.get_primitive_cell()
176
179
  self.assertEqual(
177
- structure_prim_base.get_chemical_symbols(),
178
- structure.get_chemical_symbols()
180
+ structure_prim_base.get_chemical_symbols(), structure.get_chemical_symbols()
179
181
  )
180
182
 
181
183
  def test_get_equivalent_points(self):
@@ -284,5 +286,57 @@ class TestAtoms(unittest.TestCase):
284
286
  stk.analyse.get_symmetry(structure=structure)
285
287
 
286
288
 
289
+ @unittest.skipIf(
290
+ skip_spglib_test, "spglib is not installed, so the spglib tests are skipped."
291
+ )
292
+ class TestSymmetrizeTensors(unittest.TestCase):
293
+ @classmethod
294
+ def setUpClass(cls):
295
+ cls.structure = bulk("Al", cubic=True, a=4.0).repeat(2)
296
+ cls.dataset = {
297
+ "structure": cls.structure,
298
+ "rotations": np.eye(3),
299
+ "permutations": np.arange(len(cls.structure)),
300
+ }
301
+
302
+ def test_order(self):
303
+ with self.assertRaises(ValueError):
304
+ _SymmetrizeTensor(
305
+ tensor=np.array([1]), **self.dataset
306
+ ).order
307
+ self.assertEqual(
308
+ _SymmetrizeTensor(
309
+ tensor=np.random.randn(*self.structure.positions.shape), **self.dataset
310
+ ).order,
311
+ 1,
312
+ )
313
+ self.assertEqual(
314
+ _SymmetrizeTensor(
315
+ tensor=np.random.randn(*2 * self.structure.positions.shape),
316
+ **self.dataset,
317
+ ).order,
318
+ 2,
319
+ )
320
+
321
+ def test_indexing(self):
322
+ st = _SymmetrizeTensor(
323
+ tensor=np.random.randn(*2 * self.structure.positions.shape), **self.dataset
324
+ )
325
+ self.assertEqual(st.ij, "abcd")
326
+ self.assertEqual(st.ij_reorder, "acbd")
327
+ self.assertEqual(st.IJ, "ABCD")
328
+ self.assertEqual(st.IJ_reorder, "ACBD")
329
+
330
+ def test_str_einsum(self):
331
+ st = _SymmetrizeTensor(
332
+ tensor=np.random.randn(*2 * self.structure.positions.shape), **self.dataset
333
+ )
334
+ self.assertEqual(st.str_einsum, "Cc,Dd,ABcd...->...ACBD")
335
+ st = _SymmetrizeTensor(
336
+ tensor=np.random.randn(*self.structure.positions.shape), **self.dataset
337
+ )
338
+ self.assertEqual(st.str_einsum, "Bb,Ab...->...AB")
339
+
340
+
287
341
  if __name__ == "__main__":
288
342
  unittest.main()