matid 2.0.0.dev1__tar.gz → 2.0.1__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 (52) hide show
  1. {matid-2.0.0.dev1 → matid-2.0.1}/MANIFEST.in +1 -0
  2. {matid-2.0.0.dev1/matid.egg-info → matid-2.0.1}/PKG-INFO +11 -7
  3. {matid-2.0.0.dev1 → matid-2.0.1}/README.md +2 -2
  4. {matid-2.0.0.dev1 → matid-2.0.1}/matid/classification/classifier.py +2 -2
  5. {matid-2.0.0.dev1 → matid-2.0.1}/matid/clustering/sbc.py +154 -156
  6. {matid-2.0.0.dev1 → matid-2.0.1}/matid/core/linkedunits.py +1 -2
  7. {matid-2.0.0.dev1 → matid-2.0.1}/matid/core/periodicfinder.py +27 -27
  8. {matid-2.0.0.dev1 → matid-2.0.1}/matid/data/element_data.py +1 -0
  9. matid-2.0.1/matid/ext/celllist.h +106 -0
  10. matid-2.0.1/matid/ext/geometry.h +89 -0
  11. {matid-2.0.0.dev1 → matid-2.0.1}/matid/geometry/geometry.py +3 -2
  12. {matid-2.0.0.dev1 → matid-2.0.1}/matid/symmetry/symmetryanalyzer.py +3 -3
  13. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/segfault_protect.py +1 -0
  14. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/symmetry_info_generation/generate_symmetry_info.py +1 -0
  15. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/symmetry_info_generation/query_continuous_translations.py +1 -0
  16. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/symmetry_info_generation/query_conventional_transform_info.py +1 -0
  17. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/symmetry_info_generation/query_normalizers.py +1 -0
  18. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/symmetry_info_generation/query_space_group_info.py +1 -0
  19. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/symmetry_info_generation/query_wyckoff_sets.py +1 -0
  20. {matid-2.0.0.dev1 → matid-2.0.1/matid.egg-info}/PKG-INFO +11 -7
  21. {matid-2.0.0.dev1 → matid-2.0.1}/matid.egg-info/SOURCES.txt +2 -0
  22. {matid-2.0.0.dev1 → matid-2.0.1}/matid.egg-info/requires.txt +2 -1
  23. matid-2.0.1/pyproject.toml +93 -0
  24. matid-2.0.0.dev1/pyproject.toml +0 -48
  25. {matid-2.0.0.dev1 → matid-2.0.1}/LICENSE +0 -0
  26. {matid-2.0.0.dev1 → matid-2.0.1}/matid/__init__.py +0 -0
  27. {matid-2.0.0.dev1 → matid-2.0.1}/matid/classification/__init__.py +0 -0
  28. {matid-2.0.0.dev1 → matid-2.0.1}/matid/classification/classifications.py +0 -0
  29. {matid-2.0.0.dev1 → matid-2.0.1}/matid/clustering/__init__.py +0 -0
  30. {matid-2.0.0.dev1 → matid-2.0.1}/matid/clustering/cluster.py +0 -0
  31. {matid-2.0.0.dev1 → matid-2.0.1}/matid/core/__init__.py +0 -0
  32. {matid-2.0.0.dev1 → matid-2.0.1}/matid/core/distances.py +0 -0
  33. {matid-2.0.0.dev1 → matid-2.0.1}/matid/core/lattice.py +0 -0
  34. {matid-2.0.0.dev1 → matid-2.0.1}/matid/core/system.py +0 -0
  35. {matid-2.0.0.dev1 → matid-2.0.1}/matid/data/__init__.py +0 -0
  36. {matid-2.0.0.dev1 → matid-2.0.1}/matid/data/alphabet_data.py +0 -0
  37. {matid-2.0.0.dev1 → matid-2.0.1}/matid/data/constants.py +0 -0
  38. {matid-2.0.0.dev1 → matid-2.0.1}/matid/data/symmetry_data.py +0 -0
  39. {matid-2.0.0.dev1 → matid-2.0.1}/matid/ext/celllist.cpp +0 -0
  40. {matid-2.0.0.dev1 → matid-2.0.1}/matid/ext/ext.cpp +0 -0
  41. {matid-2.0.0.dev1 → matid-2.0.1}/matid/ext/geometry.cpp +0 -0
  42. {matid-2.0.0.dev1 → matid-2.0.1}/matid/geometry/__init__.py +0 -0
  43. {matid-2.0.0.dev1 → matid-2.0.1}/matid/symmetry/__init__.py +0 -0
  44. {matid-2.0.0.dev1 → matid-2.0.1}/matid/symmetry/wyckoffset.py +0 -0
  45. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/__init__.py +0 -0
  46. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/exceptions.py +0 -0
  47. {matid-2.0.0.dev1 → matid-2.0.1}/matid/utils/surfacegenerator.py +0 -0
  48. {matid-2.0.0.dev1 → matid-2.0.1}/matid.egg-info/dependency_links.txt +0 -0
  49. {matid-2.0.0.dev1 → matid-2.0.1}/matid.egg-info/top_level.txt +0 -0
  50. {matid-2.0.0.dev1 → matid-2.0.1}/requirements.txt +0 -0
  51. {matid-2.0.0.dev1 → matid-2.0.1}/setup.cfg +0 -0
  52. {matid-2.0.0.dev1 → matid-2.0.1}/setup.py +0 -0
@@ -1,3 +1,4 @@
1
1
  include LICENSE
2
2
  include requirements.txt
3
+ recursive-include matid/ext *.h
3
4
  recursive-exclude tests *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: matid
3
- Version: 2.0.0.dev1
3
+ Version: 2.0.1
4
4
  Summary: MatID is a Python package for identifying and analyzing atomistic systems based on their structure.
5
5
  Author: Lauri Himanen
6
6
  License: Apache License
@@ -214,10 +214,13 @@ Classifier: Topic :: Scientific/Engineering :: Physics
214
214
  Classifier: License :: OSI Approved :: Apache Software License
215
215
  Classifier: Programming Language :: Python
216
216
  Classifier: Programming Language :: Python :: 3
217
- Classifier: Programming Language :: Python :: 3.7
218
- Classifier: Programming Language :: Python :: 3.8
219
217
  Classifier: Programming Language :: Python :: 3 :: Only
220
- Requires-Python: >=3.7
218
+ Classifier: Programming Language :: Python :: 3.8
219
+ Classifier: Programming Language :: Python :: 3.9
220
+ Classifier: Programming Language :: Python :: 3.10
221
+ Classifier: Programming Language :: Python :: 3.11
222
+ Classifier: Programming Language :: Python :: 3.12
223
+ Requires-Python: >=3.8
221
224
  Description-Content-Type: text/markdown
222
225
  License-File: LICENSE
223
226
  Requires-Dist: numpy
@@ -229,14 +232,15 @@ Requires-Dist: networkx>=2.4
229
232
  Provides-Extra: dev
230
233
  Requires-Dist: coverage; extra == "dev"
231
234
  Requires-Dist: pytest; extra == "dev"
232
- Requires-Dist: black; extra == "dev"
235
+ Requires-Dist: ruff; extra == "dev"
233
236
  Requires-Dist: pydoc-markdown; extra == "dev"
237
+ Requires-Dist: genbadge[coverage]; extra == "dev"
234
238
 
235
239
  <img src="https://raw.githubusercontent.com/nomad-coe/matid/main/docs/static/img/logo.png" width="300">
236
240
 
237
241
  ![Build status](https://github.com/nomad-coe/matid/actions/workflows/test.yml/badge.svg)
238
- [![Coverage Status](https://coveralls.io/repos/github/nomad-coe/matid/badge.svg?branch=main)](https://coveralls.io/github/nomad-coe/matid?branch=main)
239
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
242
+ ![Coverage Status](./reports/coverage/coverage-badge.svg)
243
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
240
244
 
241
245
  MatID is a Python package for identifying and analyzing atomistic systems based
242
246
  on their structure.
@@ -1,8 +1,8 @@
1
1
  <img src="https://raw.githubusercontent.com/nomad-coe/matid/main/docs/static/img/logo.png" width="300">
2
2
 
3
3
  ![Build status](https://github.com/nomad-coe/matid/actions/workflows/test.yml/badge.svg)
4
- [![Coverage Status](https://coveralls.io/repos/github/nomad-coe/matid/badge.svg?branch=main)](https://coveralls.io/github/nomad-coe/matid?branch=main)
5
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
4
+ ![Coverage Status](./reports/coverage/coverage-badge.svg)
5
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
6
6
 
7
7
  MatID is a Python package for identifying and analyzing atomistic systems based
8
8
  on their structure.
@@ -127,7 +127,7 @@ class Classifier:
127
127
  self.min_coverage = min_coverage
128
128
 
129
129
  # Check seed position
130
- if type(seed_position) == str:
130
+ if isinstance(seed_position, str):
131
131
  if seed_position == "cm":
132
132
  pass
133
133
  else:
@@ -251,7 +251,7 @@ class Classifier:
251
251
  if len(elems) == 0:
252
252
  break
253
253
  else:
254
- if type(self.seed_position) == int:
254
+ if isinstance(self.seed_position, int):
255
255
  seed_indices = [self.seed_position]
256
256
  elif isinstance(self.seed_position, (tuple, list, np.ndarray)):
257
257
  seed_indices = self.seed_position
@@ -19,13 +19,166 @@ class SBC:
19
19
  will be returned as isolated clusters.
20
20
  """
21
21
 
22
- def __init__(self, seed=7):
22
+ def get_clusters(
23
+ self,
24
+ system,
25
+ angle_tol=20,
26
+ max_cell_size=6,
27
+ pos_tol=0.7,
28
+ merge_threshold=0.5,
29
+ merge_radius=1,
30
+ bond_threshold=0.65,
31
+ overlap_threshold=-0.1,
32
+ radii="covalent",
33
+ seed=7,
34
+ ):
23
35
  """
36
+ Used to detect and return structurally separate clusters within the
37
+ given system.
38
+
24
39
  Args:
40
+ system (ase.Atoms): The structure to partition.
41
+ angle_tol (float): angle_tol parameter for PeriodicFinder
42
+ max_cell_size (float): max_cell_size parameter for PeriodicFinder.get_region
43
+ pos_tol (float): pos_tol parameter for PeriodicFinder.get_region
44
+ merge_threshold (float): A threshold for merging two clusters
45
+ together. Give as a fraction of shared atoms. Value of 1 would
46
+ mean that clusters are never merged, value of 0 means that they
47
+ are merged always when at least one atom is shared.
48
+ merge_radius (float): Radius for finding nearby atoms when deciding
49
+ which cluster is closest. The atomic radii are subtracted from
50
+ distances. Given in angstroms.
51
+ bond_threshold(float): Used to control the connectivity threshold
52
+ for defining a chemical connection between atoms. Controls e.g.
53
+ what types of unit cells are accepted and how outliers are
54
+ removed from clusters.
55
+ overlap_threshold(float): Used to exclude non-physical cells by
56
+ checking overlap of atoms. Overlap between two atoms is
57
+ calculated by subtracting atomic radii from the distance between
58
+ the atoms.
59
+ radii(str|np.ndarray): The radii to use for atoms. Use either a preset
60
+ or a custom list of atomic radii for each atom. The available presets are:
61
+
62
+ - covalent: Covalent radii from DOI:10.1039/B801115J
63
+ - vdw: van Der Waals radii from DOI:10.1039/C3DT50599E
64
+ - vdw_covalent: preferably van Der Waals radii, covalent if vdw
65
+ not defined.
25
66
  seed(int): The seed that is used for random number generation.
67
+
68
+ Returns:
69
+ A list of Clusters.
26
70
  """
71
+ # Create a random number gen with custom seed
27
72
  self.rng = np.random.default_rng(seed)
28
73
 
74
+ # Copy the system to avoid mutating the original
75
+ system_copy = system.copy()
76
+
77
+ # Here we ensure that the system has three valid basis vectors which
78
+ # when forming a unit cell contain all of the atoms in the system. This
79
+ # is to ensure that the code that requires scaled positions works
80
+ # correctly.
81
+ pbc = system_copy.get_pbc()
82
+ basis = system_copy.get_cell()
83
+ requires_completion = False
84
+ for i in range(3):
85
+ if not basis[i, :].any():
86
+ if not pbc[i]:
87
+ requires_completion = True
88
+ else:
89
+ raise ValueError(
90
+ "Cannot process system with zero-volume cell and periodic boundaries."
91
+ )
92
+ if requires_completion:
93
+ system_copy.set_cell(ase.geometry.complete_cell(basis))
94
+ if not all(pbc):
95
+ scaled_positions = system_copy.get_scaled_positions()
96
+ new_cell = system_copy.get_cell()
97
+ scale_cell = False
98
+ for i in range(3):
99
+ if not pbc[i]:
100
+ i_pos = scaled_positions[:, i]
101
+ max_pos = i_pos.max()
102
+ min_pos = i_pos.min()
103
+ if max_pos > 1 or min_pos < 0:
104
+ scale_cell = True
105
+ new_cell[i, :] *= (max_pos - min_pos) + 1
106
+ if scale_cell:
107
+ system_copy.set_cell(new_cell)
108
+ system_copy.center()
109
+
110
+ # Positions are wrapped
111
+ system_copy.wrap()
112
+
113
+ atomic_numbers = system.get_atomic_numbers()
114
+ radii = matid.geometry.get_radii(radii, atomic_numbers)
115
+
116
+ # Calculate the distances here once if they have not been provided.
117
+ distances = matid.geometry.get_distances(system_copy, radii)
118
+
119
+ # Iteratively search for new clusters until whole system is covered
120
+ periodic_finder = PeriodicFinder(
121
+ angle_tol=angle_tol, chem_similarity_threshold=0
122
+ )
123
+ indices = set(list(range(len(system_copy))))
124
+ clusters = []
125
+ while len(indices) != 0:
126
+ i_seed = self.rng.choice(list(indices), 1)[0]
127
+ i_grain, mask = periodic_finder.get_region(
128
+ system_copy,
129
+ seed_index=i_seed,
130
+ max_cell_size=max_cell_size,
131
+ pos_tol=pos_tol,
132
+ bond_threshold=bond_threshold,
133
+ overlap_threshold=overlap_threshold,
134
+ distances=distances,
135
+ return_mask=True,
136
+ )
137
+
138
+ # All neighbours that the periodic finder has tested are removed
139
+ # from the search. This significantly helps with the scaling of the
140
+ # clustering.
141
+ tested_indices = set(np.arange(len(mask))[mask])
142
+ indices -= tested_indices
143
+
144
+ # If a grain is found, it is added as a single cluster and removed
145
+ # from the search
146
+ if i_grain is not None:
147
+ i_indices = {i_seed}
148
+ i_indices.update(i_grain.get_basis_indices())
149
+ i_species = set(atomic_numbers[list(i_indices)])
150
+ clusters.append(
151
+ Cluster(
152
+ i_indices,
153
+ i_species,
154
+ i_grain,
155
+ system=system_copy,
156
+ distances=distances,
157
+ radii=radii,
158
+ bond_threshold=bond_threshold,
159
+ )
160
+ )
161
+ indices -= i_indices
162
+
163
+ # Check overlaps of the regions. For large overlaps the grains are
164
+ # merged (the real region was probably cut into pieces by unfortunate
165
+ # selection of the seed atom)
166
+ clusters = self._merge_clusters(
167
+ system_copy, clusters, merge_threshold, distances, bond_threshold
168
+ )
169
+
170
+ # Any remaining overlaps are resolved by assigning atoms to the
171
+ # "nearest" cluster
172
+ clusters = self._localize_clusters(
173
+ system_copy, clusters, merge_radius, distances
174
+ )
175
+
176
+ # Any atoms that are not chemically connected to the region will be
177
+ # excluded.
178
+ clusters = self._clean_clusters(clusters, bond_threshold)
179
+
180
+ return clusters
181
+
29
182
  def _merge_clusters(
30
183
  self, system, clusters, merge_threshold, distances, bond_threshold
31
184
  ):
@@ -181,158 +334,3 @@ class SBC:
181
334
  largest_indices = max(dbscan_clusters, key=lambda x: len(x))
182
335
  cluster.indices = np.array(cluster.indices)[largest_indices].tolist()
183
336
  return clusters
184
-
185
- def get_clusters(
186
- self,
187
- system,
188
- angle_tol=20,
189
- max_cell_size=6,
190
- pos_tol=0.7,
191
- merge_threshold=0.5,
192
- merge_radius=1,
193
- bond_threshold=0.65,
194
- overlap_threshold=-0.1,
195
- radii="covalent",
196
- ):
197
- """
198
- Used to detect and return structurally separate clusters within the
199
- given system.
200
-
201
- Args:
202
- system (ase.Atoms): The structure to partition.
203
- angle_tol (float): angle_tol parameter for PeriodicFinder
204
- max_cell_size (float): max_cell_size parameter for PeriodicFinder.get_region
205
- pos_tol (float): pos_tol parameter for PeriodicFinder.get_region
206
- merge_threshold (float): A threshold for merging two clusters
207
- together. Give as a fraction of shared atoms. Value of 1 would
208
- mean that clusters are never merged, value of 0 means that they
209
- are merged always when at least one atom is shared.
210
- merge_radius (float): Radius for finding nearby atoms when deciding
211
- which cluster is closest. The atomic radii are subtracted from
212
- distances. Given in angstroms.
213
- bond_threshold(float): Used to control the connectivity threshold
214
- for defining a chemical connection between atoms. Controls e.g.
215
- what types of unit cells are accepted and how outliers are
216
- removed from clusters.
217
- overlap_threshold(float): Used to exclude non-physical cells by
218
- checking overlap of atoms. Overlap between two atoms is
219
- calculated by subtracting atomic radii from the distance between
220
- the atoms.
221
- radii(str|np.ndarray): The radii to use for atoms. Use either a preset
222
- or a custom list of atomic radii for each atom. The available presets are:
223
-
224
- - covalent: Covalent radii from DOI:10.1039/B801115J
225
- - vdw: van Der Waals radii from DOI:10.1039/C3DT50599E
226
- - vdw_covalent: preferably van Der Waals radii, covalent if vdw
227
- not defined.
228
-
229
- Returns:
230
- A list of Clusters.
231
- """
232
- # Copy the system to avoid mutating the original
233
- system_copy = system.copy()
234
-
235
- # Here we ensure that the system has three valid basis vectors which
236
- # when forming a unit cell contain all of the atoms in the system. This
237
- # is to ensure that the code that requires scaled positions works
238
- # correctly.
239
- pbc = system_copy.get_pbc()
240
- basis = system_copy.get_cell()
241
- requires_completion = False
242
- for i in range(3):
243
- if not basis[i, :].any():
244
- if not pbc[i]:
245
- requires_completion = True
246
- else:
247
- raise ValueError(
248
- "Cannot process system with zero-volume cell and periodic boundaries."
249
- )
250
- if requires_completion:
251
- system_copy.set_cell(ase.geometry.complete_cell(basis))
252
- if not all(pbc):
253
- scaled_positions = system_copy.get_scaled_positions()
254
- new_cell = system_copy.get_cell()
255
- scale_cell = False
256
- for i in range(3):
257
- if not pbc[i]:
258
- i_pos = scaled_positions[:, i]
259
- max_pos = i_pos.max()
260
- min_pos = i_pos.min()
261
- if max_pos > 1 or min_pos < 0:
262
- scale_cell = True
263
- new_cell[i, :] *= (max_pos - min_pos) + 1
264
- if scale_cell:
265
- system_copy.set_cell(new_cell)
266
- system_copy.center()
267
-
268
- # Positions are wrapped
269
- system_copy.wrap()
270
-
271
- atomic_numbers = system.get_atomic_numbers()
272
- radii = matid.geometry.get_radii(radii, atomic_numbers)
273
-
274
- # Calculate the distances here once if they have not been provided.
275
- distances = matid.geometry.get_distances(system_copy, radii)
276
-
277
- # Iteratively search for new clusters until whole system is covered
278
- periodic_finder = PeriodicFinder(
279
- angle_tol=angle_tol, chem_similarity_threshold=0
280
- )
281
- indices = set(list(range(len(system_copy))))
282
- clusters = []
283
- while len(indices) != 0:
284
- i_seed = self.rng.choice(list(indices), 1)[0]
285
- i_grain, mask = periodic_finder.get_region(
286
- system_copy,
287
- seed_index=i_seed,
288
- max_cell_size=max_cell_size,
289
- pos_tol=pos_tol,
290
- bond_threshold=bond_threshold,
291
- overlap_threshold=overlap_threshold,
292
- distances=distances,
293
- return_mask=True,
294
- )
295
-
296
- # All neighbours that the periodic finder has tested are removed
297
- # from the search. This significantly helps with the scaling of the
298
- # clustering.
299
- tested_indices = set(np.arange(len(mask))[mask])
300
- indices -= tested_indices
301
-
302
- # If a grain is found, it is added as a single cluster and removed
303
- # from the search
304
- if i_grain is not None:
305
- i_indices = {i_seed}
306
- i_indices.update(i_grain.get_basis_indices())
307
- i_species = set(atomic_numbers[list(i_indices)])
308
- clusters.append(
309
- Cluster(
310
- i_indices,
311
- i_species,
312
- i_grain,
313
- system=system_copy,
314
- distances=distances,
315
- radii=radii,
316
- bond_threshold=bond_threshold,
317
- )
318
- )
319
- indices -= i_indices
320
-
321
- # Check overlaps of the regions. For large overlaps the grains are
322
- # merged (the real region was probably cut into pieces by unfortunate
323
- # selection of the seed atom)
324
- clusters = self._merge_clusters(
325
- system_copy, clusters, merge_threshold, distances, bond_threshold
326
- )
327
-
328
- # Any remaining overlaps are resolved by assigning atoms to the
329
- # "nearest" cluster
330
- clusters = self._localize_clusters(
331
- system_copy, clusters, merge_radius, distances
332
- )
333
-
334
- # Any atoms that are not chemically connected to the region will be
335
- # excluded.
336
- clusters = self._clean_clusters(clusters, bond_threshold)
337
-
338
- return clusters
@@ -69,7 +69,7 @@ class LinkedUnitCollection(dict):
69
69
  # Transform key to tuple, check length
70
70
  try:
71
71
  key = tuple(key)
72
- except:
72
+ except Exception:
73
73
  raise TypeError(
74
74
  "Could not transform the given key '{}' into tuple.".format(key)
75
75
  )
@@ -311,7 +311,6 @@ class LinkedUnitCollection(dict):
311
311
  if self._adsorbates is None:
312
312
  _, outside_indices = self.get_inside_and_outside_indices()
313
313
  basis_elements = set(self.cell.get_atomic_numbers())
314
- outside_indices = outside_indices
315
314
 
316
315
  # The substitutions have to be removed explicitly from the ouside
317
316
  # atoms because sometimes they are outside the tesselation.
@@ -219,6 +219,14 @@ class PeriodicFinder:
219
219
  generate a periodic region in the structure.
220
220
 
221
221
  Args:
222
+ system(ase.Atoms):
223
+ seed_index(int):
224
+ possible_spans(np.ndarray):
225
+ neighbour_mask(np.ndarray):
226
+ neighbour_factors(np.ndarray):
227
+ bond_threshold(float):
228
+ overlap_threshold(float):
229
+ pos_tol(float):
222
230
 
223
231
  Returns:
224
232
  ase.Atoms: A system representing the best cell that was found
@@ -288,9 +296,9 @@ class PeriodicFinder:
288
296
  adjacency_lists_add.append(i_adj_list_add)
289
297
  adjacency_lists_sub.append(i_adj_list_sub)
290
298
 
291
- # Get the spans that come from the original cell basis if they are
292
- # smaller than the maximum cell size.
293
- periodic_spans = system.get_cell()
299
+ # The lattice vectors are added as possible spans if they are set as
300
+ # periodic and they are shorter than the maximum cell size
301
+ periodic_spans = system.get_cell()[system.get_pbc()]
294
302
  periodic_span_lengths = np.linalg.norm(periodic_spans, axis=1)
295
303
  periodic_filter = periodic_span_lengths <= self.max_cell_size
296
304
  n_periodic_spans = periodic_filter.sum()
@@ -330,12 +338,19 @@ class PeriodicFinder:
330
338
  adjacency_lists_add.append(per_adjacency_list_add)
331
339
  adjacency_lists_sub.append(per_adjacency_list_sub)
332
340
 
333
- # Find the directions that repeat the neighbours above some preset
334
- # threshold. This is used to eliminate directions that are caused by
335
- # pure chance. The maximum score that a direction can get is
336
- # 2*n_neighbours. We specify that the score must be above 75% percent
337
- # of this maximum score to be considered a valid direction.
338
- valid_span_indices = np.where(metric >= 0.75 * n_neighbours)[0]
341
+ # Filter the spans based on how well they explain the repeated patterns
342
+ # around the neighbourhood of the seed atom. Spans are accepted if they
343
+ # produce more than 0.75 % of the possible repetitions in the seed
344
+ # neighbourhood OR they explain at least 50% of the maximum repetition
345
+ # that was found. These criteria are important to eliminate directions
346
+ # that are caused by pure chance, or represent directions where there is
347
+ # a periodic substructure that can only explain the structure partially.
348
+ # OLD: valid_span_indices = np.where(metric >= 0.75 * n_neighbours)[0]
349
+ valid_span_indices = np.where(
350
+ (metric >= 0.4 * (0 if len(metric) == 0 else metric.max()))
351
+ | (metric >= 0.75 * n_neighbours)
352
+ )[0]
353
+
339
354
  if len(valid_span_indices) == 0:
340
355
  return None, None, None
341
356
 
@@ -369,9 +384,7 @@ class PeriodicFinder:
369
384
  seed_nodes, seed_group_index, group_data_pbc = self._find_graphs(
370
385
  seed_index,
371
386
  numbers,
372
- dim,
373
387
  best_adjacency_lists,
374
- neighbour_nodes,
375
388
  neighbour_indices,
376
389
  neighbour_factors,
377
390
  )
@@ -525,9 +538,7 @@ class PeriodicFinder:
525
538
  self,
526
539
  seed_index,
527
540
  numbers,
528
- dim,
529
541
  best_adjacency_lists,
530
- neighbour_nodes,
531
542
  neighbour_indices,
532
543
  neighbour_factors,
533
544
  ):
@@ -836,7 +847,7 @@ class PeriodicFinder:
836
847
  min_dist_pos = scaled_pos[min_dist_index]
837
848
 
838
849
  # All the other copies are moved periodically to be near the
839
- # position that is closest to origin.
850
+ # position that is closest to origin
840
851
  distances = scaled_pos - min_dist_pos
841
852
  displacement = np.rint(distances)
842
853
  final_pos = scaled_pos - displacement
@@ -862,6 +873,7 @@ class PeriodicFinder:
862
873
  cell=best_spans,
863
874
  pbc=[True, True, True],
864
875
  )
876
+
865
877
  offset = proto_cell.get_positions()[seed_group_index]
866
878
 
867
879
  return proto_cell, offset, seed_group_index
@@ -1052,6 +1064,7 @@ class PeriodicFinder:
1052
1064
  symbols=averaged_rel_num,
1053
1065
  pbc=[True, True, False],
1054
1066
  )
1067
+
1055
1068
  offset = proto_cell.get_positions()[seed_group_index]
1056
1069
 
1057
1070
  return proto_cell, offset, seed_group_index
@@ -1275,7 +1288,6 @@ class PeriodicFinder:
1275
1288
 
1276
1289
  searched_cell_indices = set()
1277
1290
  used_indices = set()
1278
- used_seed_indices = set()
1279
1291
  searched_vacancy_positions = []
1280
1292
  queue = deque()
1281
1293
  collection = LinkedUnitCollection(
@@ -1294,8 +1306,6 @@ class PeriodicFinder:
1294
1306
  self._find_region_rec(
1295
1307
  system,
1296
1308
  collection,
1297
- number_to_index_map,
1298
- number_to_pos_map,
1299
1309
  seed_index,
1300
1310
  seed_pos,
1301
1311
  seed_number,
@@ -1305,10 +1315,8 @@ class PeriodicFinder:
1305
1315
  (0, 0, 0),
1306
1316
  used_indices,
1307
1317
  searched_vacancy_positions,
1308
- periodic_indices,
1309
1318
  queue,
1310
1319
  multipliers,
1311
- used_seed_indices,
1312
1320
  )
1313
1321
 
1314
1322
  # Keep searching while new cells are found
@@ -1327,8 +1335,6 @@ class PeriodicFinder:
1327
1335
  self._find_region_rec(
1328
1336
  system,
1329
1337
  collection,
1330
- number_to_index_map,
1331
- number_to_pos_map,
1332
1338
  queue_seed_index,
1333
1339
  queue_seed_pos,
1334
1340
  seed_number,
@@ -1338,10 +1344,8 @@ class PeriodicFinder:
1338
1344
  queue_index,
1339
1345
  used_indices,
1340
1346
  searched_vacancy_positions,
1341
- periodic_indices,
1342
1347
  queue,
1343
1348
  multipliers,
1344
- used_seed_indices,
1345
1349
  )
1346
1350
 
1347
1351
  return collection
@@ -1363,8 +1367,6 @@ class PeriodicFinder:
1363
1367
  self,
1364
1368
  system,
1365
1369
  collection,
1366
- number_to_index_map,
1367
- number_to_pos_map,
1368
1370
  seed_index,
1369
1371
  seed_pos,
1370
1372
  seed_atomic_number,
@@ -1374,10 +1376,8 @@ class PeriodicFinder:
1374
1376
  cell_index,
1375
1377
  used_indices,
1376
1378
  searched_vacancy_positions,
1377
- periodic_indices,
1378
1379
  queue,
1379
1380
  multipliers,
1380
- used_seed_indices,
1381
1381
  ):
1382
1382
  """
1383
1383
  Args:
@@ -1,6 +1,7 @@
1
1
  """Used to store data related to atomic elements. Also has some conversion
2
2
  utilities related to the data.
3
3
  """
4
+
4
5
  import numpy as np
5
6
  from ase.data import covalent_radii
6
7
 
@@ -0,0 +1,106 @@
1
+ /*Copyright 2019 DScribe developers
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+ */
15
+
16
+ #ifndef CELLLIST_H
17
+ #define CELLLIST_H
18
+
19
+ #include <pybind11/numpy.h>
20
+ #include <vector>
21
+
22
+ namespace py = pybind11;
23
+ using namespace std;
24
+
25
+ struct CellListResult {
26
+ vector<int> indices;
27
+ vector<double> distances;
28
+ vector<double> distances_squared;
29
+ vector<vector<double>> displacements;
30
+ vector<int> indices_original;
31
+ vector<vector<double>> factors;
32
+ };
33
+
34
+ /**
35
+ * For calculating pairwise distances using a cell list.
36
+ */
37
+ class CellList {
38
+ public:
39
+ /**
40
+ * Constructor
41
+ *
42
+ * @param positions Atomic positions in cartesian coordinates.
43
+ * @param atomicNumbers Atomic numbers.
44
+ */
45
+ CellList(py::array_t<double> positions, py::array_t<int> indices, py::array_t<double> factors, double cutoff);
46
+ /**
47
+ * Get the indices of atoms within the radial cutoff distance from the
48
+ * given position.
49
+ *
50
+ * @param x Cartesian x-coordinate.
51
+ * @param y Cartesian y-coordinate.
52
+ * @param z Cartesian z-coordinate.
53
+ */
54
+ CellListResult get_neighbours_for_position(const double x, const double y, const double z);
55
+ /**
56
+ * Get the indices of atoms within the radial cutoff distance from the
57
+ * given atomic index. The given index is not included in the returned
58
+ * values.
59
+ *
60
+ * @param i Index of the atom for which neighbours are queried for.
61
+ */
62
+ CellListResult get_neighbours_for_index(const int i);
63
+ /**
64
+ * Used to calculate the full displacement tensor in an efficient
65
+ * manner.
66
+ */
67
+ void get_displacement_tensor(
68
+ py::array_t<double> displacements,
69
+ py::array_t<double> distances,
70
+ py::array_t<double> factors,
71
+ py::array_t<int> original_indices,
72
+ int n_atoms
73
+ );
74
+ py::array_t<int> indices_py;
75
+
76
+ private:
77
+ /**
78
+ * Used to initialize the cell list. Querying for distances is only
79
+ * possible after this initialization.
80
+ */
81
+ void init();
82
+
83
+ // const py::detail::unchecked_reference<double, 2> positions;
84
+ // const py::detail::unchecked_reference<int, 1> indices;
85
+ // const py::detail::unchecked_reference<double, 2> factors;
86
+ vector<vector<double>> positions;
87
+ vector<int> indices;
88
+ vector<vector<double>> factors;
89
+ const double cutoff;
90
+ const double cutoffSquared;
91
+ double xmin;
92
+ double xmax;
93
+ double ymin;
94
+ double ymax;
95
+ double zmin;
96
+ double zmax;
97
+ double dx;
98
+ double dy;
99
+ double dz;
100
+ int nx;
101
+ int ny;
102
+ int nz;
103
+ vector<vector<vector<vector<int>>>> bins;
104
+ };
105
+
106
+ #endif
@@ -0,0 +1,89 @@
1
+ /*Copyright 2019 DScribe developers
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+ */
15
+
16
+ #ifndef GEOMETRY_H
17
+ #define GEOMETRY_H
18
+
19
+ #include <vector>
20
+ #include <stdexcept>
21
+ #include <pybind11/numpy.h>
22
+ #include "celllist.h"
23
+
24
+ namespace py = pybind11;
25
+ using namespace std;
26
+
27
+ struct ExtendedSystem {
28
+ py::array_t<double> positions;
29
+ py::array_t<int> atomic_numbers;
30
+ py::array_t<int> indices;
31
+ py::array_t<double> factors;
32
+ };
33
+
34
+ inline vector<double> cross(const vector<double>& a, const vector<double>& b);
35
+ inline double dot(const vector<double>& a, const vector<double>& b);
36
+ inline double norm(const vector<double>& a);
37
+
38
+ /**
39
+ * Used to periodically extend an atomic system in order to take into account
40
+ * periodic copies beyond the given unit cell.
41
+ *
42
+ * @param positions Cartesian positions of the original system.
43
+ * @param atomic_numbers Atomic numbers of the original system.
44
+ * @param cell Unit cell of the original system.
45
+ * @param pbc Periodic boundary conditions (array of three booleans) of the original system.
46
+ * @param cutoff Radial cutoff value for determining extension size.
47
+ *
48
+ * @return Instance of ExtendedSystem.
49
+ */
50
+ ExtendedSystem extend_system(
51
+ py::array_t<double> positions,
52
+ py::array_t<int> atomic_numbers,
53
+ py::array_t<double> cell,
54
+ py::array_t<bool> pbc,
55
+ double cutoff
56
+ );
57
+
58
+ /**
59
+ * Returns a CellList instance for the given atomic system.
60
+ *
61
+ * Note that you should control how large extension should be performed to take
62
+ * into account the periodic boundary conditions, as the CellList internally
63
+ * works with plain cartesian coordinates.
64
+ */
65
+ CellList get_cell_list(
66
+ py::array_t<double> positions,
67
+ py::array_t<double> cell,
68
+ py::array_t<bool> pbc,
69
+ double extension,
70
+ double cutoff
71
+ );
72
+
73
+ /**
74
+ * Calculates a pairwise displacement tensor (distance vectors) with a given
75
+ * cutoff.
76
+ */
77
+ void get_displacement_tensor(
78
+ py::array_t<double> displacements,
79
+ py::array_t<double> distances,
80
+ py::array_t<double> factors,
81
+ py::array_t<double> positions,
82
+ py::array_t<double> cell,
83
+ py::array_t<bool> pbc,
84
+ double cutoff,
85
+ bool return_factors,
86
+ bool return_distances
87
+ );
88
+
89
+ #endif
@@ -1,6 +1,7 @@
1
1
  """This module defines functions for deriving geometry related quantities from
2
2
  a atomic system.
3
3
  """
4
+
4
5
  import math
5
6
  import itertools
6
7
  from collections import defaultdict
@@ -291,9 +292,9 @@ def get_moments_of_inertia(system, weight=True):
291
292
  I13 = np.sum(-weights * x * z)
292
293
  I23 = np.sum(-weights * y * z)
293
294
 
294
- I = np.array([[I11, I12, I13], [I12, I22, I23], [I13, I23, I33]])
295
+ inertia_tensor = np.array([[I11, I12, I13], [I12, I22, I23], [I13, I23, I33]])
295
296
 
296
- evals, evecs = np.linalg.eigh(I)
297
+ evals, evecs = np.linalg.eigh(inertia_tensor)
297
298
 
298
299
  return evals, evecs
299
300
 
@@ -58,7 +58,7 @@ class SymmetryAnalyzer(object):
58
58
  self._analyzed_system = system
59
59
  elif n_pbc == 2:
60
60
  # Get the index of the non-periodic axis
61
- i_pbc = np.argwhere(pbc == False)[0]
61
+ i_pbc = np.argwhere(pbc == False)[0] # noqa: E712
62
62
 
63
63
  # Before calculating the conventional system, make sure that there
64
64
  # is enough vacuum in the periodic direction to remove any
@@ -355,7 +355,7 @@ class SymmetryAnalyzer(object):
355
355
  return ideal_sys
356
356
  # 2D materials get a special treatment
357
357
  elif n_pbc == 2:
358
- i_pbc = np.argwhere(pbc == False)[0]
358
+ i_pbc = np.argwhere(pbc == False)[0] # noqa: E712
359
359
 
360
360
  # Get the full 3D conventional system and it's symmetries. It will
361
361
  # include some symmetries that have a translational component
@@ -1357,7 +1357,7 @@ class SymmetryAnalyzer(object):
1357
1357
  for trans in translations:
1358
1358
  test_positions[
1359
1359
  i_trans * n_expr : (i_trans + 1) * n_expr, :
1360
- ] = (first_test_pos + trans)
1360
+ ] = first_test_pos + trans
1361
1361
  i_trans += 1
1362
1362
 
1363
1363
  # Test if each test positions can be matched to an atom
@@ -1,4 +1,5 @@
1
1
  """Utility functions to protect main process against segfaults"""
2
+
2
3
  import signal
3
4
 
4
5
 
@@ -2,6 +2,7 @@
2
2
  This script combines multiple pickle files into one python source file
3
3
  containing the symmetry information.
4
4
  """
5
+
5
6
  import os
6
7
  import pickle
7
8
  import pprint
@@ -2,6 +2,7 @@
2
2
  For each spacegroup returns the lattice directions in which all the atoms can
3
3
  be moved without breaking the symmetry.
4
4
  """
5
+
5
6
  import re
6
7
  import json
7
8
  import urllib.request
@@ -7,6 +7,7 @@ This information is needed because the normalizers and Wyckoff positions in
7
7
  Bilbao Crystallographic Server are given in the conventional setttings only by
8
8
  default.
9
9
  """
10
+
10
11
  import spglib
11
12
  from collections import defaultdict
12
13
 
@@ -8,6 +8,7 @@ Note that there can be connections problems with the server and it is
8
8
  recommeneded to do the scraping with some delays and by storing intermediate
9
9
  results.
10
10
  """
11
+
11
12
  import re
12
13
  import pickle
13
14
  import numpy as np
@@ -3,6 +3,7 @@ Goes through the spglib database for different Hall numbers and extracts space
3
3
  group specific intormation. The results are then written to a python file for
4
4
  later use.
5
5
  """
6
+
6
7
  import spglib
7
8
  import pickle
8
9
 
@@ -16,6 +16,7 @@ correponding ITA setting in Bilbao to fetch the Wyckoff positions.
16
16
  The mathematical expression for the Wyckoff positions is stored along with the
17
17
  necessary matrices for solving the variables from a particular system.
18
18
  """
19
+
19
20
  import re
20
21
  import parser
21
22
  import numpy as np
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: matid
3
- Version: 2.0.0.dev1
3
+ Version: 2.0.1
4
4
  Summary: MatID is a Python package for identifying and analyzing atomistic systems based on their structure.
5
5
  Author: Lauri Himanen
6
6
  License: Apache License
@@ -214,10 +214,13 @@ Classifier: Topic :: Scientific/Engineering :: Physics
214
214
  Classifier: License :: OSI Approved :: Apache Software License
215
215
  Classifier: Programming Language :: Python
216
216
  Classifier: Programming Language :: Python :: 3
217
- Classifier: Programming Language :: Python :: 3.7
218
- Classifier: Programming Language :: Python :: 3.8
219
217
  Classifier: Programming Language :: Python :: 3 :: Only
220
- Requires-Python: >=3.7
218
+ Classifier: Programming Language :: Python :: 3.8
219
+ Classifier: Programming Language :: Python :: 3.9
220
+ Classifier: Programming Language :: Python :: 3.10
221
+ Classifier: Programming Language :: Python :: 3.11
222
+ Classifier: Programming Language :: Python :: 3.12
223
+ Requires-Python: >=3.8
221
224
  Description-Content-Type: text/markdown
222
225
  License-File: LICENSE
223
226
  Requires-Dist: numpy
@@ -229,14 +232,15 @@ Requires-Dist: networkx>=2.4
229
232
  Provides-Extra: dev
230
233
  Requires-Dist: coverage; extra == "dev"
231
234
  Requires-Dist: pytest; extra == "dev"
232
- Requires-Dist: black; extra == "dev"
235
+ Requires-Dist: ruff; extra == "dev"
233
236
  Requires-Dist: pydoc-markdown; extra == "dev"
237
+ Requires-Dist: genbadge[coverage]; extra == "dev"
234
238
 
235
239
  <img src="https://raw.githubusercontent.com/nomad-coe/matid/main/docs/static/img/logo.png" width="300">
236
240
 
237
241
  ![Build status](https://github.com/nomad-coe/matid/actions/workflows/test.yml/badge.svg)
238
- [![Coverage Status](https://coveralls.io/repos/github/nomad-coe/matid/badge.svg?branch=main)](https://coveralls.io/github/nomad-coe/matid?branch=main)
239
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
242
+ ![Coverage Status](./reports/coverage/coverage-badge.svg)
243
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
240
244
 
241
245
  MatID is a Python package for identifying and analyzing atomistic systems based
242
246
  on their structure.
@@ -28,8 +28,10 @@ matid/data/constants.py
28
28
  matid/data/element_data.py
29
29
  matid/data/symmetry_data.py
30
30
  matid/ext/celllist.cpp
31
+ matid/ext/celllist.h
31
32
  matid/ext/ext.cpp
32
33
  matid/ext/geometry.cpp
34
+ matid/ext/geometry.h
33
35
  matid/geometry/__init__.py
34
36
  matid/geometry/geometry.py
35
37
  matid/symmetry/__init__.py
@@ -8,5 +8,6 @@ networkx>=2.4
8
8
  [dev]
9
9
  coverage
10
10
  pytest
11
- black
11
+ ruff
12
12
  pydoc-markdown
13
+ genbadge[coverage]
@@ -0,0 +1,93 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel", "pybind11~=2.11.1"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = 'matid'
7
+ version = '2.0.1'
8
+ description = 'MatID is a Python package for identifying and analyzing atomistic systems based on their structure.'
9
+ readme = "README.md"
10
+ authors = [{ name = "Lauri Himanen" }]
11
+ license = { file = "LICENSE" }
12
+ requires-python = ">=3.8"
13
+ dependencies = [
14
+ "numpy",
15
+ "ase",
16
+ "scipy",
17
+ "spglib>=1.15.0",
18
+ "scikit-learn",
19
+ "networkx>=2.4",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 5 - Production/Stable",
23
+ "Intended Audience :: Developers",
24
+ "Topic :: Scientific/Engineering :: Physics",
25
+ "License :: OSI Approved :: Apache Software License",
26
+ "Programming Language :: Python",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3 :: Only",
29
+ "Programming Language :: Python :: 3.8",
30
+ "Programming Language :: Python :: 3.9",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ ]
35
+ keywords = ['atoms', 'structure', 'materials', 'science', 'crystal', 'symmetry']
36
+
37
+ [project.urls]
38
+ Documentation = 'https://nomad-coe.github.io/matid/'
39
+ Source = "https://github.com/nomad-coe/matid"
40
+
41
+ [project.optional-dependencies]
42
+ dev = [
43
+ "coverage",
44
+ "pytest",
45
+ "ruff",
46
+ "pydoc-markdown",
47
+ "genbadge[coverage]",
48
+ ]
49
+
50
+ [tool.setuptools.packages.find]
51
+ include = ["matid*"]
52
+ exclude = ["tests*"]
53
+
54
+ [tool.ruff]
55
+ include = ["matid/*.py", "tests/*.py"]
56
+
57
+ # Same as Black.
58
+ line-length = 88
59
+ indent-width = 4
60
+
61
+ [tool.ruff.lint]
62
+ select = [
63
+ "E", # pycodestyle
64
+ "W", # pycodestyle
65
+ "PL", # pylint
66
+ ]
67
+ ignore = [
68
+ "E501", # Line too long ({width} > {limit} characters)
69
+ "E701", # Multiple statements on one line (colon)
70
+ "E731", # Do not assign a lambda expression, use a def
71
+ "E402", # Module level import not at top of file
72
+ "W605", # Invalid escape sequence
73
+ "PLR0911", # Too many return statements
74
+ "PLR0912", # Too many branches
75
+ "PLR0913", # Too many arguments in function definition
76
+ "PLR0915", # Too many statements
77
+ "PLR2004", # Magic value used instead of constant
78
+ "PLW0603", # Using the global statement
79
+ "PLW2901", # redefined-loop-name
80
+ "PLR1714", # consider-using-in
81
+ "PLR5501", # else-if-used
82
+ ]
83
+ fixable = ["ALL"]
84
+
85
+ [tool.ruff.format]
86
+ # indent with spaces, rather than tabs.
87
+ indent-style = "space"
88
+
89
+ # Like Black, respect magic trailing commas.
90
+ skip-magic-trailing-comma = false
91
+
92
+ # Like Black, automatically detect the appropriate line ending.
93
+ line-ending = "auto"
@@ -1,48 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools", "wheel", "pybind11~=2.11.1"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = 'matid'
7
- version = '2.0.0.dev1'
8
- description = 'MatID is a Python package for identifying and analyzing atomistic systems based on their structure.'
9
- readme = "README.md"
10
- authors = [{ name = "Lauri Himanen" }]
11
- license = { file = "LICENSE" }
12
- requires-python = ">=3.7"
13
- dependencies = [
14
- "numpy",
15
- "ase",
16
- "scipy",
17
- "spglib>=1.15.0",
18
- "scikit-learn",
19
- "networkx>=2.4",
20
- ]
21
- classifiers = [
22
- "Development Status :: 5 - Production/Stable",
23
- "Intended Audience :: Developers",
24
- "Topic :: Scientific/Engineering :: Physics",
25
- "License :: OSI Approved :: Apache Software License",
26
- "Programming Language :: Python",
27
- "Programming Language :: Python :: 3",
28
- "Programming Language :: Python :: 3.7",
29
- "Programming Language :: Python :: 3.8",
30
- "Programming Language :: Python :: 3 :: Only",
31
- ]
32
- keywords = ['atoms', 'structure', 'materials', 'science', 'crystal', 'symmetry']
33
-
34
- [project.urls]
35
- Documentation = 'https://nomad-coe.github.io/matid/'
36
- Source = "https://github.com/nomad-coe/matid"
37
-
38
- [project.optional-dependencies]
39
- dev = [
40
- "coverage",
41
- "pytest",
42
- "black",
43
- "pydoc-markdown"
44
- ]
45
-
46
- [tool.setuptools.packages.find]
47
- include = ["matid*"]
48
- exclude = ["tests*"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes