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.

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 -928
  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.2.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.2.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.2.dist-info/RECORD +0 -38
  92. pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
  93. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
  94. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,293 @@
1
+ """
2
+ Dynamic Scattering Library for Multi-Slice Calculations
3
+
4
+ author: Gerd Duscher
5
+ """
6
+
7
+ import numpy as np
8
+ import scipy.constants
9
+ import scipy.special
10
+
11
+ import pyTEMlib.kinematic_scattering as ks # kinematic scattering Library
12
+
13
+
14
+ def potential_1dim(element, r):
15
+ """ Calculates the projected potential of an atom of element
16
+
17
+ The projected potential will be in units of V nm^2,
18
+ however, internally we will use Angstrom instead of nm!
19
+ The basis for these calculations are the atomic form factors of Kirkland 2𝑛𝑑 edition
20
+ following the equation in Appendix C page 252.
21
+
22
+ Parameter
23
+ ---------
24
+ element: str
25
+ name of 'element
26
+ r: numpy array [nxn]
27
+ impact parameters (distances from atom position) in nm
28
+
29
+ Returns
30
+ -------
31
+ numpy array (nxn)
32
+ projected potential in units of V nm^2
33
+ """
34
+
35
+ # get elementary constants
36
+ a0 = scipy.constants.value('Bohr radius') * 1e10 # in Angstrom
37
+ rydberg_div_e = scipy.constants.value('Rydberg constant times hc in eV') # in V
38
+ e0 = 2 * rydberg_div_e * scipy.constants.value('Bohr radius') * 1e10 # now in V A
39
+
40
+ pre_factor = 2 * np.pi ** 2 * a0 * e0
41
+
42
+ param = ks.electronFF[element] # parametrized form factors
43
+ f_lorentz = r * 0 # Lorentzian term
44
+ f_gauss = r * 0 # Gaussian term
45
+ for i in range(3):
46
+ f_lorentz += param['fa'][i] * scipy.special.k0(2 * np.pi * r * np.sqrt(param['fb'][i]))
47
+ f_gauss += param['fc'][i] / param['fd'][i] * np.exp(-np.pi ** 2 * r ** 2 / param['fd'][i])
48
+ f_lorentz[0, 0] = f_lorentz[0, 1]
49
+ # / 100 is conversion from V Angstrom^2 to V nm^2
50
+ return pre_factor * (2 * f_lorentz + f_gauss) # V Angstrom^2
51
+
52
+
53
+ def potential_2dim(element, nx, ny, n_cell_x, n_cell_y, lattice_parameter, base):
54
+ """Make a super-cell with potentials
55
+
56
+ Limitation is that we only place atom potential with single pixel resolution
57
+ """
58
+ n_cell_x = int(2 ** np.log2(n_cell_x))
59
+ n_cell_y = int(2 ** np.log2(n_cell_y))
60
+
61
+ pixel_size = lattice_parameter / (nx / n_cell_x)
62
+
63
+ a_nx = a_ny = int(1 / pixel_size)
64
+ x, y = np.mgrid[0:a_nx, 0:a_ny] * pixel_size
65
+ a = int(nx / n_cell_x)
66
+ r = x ** 2 + y ** 2
67
+
68
+ atom_potential = potential_1dim(element, r)
69
+
70
+ potential = np.zeros([nx, ny])
71
+
72
+ atom_potential_corner = np.zeros([nx, ny])
73
+ atom_potential_corner[0:a_nx, 0:a_ny] = atom_potential
74
+ atom_potential_corner[nx - a_nx:, 0:a_ny] = np.flip(atom_potential, axis=0)
75
+ atom_potential_corner[0:a_nx, ny - a_ny:] = np.flip(atom_potential, axis=1)
76
+ atom_potential_corner[nx - a_nx:, ny - a_ny:] = np.flip(np.flip(atom_potential, axis=0), axis=1)
77
+
78
+ unit_cell_base = np.array(base) * a
79
+ unit_cell_base = np.array(unit_cell_base, dtype=int)
80
+
81
+ for pos in unit_cell_base:
82
+ potential = potential + np.roll(atom_potential_corner, shift=np.array(pos), axis=[0, 1])
83
+
84
+ for column in range(int(np.log2(n_cell_x))):
85
+ potential = potential + np.roll(potential, shift=2 ** column * a, axis=1)
86
+ for row in range(int(np.log2(n_cell_y))):
87
+ potential = potential + np.roll(potential, shift=2 ** row * a, axis=0)
88
+
89
+ return potential
90
+
91
+
92
+ def interaction_parameter(acceleration_voltage):
93
+ """Calculates interaction parameter sigma
94
+
95
+ Parameter
96
+ ---------
97
+ acceleration_voltage: float
98
+ acceleration voltage in volt
99
+
100
+ Returns
101
+ -------
102
+ interaction parameter: float
103
+ interaction parameter (dimensionless)
104
+ """
105
+ e0 = 510998.95 # m_0 c^2 in eV
106
+
107
+ wavelength = ks.get_wavelength(acceleration_voltage)
108
+ e = acceleration_voltage
109
+
110
+ return 2. * np.pi / (wavelength * e) * (e0 + e) / (2. * e0 + e)
111
+
112
+
113
+ def get_transmission(potential, acceleration_voltage):
114
+ """ Get transmission function
115
+
116
+ has to be multiplied in real space with wave function
117
+
118
+ Parameter
119
+ ---------
120
+ potential: numpy array (nxn)
121
+ potential of a layer
122
+ acceleration_voltage: float
123
+ acceleration voltage in V
124
+
125
+ Returns
126
+ -------
127
+ complex numpy array (nxn)
128
+ """
129
+
130
+ sigma = interaction_parameter(acceleration_voltage)
131
+
132
+ return np.exp(1j * sigma * potential)
133
+
134
+
135
+ def get_propagator(size_in_pixel, delta_z, number_layers, wavelength, field_of_view, bandwidth_factor, verbose=True):
136
+ """Get propagator function
137
+
138
+ has to be convoluted with wave function after transmission
139
+
140
+ Parameter
141
+ ---------
142
+ size_in_pixel: int
143
+ number of pixels of one axis in square image
144
+ delta_z: float
145
+ distance between layers
146
+ number_layers: int
147
+ number of layers to make a propagator
148
+ wavelength: float
149
+ wavelength of incident electrons
150
+ field_of_view: float
151
+ field of view of image
152
+ bandwidth_factor: float
153
+ relative bandwidth to avoid anti-aliasing
154
+
155
+ Returns
156
+ -------
157
+ propagator: complex numpy array (layers x size_in_pixel x size_in_pixel)
158
+
159
+ """
160
+
161
+ k2max = size_in_pixel / field_of_view / 2. * bandwidth_factor
162
+ print(k2max)
163
+ if verbose:
164
+ print(f"Bandwidth limited to a real space resolution of {1.0 / k2max * 1000} pm")
165
+ print(f" (= {wavelength * k2max * 1000.0:.2f} mrad) for symmetrical anti-aliasing.")
166
+ k2max = k2max * k2max
167
+
168
+ kx, ky = np.mgrid[-size_in_pixel / 2:size_in_pixel / 2, -size_in_pixel / 2:size_in_pixel / 2] / field_of_view
169
+ k_square = kx ** 2 + ky ** 2
170
+ k_square[k_square > k2max] = 0 # bandwidth limiting
171
+
172
+ if verbose:
173
+ temp = np.zeros([size_in_pixel, size_in_pixel])
174
+ temp[k_square > 0] = 1
175
+ print(f"Number of symmetrical non-aliasing beams = {temp.sum():.0f}")
176
+
177
+ propagator = np.zeros([number_layers, size_in_pixel, size_in_pixel], dtype=complex)
178
+ for i in range(number_layers):
179
+ propagator[i] = np.exp(-1j * np.pi * wavelength * k_square * delta_z[i])
180
+
181
+ return propagator
182
+
183
+
184
+ def multi_slice(wave, number_of_unit_cell_z, number_layers, transmission, propagator):
185
+ """Multi-Slice Calculation
186
+
187
+ The wave function will be changed iteratively
188
+
189
+ Parameters
190
+ ----------
191
+ wave: complex numpy array (nxn)
192
+ starting wave function
193
+ number_of_unit_cell_z: int
194
+ this gives the thickness in multiples of c lattice parameter
195
+ number_layers: int
196
+ number of layers per unit cell
197
+ transmission: complex numpy array
198
+ transmission function
199
+ propagator: complex numpy array
200
+ propagator function
201
+
202
+ Returns
203
+ -------
204
+ complex numpy array
205
+ """
206
+
207
+ for i in range(number_of_unit_cell_z):
208
+ for layer in range(number_layers):
209
+ wave = wave * transmission[layer] # transmission - real space
210
+ wave = np.fft.fft2(wave)
211
+ wave = wave * propagator[layer] # propagation; propagator is defined in reciprocal space
212
+ wave = np.fft.ifft2(wave) # back to real space
213
+ return wave
214
+
215
+
216
+ def make_chi(theta, phi, aberrations):
217
+ """
218
+ ###
219
+ # Aberration function chi
220
+ ###
221
+ phi and theta are meshgrids of the angles in polar coordinates.
222
+ aberrations is a dictionary with the aberrations coefficients
223
+ Attention: an empty aberration dictionary will give you a perfect aberration
224
+ """
225
+
226
+ chi = np.zeros(theta.shape)
227
+ for n in range(6): # First Sum up to fifth order
228
+ term_first_sum = np.power(theta, n + 1) / (n + 1) # term in first sum
229
+
230
+ second_sum = np.zeros(theta.shape) # second Sum initialized with zeros
231
+ for m in range((n + 1) % 2, n + 2, 2):
232
+ # print(n, m)
233
+
234
+ if m > 0:
235
+ if f'C{n}{m}a' not in aberrations: # Set non existent aberrations coefficient to zero
236
+ aberrations[f'C{n}{m}a'] = 0.
237
+ if f'C{n}{m}b' not in aberrations:
238
+ aberrations[f'C{n}{m}b'] = 0.
239
+
240
+ # term in second sum
241
+ second_sum = second_sum + aberrations[f'C{n}{m}a'] * np.cos(m * phi) + aberrations[
242
+ f'C{n}{m}b'] * np.sin(m * phi)
243
+ else:
244
+ if f'C{n}{m}' not in aberrations: # Set non existent aberrations coefficient to zero
245
+ aberrations[f'C{n}{m}'] = 0.
246
+
247
+ # term in second sum
248
+ second_sum = second_sum + aberrations[f'C{n}{m}']
249
+ chi = chi + term_first_sum * second_sum * 2 * np.pi / aberrations['wavelength']
250
+
251
+ return chi
252
+
253
+
254
+ def objective_lens_function(ab, nx, ny, field_of_view, aperture_size=10):
255
+ """Objective len function to be convoluted with exit wave to derive image function
256
+
257
+ Parameter:
258
+ ----------
259
+ ab: dict
260
+ aberrations in nm should at least contain defocus (C10), and spherical aberration (C30)
261
+ nx: int
262
+ number of pixel in x direction
263
+ ny: int
264
+ number of pixel in y direction
265
+ field_of_view: float
266
+ field of view of potential
267
+ wavelength: float
268
+ wavelength in nm
269
+ aperture_size: float
270
+ aperture size in 1/nm
271
+
272
+ Returns:
273
+ --------
274
+ object function: numpy array (nx x ny)
275
+ extent: list
276
+ """
277
+
278
+ wavelength = ab['wavelength']
279
+ # Reciprocal plane in 1/nm
280
+ dk = 1 / field_of_view
281
+ t_xv, t_yv = np.mgrid[int(-nx / 2):int(nx / 2), int(-ny / 2):int(ny / 2)] * dk
282
+
283
+ # define reciprocal plane in angles
284
+ phi = np.arctan2(t_yv, t_xv)
285
+ theta = np.arctan2(np.sqrt(t_xv ** 2 + t_yv ** 2), 1 / wavelength)
286
+
287
+ mask = theta < aperture_size * wavelength
288
+
289
+ # calculate chi
290
+ chi = make_chi(theta, phi, ab)
291
+
292
+ extent = [-nx / 2 * dk, nx / 2 * dk, -nx / 2 * dk, nx / 2 * dk]
293
+ return np.exp(-1j * chi) * mask, extent