pyTEMlib 0.2025.4.2__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.
- build/lib/pyTEMlib/__init__.py +33 -0
- build/lib/pyTEMlib/animation.py +640 -0
- build/lib/pyTEMlib/atom_tools.py +238 -0
- build/lib/pyTEMlib/config_dir.py +31 -0
- build/lib/pyTEMlib/crystal_tools.py +1219 -0
- build/lib/pyTEMlib/diffraction_plot.py +756 -0
- build/lib/pyTEMlib/dynamic_scattering.py +293 -0
- build/lib/pyTEMlib/eds_tools.py +826 -0
- build/lib/pyTEMlib/eds_xsections.py +432 -0
- build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
- build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
- build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
- build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
- build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
- build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
- build/lib/pyTEMlib/file_reader.py +274 -0
- build/lib/pyTEMlib/file_tools.py +811 -0
- build/lib/pyTEMlib/get_bote_salvat.py +69 -0
- build/lib/pyTEMlib/graph_tools.py +1153 -0
- build/lib/pyTEMlib/graph_viz.py +599 -0
- build/lib/pyTEMlib/image/__init__.py +37 -0
- build/lib/pyTEMlib/image/image_atoms.py +270 -0
- build/lib/pyTEMlib/image/image_clean.py +197 -0
- build/lib/pyTEMlib/image/image_distortion.py +299 -0
- build/lib/pyTEMlib/image/image_fft.py +277 -0
- build/lib/pyTEMlib/image/image_graph.py +926 -0
- build/lib/pyTEMlib/image/image_registration.py +316 -0
- build/lib/pyTEMlib/image/image_utilities.py +309 -0
- build/lib/pyTEMlib/image/image_window.py +421 -0
- build/lib/pyTEMlib/image_tools.py +699 -0
- build/lib/pyTEMlib/interactive_image.py +1 -0
- build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
- build/lib/pyTEMlib/microscope.py +61 -0
- build/lib/pyTEMlib/probe_tools.py +906 -0
- build/lib/pyTEMlib/sidpy_tools.py +153 -0
- build/lib/pyTEMlib/simulation_tools.py +104 -0
- build/lib/pyTEMlib/test.py +437 -0
- build/lib/pyTEMlib/utilities.py +314 -0
- build/lib/pyTEMlib/version.py +5 -0
- build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
- pyTEMlib/__init__.py +25 -3
- pyTEMlib/animation.py +31 -22
- pyTEMlib/atom_tools.py +29 -34
- pyTEMlib/config_dir.py +2 -28
- pyTEMlib/crystal_tools.py +129 -165
- pyTEMlib/eds_tools.py +559 -342
- pyTEMlib/eds_xsections.py +432 -0
- pyTEMlib/eels_tools/__init__.py +44 -0
- pyTEMlib/eels_tools/core_loss_tools.py +751 -0
- pyTEMlib/eels_tools/eels_database.py +134 -0
- pyTEMlib/eels_tools/low_loss_tools.py +655 -0
- pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
- pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
- pyTEMlib/file_reader.py +274 -0
- pyTEMlib/file_tools.py +260 -1130
- pyTEMlib/get_bote_salvat.py +69 -0
- pyTEMlib/graph_tools.py +101 -174
- pyTEMlib/graph_viz.py +150 -0
- pyTEMlib/image/__init__.py +37 -0
- pyTEMlib/image/image_atoms.py +270 -0
- pyTEMlib/image/image_clean.py +197 -0
- pyTEMlib/image/image_distortion.py +299 -0
- pyTEMlib/image/image_fft.py +277 -0
- pyTEMlib/image/image_graph.py +926 -0
- pyTEMlib/image/image_registration.py +316 -0
- pyTEMlib/image/image_utilities.py +309 -0
- pyTEMlib/image/image_window.py +421 -0
- pyTEMlib/image_tools.py +154 -928
- pyTEMlib/kinematic_scattering.py +1 -1
- pyTEMlib/probe_tools.py +1 -1
- pyTEMlib/test.py +437 -0
- pyTEMlib/utilities.py +314 -0
- pyTEMlib/version.py +2 -3
- pyTEMlib/xrpa_x_sections.py +14 -10
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
- pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
- pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
- pyTEMlib/core_loss_widget.py +0 -721
- pyTEMlib/eels_dialog.py +0 -754
- pyTEMlib/eels_dialog_utilities.py +0 -1199
- pyTEMlib/eels_tools.py +0 -2359
- pyTEMlib/file_tools_qt.py +0 -193
- pyTEMlib/image_dialog.py +0 -158
- pyTEMlib/image_dlg.py +0 -146
- pyTEMlib/info_widget.py +0 -1086
- pyTEMlib/info_widget3.py +0 -1120
- pyTEMlib/low_loss_widget.py +0 -479
- pyTEMlib/peak_dialog.py +0 -1129
- pyTEMlib/peak_dlg.py +0 -286
- pytemlib-0.2025.4.2.dist-info/RECORD +0 -38
- pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
- {pytemlib-0.2025.4.2.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
|
-
|
|
32
|
-
try:
|
|
33
|
-
import spglib
|
|
34
|
-
except ModuleNotFoundError:
|
|
35
|
-
_spglib_present = False
|
|
33
|
+
import scipy.sparse
|
|
36
34
|
|
|
37
|
-
|
|
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
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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(
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
print('
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
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',
|
|
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],
|
|
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,
|
|
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,
|
|
292
|
-
'
|
|
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,
|
|
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
|
|
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],
|
|
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],
|
|
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'],
|
|
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,
|
|
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'],
|
|
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,
|
|
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'],
|
|
389
|
+
atoms = ase.Atoms(tags['elements'], cell=tags['unit_cell'],
|
|
390
|
+
scaled_positions=tags['base'])
|
|
440
391
|
|
|
441
392
|
else:
|
|
442
|
-
|
|
393
|
+
raise ValueError("Symmetry of structure is wrong")
|
|
443
394
|
|
|
444
|
-
atoms.info = {'structure': {'reference': tags['reference'],
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
522
|
-
'
|
|
523
|
-
'
|
|
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,
|
|
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':
|
|
628
|
-
'c':
|
|
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': [[
|
|
631
|
-
[np.cos(120
|
|
632
|
-
[0., 0.,
|
|
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
|
|