pyTEMlib 0.2025.4.1__py3-none-any.whl → 0.2025.9.1__py3-none-any.whl

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.

Potentially problematic release.


This version of pyTEMlib might be problematic. Click here for more details.

Files changed (94) hide show
  1. build/lib/pyTEMlib/__init__.py +33 -0
  2. build/lib/pyTEMlib/animation.py +640 -0
  3. build/lib/pyTEMlib/atom_tools.py +238 -0
  4. build/lib/pyTEMlib/config_dir.py +31 -0
  5. build/lib/pyTEMlib/crystal_tools.py +1219 -0
  6. build/lib/pyTEMlib/diffraction_plot.py +756 -0
  7. build/lib/pyTEMlib/dynamic_scattering.py +293 -0
  8. build/lib/pyTEMlib/eds_tools.py +826 -0
  9. build/lib/pyTEMlib/eds_xsections.py +432 -0
  10. build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
  11. build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  12. build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
  13. build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  14. build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  15. build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  16. build/lib/pyTEMlib/file_reader.py +274 -0
  17. build/lib/pyTEMlib/file_tools.py +811 -0
  18. build/lib/pyTEMlib/get_bote_salvat.py +69 -0
  19. build/lib/pyTEMlib/graph_tools.py +1153 -0
  20. build/lib/pyTEMlib/graph_viz.py +599 -0
  21. build/lib/pyTEMlib/image/__init__.py +37 -0
  22. build/lib/pyTEMlib/image/image_atoms.py +270 -0
  23. build/lib/pyTEMlib/image/image_clean.py +197 -0
  24. build/lib/pyTEMlib/image/image_distortion.py +299 -0
  25. build/lib/pyTEMlib/image/image_fft.py +277 -0
  26. build/lib/pyTEMlib/image/image_graph.py +926 -0
  27. build/lib/pyTEMlib/image/image_registration.py +316 -0
  28. build/lib/pyTEMlib/image/image_utilities.py +309 -0
  29. build/lib/pyTEMlib/image/image_window.py +421 -0
  30. build/lib/pyTEMlib/image_tools.py +699 -0
  31. build/lib/pyTEMlib/interactive_image.py +1 -0
  32. build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
  33. build/lib/pyTEMlib/microscope.py +61 -0
  34. build/lib/pyTEMlib/probe_tools.py +906 -0
  35. build/lib/pyTEMlib/sidpy_tools.py +153 -0
  36. build/lib/pyTEMlib/simulation_tools.py +104 -0
  37. build/lib/pyTEMlib/test.py +437 -0
  38. build/lib/pyTEMlib/utilities.py +314 -0
  39. build/lib/pyTEMlib/version.py +5 -0
  40. build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
  41. pyTEMlib/__init__.py +25 -3
  42. pyTEMlib/animation.py +31 -22
  43. pyTEMlib/atom_tools.py +29 -34
  44. pyTEMlib/config_dir.py +2 -28
  45. pyTEMlib/crystal_tools.py +129 -165
  46. pyTEMlib/eds_tools.py +559 -342
  47. pyTEMlib/eds_xsections.py +432 -0
  48. pyTEMlib/eels_tools/__init__.py +44 -0
  49. pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  50. pyTEMlib/eels_tools/eels_database.py +134 -0
  51. pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  52. pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  53. pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  54. pyTEMlib/file_reader.py +274 -0
  55. pyTEMlib/file_tools.py +260 -1130
  56. pyTEMlib/get_bote_salvat.py +69 -0
  57. pyTEMlib/graph_tools.py +101 -174
  58. pyTEMlib/graph_viz.py +150 -0
  59. pyTEMlib/image/__init__.py +37 -0
  60. pyTEMlib/image/image_atoms.py +270 -0
  61. pyTEMlib/image/image_clean.py +197 -0
  62. pyTEMlib/image/image_distortion.py +299 -0
  63. pyTEMlib/image/image_fft.py +277 -0
  64. pyTEMlib/image/image_graph.py +926 -0
  65. pyTEMlib/image/image_registration.py +316 -0
  66. pyTEMlib/image/image_utilities.py +309 -0
  67. pyTEMlib/image/image_window.py +421 -0
  68. pyTEMlib/image_tools.py +154 -915
  69. pyTEMlib/kinematic_scattering.py +1 -1
  70. pyTEMlib/probe_tools.py +1 -1
  71. pyTEMlib/test.py +437 -0
  72. pyTEMlib/utilities.py +314 -0
  73. pyTEMlib/version.py +2 -3
  74. pyTEMlib/xrpa_x_sections.py +14 -10
  75. {pytemlib-0.2025.4.1.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
  76. pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
  77. {pytemlib-0.2025.4.1.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
  78. pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
  79. pyTEMlib/core_loss_widget.py +0 -721
  80. pyTEMlib/eels_dialog.py +0 -754
  81. pyTEMlib/eels_dialog_utilities.py +0 -1199
  82. pyTEMlib/eels_tools.py +0 -2359
  83. pyTEMlib/file_tools_qt.py +0 -193
  84. pyTEMlib/image_dialog.py +0 -158
  85. pyTEMlib/image_dlg.py +0 -146
  86. pyTEMlib/info_widget.py +0 -1086
  87. pyTEMlib/info_widget3.py +0 -1120
  88. pyTEMlib/low_loss_widget.py +0 -479
  89. pyTEMlib/peak_dialog.py +0 -1129
  90. pyTEMlib/peak_dlg.py +0 -286
  91. pytemlib-0.2025.4.1.dist-info/RECORD +0 -38
  92. pytemlib-0.2025.4.1.dist-info/top_level.txt +0 -1
  93. {pytemlib-0.2025.4.1.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
  94. {pytemlib-0.2025.4.1.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
pyTEMlib/crystal_tools.py CHANGED
@@ -17,35 +17,27 @@ Usage:
17
17
  See the notebooks for examples of these routines
18
18
 
19
19
  """
20
+ import typing
21
+ import itertools
20
22
 
21
23
  import numpy as np
22
- import itertools
23
24
  import ase
24
25
  import ase.spacegroup
25
26
  import ase.build
27
+ import ase.lattice
28
+ import ase.lattice.compounds
29
+ import ase.neighborlist
26
30
  import ase.data.colors
27
31
 
28
- import matplotlib.pylab as plt # basic plotting
29
- from scipy.spatial import cKDTree
30
32
  import scipy
31
- _spglib_present = True
32
- try:
33
- import spglib
34
- except ModuleNotFoundError:
35
- _spglib_present = False
33
+ import scipy.sparse
36
34
 
37
- if _spglib_present:
38
- print('Symmetry functions of spglib enabled')
39
- else:
40
- print('spglib not installed; Symmetry functions of spglib disabled')
35
+ import spglib
41
36
 
42
-
43
- # from mpl_toolkits.mplot3d import Axes3D # 3D plotting
44
- # from matplotlib.patches import Circle # , Ellipse, Rectangle
45
- # from matplotlib.collections import PatchCollection
37
+ import matplotlib.pylab as plt # basic plotting
46
38
 
47
39
 
48
- def get_dictionary(atoms):
40
+ def get_dictionary(atoms: ase.Atoms) -> dict[str, typing.Any]:
49
41
  """
50
42
  structure dictionary from ase.Atoms object
51
43
  """
@@ -57,7 +49,10 @@ def get_dictionary(atoms):
57
49
  return tags
58
50
 
59
51
 
60
- def atoms_from_dictionary(tags):
52
+ def atoms_from_dictionary(tags: dict[str, typing.Any]) -> ase.Atoms:
53
+ """
54
+ Create an ase.Atoms object from a structure dictionary
55
+ """
61
56
  atoms = ase.Atoms(cell=tags['unit_cell'],
62
57
  symbols=tags['elements'],
63
58
  scaled_positions=tags['base'])
@@ -66,7 +61,7 @@ def atoms_from_dictionary(tags):
66
61
  return atoms
67
62
 
68
63
 
69
- def get_symmetry(atoms, verbose=True):
64
+ def get_symmetry(atoms: ase.Atoms, verbose: bool = True):
70
65
  """
71
66
  Symmetry analysis with spglib
72
67
 
@@ -80,52 +75,72 @@ def get_symmetry(atoms, verbose=True):
80
75
 
81
76
  Returns
82
77
  -------
83
-
78
+ bool
84
79
  """
85
- if _spglib_present:
86
- if verbose:
87
- print('#####################')
88
- print('# Symmetry Analysis #')
89
- print('#####################')
90
-
91
- base = atoms.get_scaled_positions()
92
- for i, atom in enumerate(atoms):
93
- if verbose:
94
- print(f'{i + 1}: {atom.number} = {2} : [{base[i][0]:.2f}, {base[i][1]:.2f}, {base[i][2]:.2f}]')
95
-
96
- lattice = (atoms.cell, atoms.get_scaled_positions(), atoms.numbers)
97
- spgroup = spglib.get_spacegroup(lattice, symprec=1e-2)
98
- sym = spglib.get_symmetry(lattice)
80
+ if verbose:
81
+ print('#####################')
82
+ print('# Symmetry Analysis #')
83
+ print('#####################')
99
84
 
85
+ base = atoms.get_scaled_positions()
86
+ for i, atom in enumerate(atoms):
100
87
  if verbose:
101
- print(" Spacegroup is %s." % spgroup)
102
- print(' Crystal has {0} symmetry operation'.format(sym['rotations'].shape[0]))
88
+ print(f'{i + 1}: {atom.number} = {atom.symbol} : [{base[i][0]:.2f}, {base[i][1]:.2f}, {base[i][2]:.2f}]')
103
89
 
104
- p_lattice, p_positions, p_numbers = spglib.find_primitive(lattice, symprec=1e-5)
105
- print("\n########################\n #Basis vectors of primitive Cell:")
106
- for i in range(3):
107
- print('[{0:.4f}, {1:.4f}, {2:.4f}]'.format(p_lattice[i][0], p_lattice[i][1], p_lattice[i][2]))
90
+ lattice = (atoms.cell, atoms.get_scaled_positions(), atoms.numbers)
91
+ spgroup = spglib.get_spacegroup(lattice, symprec=1e-2)
92
+ sym = spglib.get_symmetry(lattice)
108
93
 
109
- print('There {0} atoms and {1} species in primitive unit cell:'.format(len(p_positions), p_numbers))
110
- else:
111
- print('spglib is not installed')
94
+ if verbose:
95
+ print(f" Spacegroup is {spgroup}.")
96
+ print(f" Crystal has {sym['rotations'].shape[0]} symmetry operation")
97
+
98
+ p_lattice, p_positions, p_numbers = spglib.find_primitive(lattice, symprec=1e-5)
99
+ print("\n########################\n #Basis vectors of primitive Cell:")
100
+ for i in range(3):
101
+ print(f'[{p_lattice[i][0]:.4f}, {p_lattice[i][1]:.4f}, {p_lattice[i][2]:.4f}]')
112
102
 
103
+ print(f'There {len(p_positions)} atoms and {p_numbers} species in primitive unit cell:')
113
104
  return True
114
105
 
115
106
 
116
- def set_bond_radii(atoms):
107
+ def set_bond_radii(atoms: ase.Atoms):
108
+ """
109
+ Set bond radii for atoms in the structure from electronFF database
110
+ at the end of this
111
+ """
117
112
  bond_radii = np.ones(len(atoms))
118
113
  for i in range(len(atoms)):
119
114
  bond_radii[i] = electronFF[atoms.symbols[i]]['bond_length'][1]
120
115
  atoms.info['bond_radii'] = bond_radii
121
116
 
122
117
 
123
- def get_projection(crystal, layers=1):
118
+ def get_projection(crystal: ase.Atoms, layers: int = 1) -> ase.Atoms:
119
+ """
120
+ Get the projection of a crystal structure onto a specific plane.
121
+ zone axis must be specified in the crystal.info['experimental'] dictionary.
122
+ """
123
+ if 'experimental' not in crystal.info:
124
+ raise ValueError("Zone axis must be specified in the crystal.info['experimental'] dictionary.")
125
+ if 'zone_axis' not in crystal.info['experimental']:
126
+ raise ValueError("Zone axis must be specified in the crystal.info['experimental'] dictionary.")
124
127
  zone_axis = crystal.info['experimental']['zone_axis']
125
128
  angle = crystal.info['experimental']['angle']
129
+ if 'layers' in crystal.info['structure']:
130
+ layers = crystal.info['structure']['layers']
126
131
  projected_crystal = ase.build.surface(crystal, zone_axis, vacuum=.0, layers=layers)
127
132
 
128
- element_tree = cKDTree(projected_crystal.positions[:, 0:2])
133
+ scaled = projected_crystal.get_scaled_positions()
134
+ projected_crystal.positions[scaled[:,0]>0.95, 0] = 0
135
+ projected_crystal.positions[scaled[:,1]>0.95, 1] = 0
136
+ # do not change this because we did not add a vacuum
137
+ # projected_crystal.positions[scaled[:,2]>0.95, 2] = 0
138
+
139
+ projected_crystal.positions[scaled[:,0]<0.05, 0] = 0
140
+ projected_crystal.positions[scaled[:,1]<0.05, 1] = 0
141
+ projected_crystal.positions[scaled[:,2]<0.05, 2] = 0
142
+
143
+ element_tree = scipy.spatial.KDTree(projected_crystal.positions[:, 0:2])
129
144
  done = []
130
145
  projected = []
131
146
  for atom in projected_crystal:
@@ -133,25 +148,25 @@ def get_projection(crystal, layers=1):
133
148
  near = element_tree.query_ball_point(atom.position[:2], 0.05)
134
149
  projected.append(near)
135
150
  done.extend(near)
136
- print('projected atomic numbers')
137
151
  atomic_numbers = []
138
152
  for pro in projected:
139
153
  atomic_numbers.append(projected_crystal.get_atomic_numbers()[pro].sum())
140
-
154
+
141
155
  projected_crystal.rotate(np.degrees(angle) % 360, 'z', rotate_cell=True)
142
-
143
156
  near_base = np.array([projected_crystal.cell[0, :2], -projected_crystal.cell[0, :2],
144
- projected_crystal.cell[1, :2], -projected_crystal.cell[1, :2],
145
- projected_crystal.cell[0, :2] + projected_crystal.cell[1, :2],
146
- -(projected_crystal.cell[0, :2] + projected_crystal.cell[1, :2])])
157
+ projected_crystal.cell[1, :2], -projected_crystal.cell[1, :2],
158
+ projected_crystal.cell[0, :2] + projected_crystal.cell[1, :2],
159
+ -(projected_crystal.cell[0, :2] + projected_crystal.cell[1, :2])])
147
160
  lines = np.array([[[0, near_base[0, 0]], [0, near_base[0, 1]]],
148
- [[0, near_base[2, 0]], [0, near_base[2, 1]]],
149
- [[near_base[0, 0], near_base[4, 0]], [near_base[0, 1], near_base[4, 1]]],
150
- [[near_base[2, 0], near_base[4, 0]], [near_base[2, 1], near_base[4, 1]]]])
161
+ [[0, near_base[2, 0]], [0, near_base[2, 1]]],
162
+ [[near_base[0, 0], near_base[4, 0]], [near_base[0, 1],
163
+ near_base[4, 1]]],
164
+ [[near_base[2, 0], near_base[4, 0]], [near_base[2, 1],
165
+ near_base[4, 1]]]])
151
166
  projected_atoms = []
152
167
  for index in projected:
153
168
  projected_atoms.append(index[0])
154
-
169
+
155
170
  projected_crystal.info['projection'] = {'indices': projected,
156
171
  'projected': projected_atoms,
157
172
  'projected_Z': atomic_numbers,
@@ -161,58 +176,6 @@ def get_projection(crystal, layers=1):
161
176
  return projected_crystal
162
177
 
163
178
 
164
- def jmol_viewer(atoms, size=2):
165
- """
166
- jmol viewer of ase .Atoms object
167
- requires jupyter-jsmol to be installed (available through conda or pip)
168
-
169
- Parameter
170
- ---------
171
- atoms: ase.Atoms
172
- structure info
173
- size: int, list, or np.array of size 3; default 1
174
- size of unit_cell; maximum = 8
175
-
176
- Returns
177
- -------
178
- view: JsmolView object
179
-
180
- Example
181
- -------
182
- from jupyter_jsmol import JsmolView
183
- import ase
184
- import ase.build
185
- import itertools
186
- import numpy as np
187
- atoms = ase.build.bulk('Cu', 'fcc', a=5.76911, cubic=True)
188
- for pos in list(itertools.product([0.25, .75], repeat=3)):
189
- atoms += ase.Atom('Al', al2cu.cell.lengths()*pos)
190
-
191
- view = plot_ase(atoms, size = 8)
192
- display(view)
193
- """
194
- try:
195
- from jupyter_jsmol import JsmolView
196
- from IPython.display import display
197
- except ImportError:
198
- print('this function is based on jupyter-jsmol, please install with: \n '
199
- 'conda install -c conda-forge jupyter-jsmol')
200
- return
201
-
202
- if isinstance(size, int):
203
- size = [size] * 3
204
-
205
- [a, b, c] = atoms.cell.lengths()
206
- [alpha, beta, gamma] = atoms.cell.angles()
207
-
208
- view = JsmolView.from_ase(atoms, f"{{{size[0]} {size[1]} {size[2]}}}"
209
- f" unitcell {{{a:.3f} {b:.3f} {c:.3f} {alpha:.3f} {beta:.3f} {gamma:.3f}}}")
210
-
211
- display(view)
212
-
213
- return view
214
-
215
-
216
179
  def plot_super_cell(super_cell, shift_x=0.):
217
180
  """ make a super_cell to plot with extra atoms at periodic boundaries"""
218
181
 
@@ -231,13 +194,13 @@ def plot_super_cell(super_cell, shift_x=0.):
231
194
  return super_cell2plot
232
195
 
233
196
 
234
- def ball_and_stick(atoms, extend=1, max_bond_length=0.):
197
+ def ball_and_stick(atoms: ase.Atoms, extend: int = 1) -> ase.Atoms:
235
198
  """Calculates the data to plot a ball and stick model
236
199
 
237
200
  Parameters
238
201
  ----------
239
202
  atoms: ase.Atoms object
240
- object containing the structural information like 'cell', 'positions', and 'symbols' .
203
+ object containing the structural information like 'cell', and 'symbols' .
241
204
 
242
205
  extend: integer or list f 3 integers
243
206
  The *extend* argument scales the effective cell in which atoms
@@ -247,30 +210,22 @@ def ball_and_stick(atoms, extend=1, max_bond_length=0.):
247
210
  This will of cause make the returned cell non-repeatable, but this is
248
211
  very useful for visualisation.
249
212
 
250
- max_bond_length: 1 float
251
- The max_bond_length argument defines the distance for which a bond will be shown.
252
- If max_bond_length is zero, the tabulated atom radii will be used.
253
-
254
213
  Returns
255
214
  -------
256
215
  super_cell: ase.Atoms object
257
216
  structure with additional information in info dictionary
258
217
  """
259
-
260
218
  if not isinstance(atoms, ase.Atoms):
261
219
  raise TypeError('Need an ase Atoms object')
262
220
 
263
- from ase import neighborlist
264
- from scipy import sparse
265
- from scipy.sparse import dok_matrix
266
-
267
221
  super_cell = plot_super_cell(atoms*extend)
268
222
  cell = super_cell.cell.array
269
223
  # Corners and Outline of unit cell
270
224
  h = (0, 1)
271
225
  corner_vectors = np.dot(np.array(list(itertools.product(h, h, h))), cell)
272
- corner_matrix = dok_matrix((8, 8), dtype=bool)
273
- trace = [[0, 1], [1, 3], [2, 3], [0, 2], [0, 4], [4, 5], [5, 7], [6, 7], [4, 6], [1, 5], [2, 6], [3, 7]]
226
+ corner_matrix = scipy.sparse.dok_matrix((8, 8), dtype=bool)
227
+ trace = [[0, 1], [1, 3], [2, 3], [0, 2], [0, 4], [4, 5],
228
+ [5, 7], [6, 7], [4, 6], [1, 5], [2, 6], [3, 7]]
274
229
  for s, e in trace:
275
230
  corner_matrix[s, e] = True
276
231
 
@@ -280,7 +235,8 @@ def ball_and_stick(atoms, extend=1, max_bond_length=0.):
280
235
  bond_lengths.append(electronFF[atom.symbol]['bond_length'][1])
281
236
 
282
237
  super_cell.set_cell(cell*2, scale_atoms=False) # otherwise, corner atoms have distance 0
283
- neighbor_list = neighborlist.NeighborList(bond_lengths, self_interaction=False, bothways=False)
238
+ neighbor_list = ase.neighborlist.NeighborList(bond_lengths, self_interaction=False,
239
+ bothways=False)
284
240
  neighbor_list.update(super_cell)
285
241
  bond_matrix = neighbor_list.get_connectivity_matrix()
286
242
 
@@ -288,18 +244,20 @@ def ball_and_stick(atoms, extend=1, max_bond_length=0.):
288
244
  bond_matrix = scipy.sparse.dok_array(bond_matrix)
289
245
  if super_cell.info is None:
290
246
  super_cell.info = {}
291
- super_cell.info['plot_cell'] = {'bond_matrix': bond_matrix, 'corner_vectors': corner_vectors,
292
- 'bond_length': bond_lengths, 'corner_matrix': corner_matrix}
247
+ super_cell.info['plot_cell'] = {'bond_matrix': bond_matrix,
248
+ 'corner_vectors': corner_vectors,
249
+ 'bond_length': bond_lengths,
250
+ 'corner_matrix': corner_matrix}
293
251
  super_cell.set_cell(cell/2, scale_atoms=False)
294
252
  return super_cell
295
253
 
296
254
 
297
- def plot_unit_cell(atoms, extend=1, max_bond_length=1.0, ax=None):
255
+ def plot_unit_cell(atoms, extend=1, ax=None):
298
256
  """
299
257
  Simple plot of unit cell
300
258
  """
301
259
 
302
- super_cell = ball_and_stick(atoms, extend=extend, max_bond_length=max_bond_length)
260
+ super_cell = ball_and_stick(atoms, extend=extend)
303
261
 
304
262
  corners = super_cell.info['plot_cell']['corner_vectors']
305
263
  positions = super_cell.positions - super_cell.cell.lengths()/2
@@ -313,13 +271,14 @@ def plot_unit_cell(atoms, extend=1, max_bond_length=1.0, ax=None):
313
271
  ax.plot3D(corners[line, 0], corners[line, 1], corners[line, 2], color="blue")
314
272
 
315
273
  # draw bonds
316
- bond_matrix = super_cell.info['plot_cell']['bond_matrix']
317
274
  for bond in super_cell.info['plot_cell']['bond_matrix'].keys():
318
- ax.plot3D(positions[bond, 0], positions[bond, 1], positions[bond, 2], color="black", linewidth=4)
275
+ ax.plot3D(positions[bond, 0], positions[bond, 1], positions[bond, 2],
276
+ color="black", linewidth=4)
319
277
  # , tube_radius=0.02)
320
278
 
321
279
  # draw atoms
322
- ax.scatter(super_cell.positions[:, 0], super_cell.positions[:, 1], super_cell.positions[:, 2],
280
+ ax.scatter(super_cell.positions[:, 0], super_cell.positions[:, 1],
281
+ super_cell.positions[:, 2],
323
282
  color=tuple(jmol_colors[super_cell.get_atomic_numbers()]), alpha=1.0, s=50)
324
283
  maximum_position = super_cell.positions.max()*1.05
325
284
  ax.set_proj_type('ortho')
@@ -341,12 +300,11 @@ def plot_unit_cell(atoms, extend=1, max_bond_length=1.0, ax=None):
341
300
  jmol_colors = ase.data.colors.jmol_colors
342
301
 
343
302
 
344
- def structure_by_name(crystal_name):
303
+ def structure_by_name(crystal_name: str) -> ase.Atoms | None:
345
304
  """
346
305
  Provides crystal structure in ase.Atoms format.
347
306
  Additional information is stored in the info attribute as a dictionary
348
307
 
349
-
350
308
  Parameter
351
309
  ---------
352
310
  crystal_name: str
@@ -370,9 +328,6 @@ def structure_by_name(crystal_name):
370
328
  """
371
329
 
372
330
  # Check whether name is in the crystal_data_base
373
- import ase
374
- import ase.build
375
-
376
331
  if crystal_name.lower() in cdb:
377
332
  tags = cdb[crystal_name.lower()].copy()
378
333
  else:
@@ -387,62 +342,61 @@ def structure_by_name(crystal_name):
387
342
  atoms = ase.build.bulk(tags['elements'], 'bcc', a=tags['a'], cubic=True)
388
343
 
389
344
  elif tags['symmetry'].lower() == 'diamond':
390
- import ase.lattice.cubic
391
345
  atoms = ase.lattice.cubic.Diamond(tags['elements'], latticeconstant=tags['a'])
392
346
 
393
347
  elif 'rocksalt' in tags['symmetry']: # B1
394
- import ase.lattice.compounds
395
348
  atoms = ase.lattice.compounds.Rocksalt(tags['elements'], latticeconstant=tags['a'])
396
349
 
397
350
  elif 'zincblende' in tags['symmetry']:
398
- import ase.lattice.compounds
399
351
  atoms = ase.lattice.compounds.B3(tags['elements'], latticeconstant=tags['a'])
400
352
 
401
353
  elif 'B2' in tags['symmetry']:
402
- import ase.lattice.compounds
403
354
  atoms = ase.lattice.compounds.B2(tags['elements'], latticeconstant=tags['a'])
404
355
 
405
356
  elif 'graphite' in tags['symmetry']:
406
357
  base = [(0, 0, 0), (0, 0, 1/2), (2/3, 1/3, 0), (1/3, 2/3, 1/2)]
407
358
  structure_matrix = np.array([[tags['a'], 0., 0.],
408
- [np.cos(np.pi/3*2)*tags['a'], np.sin(np.pi/3*2)*tags['a'], 0.],
359
+ [np.cos(np.pi/3*2)*tags['a'],
360
+ np.sin(np.pi/3*2)*tags['a'], 0.],
409
361
  [0., 0., tags['c']]])
410
362
 
411
363
  atoms = ase.Atoms(tags['elements'], cell=structure_matrix, scaled_positions=base)
412
364
 
413
365
  elif 'perovskite' in tags['symmetry']:
414
- import ase.spacegroup
415
366
  atom_positions = [(0.0, 0.0, 0.0), (0.5, 0.5, 0.5), (0.5, 0.5, 0.0)]
416
- atoms = ase.spacegroup.crystal(tags['elements'], atom_positions, spacegroup=221, cellpar=tags['a'])
367
+ atoms = ase.spacegroup.crystal(tags['elements'], atom_positions, spacegroup=221,
368
+ cellpar=tags['a'])
417
369
 
418
370
  elif 'wurzite' in tags['symmetry']:
419
- import ase.spacegroup
420
371
  atom_positions = [(1/3, 2/3, 0.0), (1/3, 2/3, tags['u'])]
421
372
  atoms = ase.spacegroup.crystal(tags['elements'], atom_positions, spacegroup=186,
422
373
  cellpar=[tags['a'], tags['a'], tags['c'], 90, 90, 120])
423
374
 
424
375
  elif 'rutile' in tags['symmetry']:
425
- import ase.spacegroup
426
376
  atoms = ase.spacegroup.crystal(tags['elements'], basis=[(0, 0, 0), (0.3, 0.3, 0.0)],
427
- spacegroup=136, cellpar=[tags['a'], tags['a'], tags['c'], 90, 90, 90])
377
+ spacegroup=136, cellpar=[tags['a'], tags['a'],
378
+ tags['c'], 90, 90, 90])
428
379
  elif 'dichalcogenide' in tags['symmetry']:
429
- import ase.spacegroup
430
-
431
380
  u = tags['u']
432
381
  base = [(1 / 3., 2 / 3., 1 / 4.), (2 / 3., 1 / 3., 3 / 4.),
433
382
  (2 / 3., 1 / 3., 1 / 4. + u), (2 / 3., 1 / 3., 1 / 4. - u),
434
383
  (1 / 3., 2 / 3., 3 / 4. + u), (1 / 3., 2 / 3., 3 / 4. - u)]
435
- atoms = ase.spacegroup.crystal(tags['elements'][0] * 2 + tags['elements'][1] * 4, base, spacegroup=194,
384
+ atoms = ase.spacegroup.crystal(tags['elements'][0] * 2 + tags['elements'][1] * 4,
385
+ base, spacegroup=194,
436
386
  cellpar=[tags['a'], tags['a'], tags['c'], 90, 90, 120])
437
387
 
438
388
  elif tags['symmetry'].lower() in ['primitive', 'hexagonal']:
439
- atoms = ase.Atoms(tags['elements'], cell=tags['unit_cell'], scaled_positions=tags['base'])
389
+ atoms = ase.Atoms(tags['elements'], cell=tags['unit_cell'],
390
+ scaled_positions=tags['base'])
440
391
 
441
392
  else:
442
- print(' symmetry of structure is wrong')
393
+ raise ValueError("Symmetry of structure is wrong")
443
394
 
444
- atoms.info = {'structure': {'reference': tags['reference'], 'link': tags['link']},
395
+ atoms.info = {'structure': {'reference': tags['reference'],
396
+ 'link': tags['link']},
445
397
  'title': tags['crystal_name']}
398
+ if 'layers' in tags:
399
+ atoms.info['structure']['layers'] = tags['layers']
446
400
  return atoms
447
401
 
448
402
 
@@ -455,6 +409,17 @@ cdb = {'aluminum': {'crystal_name': 'aluminum',
455
409
  'link': 'http://doi.org/10.1515/zna-1967-0115'}}
456
410
  cdb['al'] = cdb['aluminium'] = cdb['aluminum']
457
411
 
412
+ cdb['theta_prime'] = {'crystal_name': 'theta_prime',
413
+ 'symmetry':'primitive',
414
+ 'elements':'Al4Cu2',
415
+ 'base': [(0.5,0.25,0.0), (0.0,0.25,0.5),
416
+ (0.,0.75,0.5), (0.5,0.75,0.0),
417
+ (0,0,0), (0.5,0.5,0.5)],
418
+ 'unit_cell': [[4.05, 0, 0], [0, 4.05 * 10/7, 0], [0, 0, 4.05]],
419
+ 'layers': 2,
420
+ 'reference': '',
421
+ 'link': ''}
422
+
458
423
  cdb['gold'] = {'crystal_name': 'gold',
459
424
  'symmetry': 'FCC',
460
425
  'elements': 'Au',
@@ -495,7 +460,7 @@ cdb['silicon'] = {'crystal_name': 'silicon',
495
460
  'symmetry': 'diamond',
496
461
  'elements': 'Si',
497
462
  'a': 5.430880, # Angstrom for 300K
498
- 'reference': 'C. R. Hubbard, H. E. Swanson, and F. A. Mauer, J. Appl. Crystallogr., 1975, 8, 45',
463
+ 'reference': 'C. R. Hubbard, et al., J. Appl. Crystallogr., 1975, 8, 45',
499
464
  'link': 'https://doi.org/10.1107/S0021889875009508'}
500
465
  cdb['si'] = cdb['silicon']
501
466
 
@@ -503,7 +468,7 @@ cdb['gaas'] = {'crystal_name': 'GaAs',
503
468
  'symmetry': 'zincblende(B3)',
504
469
  'elements': ['Ga', 'As'],
505
470
  'a': 5.65325, # Angstrom for 300K
506
- 'reference': 'J.F.C. Baker, M. Hart, M.A.G. Halliwell, R. Heckingbottom, Solid-State Electronics, 19, '
471
+ 'reference': 'J.F.C. Baker, et al., Solid-State Electronics, 19, '
507
472
  '1976, 331-334,',
508
473
  'link': 'https://doi.org/10.1016/0038-1101(76)90031-9'}
509
474
 
@@ -511,16 +476,17 @@ cdb['fcc fe'] = {'crystal_name': 'FCC Fe',
511
476
  'symmetry': 'FCC',
512
477
  'elements': 'Fe',
513
478
  'a': 3.3571, # Angstrom
514
- 'reference': 'R. Kohlhaas, P. Donner, and N. Schmitz-Pranghe, Z. Angew. Phys., 1967, 23, 245',
479
+ 'reference': 'R. Kohlhaas, et al., Z. Angew. Phys., 1967, 23, 245',
515
480
  'link': ''}
516
481
 
517
482
  cdb['iron'] = {'crystal_name': 'BCC Fe',
518
483
  'symmetry': 'BCC',
519
484
  'elements': 'Fe',
520
485
  'a': 2.866, # Angstrom
521
- 'reference': 'Z. S. Basinski, W. Hume-Rothery and A. L. Sutton, Proceedings of the Royal Society of '
522
- 'London. Series A, Mathematical and Physical Sciences Vol. 229, No. 1179 '
523
- '(May 24, 1955), pp. 459-467',
486
+ 'reference': 'Z. S. Basinski, W. Hume-Rothery and A. L. Sutton'+
487
+ 'Proceedings of the Royal Society of '+
488
+ 'London. Series A, Mathematical and Physical Sciences'+
489
+ 'Vol. 229, No. 1179 (May 24, 1955), pp. 459-467',
524
490
  'link': 'http://www.jstor.org/stable/99693'}
525
491
  cdb['bcc fe'] = cdb['alpha iron'] = cdb['iron']
526
492
 
@@ -528,7 +494,7 @@ cdb['srtio3'] = {'crystal_name': 'SrTiO3',
528
494
  'symmetry': 'perovskite',
529
495
  'elements': ['Sr', 'Ti', 'O'],
530
496
  'a': 3.905268, # Angstrom
531
- 'reference': 'M. Schmidbauer, A. Kwasniewski and J. Schwarzkopf, Acta Cryst. (2012). B68, 8-14',
497
+ 'reference': 'M. Schmidbauer, et al., Acta Cryst. (2012). B68, 8-14',
532
498
  'link': 'http://doi.org/10.1107/S0108768111046738'}
533
499
  cdb['strontium titanate'] = cdb['srtio3']
534
500
 
@@ -618,18 +584,16 @@ cdb['mose2'] = {'crystal_name': 'MoSe2',
618
584
  'c': 15.45142322, # Angstrom
619
585
  'u': 0.108249,
620
586
  'reference': '', 'link': ''}
621
- a_l = 0.3336
622
- c_l = 0.4754
623
- base_l = [(2. / 3., 1. / 3., .5), (1. / 3., 2. / 3., 0.), (2. / 3., 1. / 3., 0.), (1. / 3., 2. / 3., .5)]
624
587
 
588
+ base_l = [(2./3., 1./3., .5), (1./3., 2./3., 0.), (2./3., 1./3., 0.), (1./3., 2./3., .5)]
625
589
  cdb['zno hexagonal'] = {'crystal_name': 'ZnO hexagonal',
626
590
  'symmetry': 'hexagonal',
627
- 'a': a_l, # nm
628
- 'c': c_l, # not np.sqrt(8/3)*1
591
+ 'a': 0.3336, # nm
592
+ 'c': 0.4754, # not np.sqrt(8/3)*1
629
593
  'elements': ['Zn', 'Zn', 'O', 'O'],
630
- 'unit_cell': [[a_l, 0., 0.],
631
- [np.cos(120 / 180 * np.pi) * a_l, np.sin(120 / 180 * np.pi) * a_l, 0.],
632
- [0., 0., c_l]],
594
+ 'unit_cell': [[0.3336, 0., 0.],
595
+ [np.cos(120/180 * np.pi) * 0.3336, np.sin(120/180 * np.pi) * 0.3336, 0.],
596
+ [0., 0., 0.4754]],
633
597
  'base': np.array(base_l),
634
598
  'reference': '', 'link': ''}
635
599