pyTEMlib 0.2025.12.0__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.
Files changed (92) hide show
  1. build/lib/pyTEMlib/__init__.py +36 -0
  2. build/lib/pyTEMlib/animation.py +667 -0
  3. build/lib/pyTEMlib/atom_tools.py +229 -0
  4. build/lib/pyTEMlib/config_dir.py +43 -0
  5. build/lib/pyTEMlib/crystal_tools.py +1219 -0
  6. build/lib/pyTEMlib/diffraction_plot.py +757 -0
  7. build/lib/pyTEMlib/dynamic_scattering.py +298 -0
  8. build/lib/pyTEMlib/eds_tools.py +901 -0
  9. build/lib/pyTEMlib/eds_xsections.py +268 -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 +275 -0
  17. build/lib/pyTEMlib/file_tools.py +816 -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 +198 -0
  24. build/lib/pyTEMlib/image/image_distortion.py +299 -0
  25. build/lib/pyTEMlib/image/image_fft.py +278 -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 +308 -0
  29. build/lib/pyTEMlib/image/image_window.py +421 -0
  30. build/lib/pyTEMlib/image_tools.py +701 -0
  31. build/lib/pyTEMlib/interactive_image.py +1 -0
  32. build/lib/pyTEMlib/kinematic_scattering.py +1145 -0
  33. build/lib/pyTEMlib/microscope.py +62 -0
  34. build/lib/pyTEMlib/probe_tools.py +928 -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 +431 -0
  39. build/lib/pyTEMlib/version.py +5 -0
  40. build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
  41. pyTEMlib/__init__.py +36 -0
  42. pyTEMlib/animation.py +667 -0
  43. pyTEMlib/atom_tools.py +229 -0
  44. pyTEMlib/config_dir.py +43 -0
  45. pyTEMlib/crystal_tools.py +1219 -0
  46. pyTEMlib/data/k_factors_Bruker_15keV.json +293 -0
  47. pyTEMlib/data/k_factors_Thermo_200keV.json +494 -0
  48. pyTEMlib/data/xrays_X_section_100kV.json +19334 -0
  49. pyTEMlib/data/xrays_X_section_200kV.json +18711 -0
  50. pyTEMlib/data/xrays_X_section_300kV.json +18347 -0
  51. pyTEMlib/data/xrays_X_section_60kV.json +20202 -0
  52. pyTEMlib/diffraction_plot.py +757 -0
  53. pyTEMlib/dynamic_scattering.py +298 -0
  54. pyTEMlib/eds_tools.py +901 -0
  55. pyTEMlib/eds_xsections.py +268 -0
  56. pyTEMlib/eels_tools/__init__.py +44 -0
  57. pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  58. pyTEMlib/eels_tools/eels_database.py +134 -0
  59. pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  60. pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  61. pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  62. pyTEMlib/file_reader.py +275 -0
  63. pyTEMlib/file_tools.py +816 -0
  64. pyTEMlib/get_bote_salvat.py +69 -0
  65. pyTEMlib/graph_tools.py +1153 -0
  66. pyTEMlib/graph_viz.py +599 -0
  67. pyTEMlib/image/__init__.py +37 -0
  68. pyTEMlib/image/image_atoms.py +270 -0
  69. pyTEMlib/image/image_clean.py +198 -0
  70. pyTEMlib/image/image_distortion.py +299 -0
  71. pyTEMlib/image/image_fft.py +278 -0
  72. pyTEMlib/image/image_graph.py +926 -0
  73. pyTEMlib/image/image_registration.py +316 -0
  74. pyTEMlib/image/image_utilities.py +308 -0
  75. pyTEMlib/image/image_window.py +421 -0
  76. pyTEMlib/image_tools.py +701 -0
  77. pyTEMlib/interactive_image.py +1 -0
  78. pyTEMlib/kinematic_scattering.py +1145 -0
  79. pyTEMlib/microscope.py +62 -0
  80. pyTEMlib/probe_tools.py +928 -0
  81. pyTEMlib/sidpy_tools.py +153 -0
  82. pyTEMlib/simulation_tools.py +104 -0
  83. pyTEMlib/test.py +437 -0
  84. pyTEMlib/utilities.py +431 -0
  85. pyTEMlib/version.py +5 -0
  86. pyTEMlib/xrpa_x_sections.py +20976 -0
  87. pytemlib-0.2025.12.0.dist-info/METADATA +100 -0
  88. pytemlib-0.2025.12.0.dist-info/RECORD +92 -0
  89. pytemlib-0.2025.12.0.dist-info/WHEEL +5 -0
  90. pytemlib-0.2025.12.0.dist-info/entry_points.txt +2 -0
  91. pytemlib-0.2025.12.0.dist-info/licenses/LICENSE +21 -0
  92. pytemlib-0.2025.12.0.dist-info/top_level.txt +5 -0
@@ -0,0 +1,298 @@
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,
136
+ wavelength, field_of_view, bandwidth_factor,
137
+ verbose=True):
138
+ """Get propagator function
139
+
140
+ has to be convoluted with wave function after transmission
141
+
142
+ Parameter
143
+ ---------
144
+ size_in_pixel: int
145
+ number of pixels of one axis in square image
146
+ delta_z: float
147
+ distance between layers
148
+ number_layers: int
149
+ number of layers to make a propagator
150
+ wavelength: float
151
+ wavelength of incident electrons
152
+ field_of_view: float
153
+ field of view of image
154
+ bandwidth_factor: float
155
+ relative bandwidth to avoid anti-aliasing
156
+
157
+ Returns
158
+ -------
159
+ propagator: complex numpy array (layers x size_in_pixel x size_in_pixel)
160
+
161
+ """
162
+
163
+ k2max = size_in_pixel / field_of_view / 2. * bandwidth_factor
164
+ print(k2max)
165
+ if verbose:
166
+ print(f"Bandwidth limited to a real space resolution of {1.0 / k2max * 1000} pm")
167
+ print(f" (= {wavelength * k2max * 1000.0:.2f} mrad) for symmetrical anti-aliasing.")
168
+ k2max = k2max * k2max
169
+
170
+ kx, ky = np.mgrid[-size_in_pixel / 2:size_in_pixel / 2,
171
+ -size_in_pixel / 2:size_in_pixel / 2] / field_of_view
172
+ k_square = kx ** 2 + ky ** 2
173
+ k_square[k_square > k2max] = 0 # bandwidth limiting
174
+
175
+ if verbose:
176
+ temp = np.zeros([size_in_pixel, size_in_pixel])
177
+ temp[k_square > 0] = 1
178
+ print(f"Number of symmetrical non-aliasing beams = {temp.sum():.0f}")
179
+
180
+ propagator = np.zeros([number_layers, size_in_pixel, size_in_pixel], dtype=complex)
181
+ for i in range(number_layers):
182
+ propagator[i] = np.exp(-1j * np.pi * wavelength * k_square * delta_z[i])
183
+
184
+ return propagator
185
+
186
+
187
+ def multi_slice(wave, number_of_unit_cell_z, number_layers, transmission, propagator):
188
+ """Multi-Slice Calculation
189
+
190
+ The wave function will be changed iteratively
191
+
192
+ Parameters
193
+ ----------
194
+ wave: complex numpy array (nxn)
195
+ starting wave function
196
+ number_of_unit_cell_z: int
197
+ this gives the thickness in multiples of c lattice parameter
198
+ number_layers: int
199
+ number of layers per unit cell
200
+ transmission: complex numpy array
201
+ transmission function
202
+ propagator: complex numpy array
203
+ propagator function
204
+
205
+ Returns
206
+ -------
207
+ complex numpy array
208
+ """
209
+
210
+ for _ in range(number_of_unit_cell_z):
211
+ for layer in range(number_layers):
212
+ wave = wave * transmission[layer] # transmission - real space
213
+ wave = np.fft.fft2(wave)
214
+ # propagation; propagator is defined in reciprocal space
215
+ wave = wave * propagator[layer]
216
+ wave = np.fft.ifft2(wave) # back to real space
217
+ return wave
218
+
219
+
220
+ def make_chi(theta, phi, aberrations):
221
+ """
222
+ ###
223
+ # Aberration function chi
224
+ ###
225
+ phi and theta are meshgrids of the angles in polar coordinates.
226
+ aberrations is a dictionary with the aberrations coefficients
227
+ Attention: an empty aberration dictionary will give you a perfect aberration
228
+ """
229
+
230
+ chi = np.zeros(theta.shape)
231
+ for n in range(6): # First Sum up to fifth order
232
+ term_first_sum = np.power(theta, n + 1) / (n + 1) # term in first sum
233
+
234
+ second_sum = np.zeros(theta.shape) # second Sum initialized with zeros
235
+ for m in range((n + 1) % 2, n + 2, 2):
236
+ # print(n, m)
237
+
238
+ if m > 0:
239
+ # Set non existent aberrations coefficient to zero
240
+ if f'C{n}{m}a' not in aberrations:
241
+ aberrations[f'C{n}{m}a'] = 0.
242
+ if f'C{n}{m}b' not in aberrations:
243
+ aberrations[f'C{n}{m}b'] = 0.
244
+
245
+ # term in second sum
246
+ second_sum = second_sum + aberrations[f'C{n}{m}a'] * np.cos(m * phi) + aberrations[
247
+ f'C{n}{m}b'] * np.sin(m * phi)
248
+ else:
249
+ # Set non existent aberrations coefficient to zero
250
+ if f'C{n}{m}' not in aberrations:
251
+ aberrations[f'C{n}{m}'] = 0.
252
+ # term in second sum
253
+ second_sum = second_sum + aberrations[f'C{n}{m}']
254
+ chi = chi + term_first_sum * second_sum * 2 * np.pi / aberrations['wavelength']
255
+
256
+ return chi
257
+
258
+
259
+ def objective_lens_function(ab, nx, ny, field_of_view, aperture_size=10):
260
+ """Objective len function to be convoluted with exit wave to derive image function
261
+
262
+ Parameter:
263
+ ----------
264
+ ab: dict
265
+ aberrations in nm should at least contain defocus (C10), and spherical aberration (C30)
266
+ nx: int
267
+ number of pixel in x direction
268
+ ny: int
269
+ number of pixel in y direction
270
+ field_of_view: float
271
+ field of view of potential
272
+ wavelength: float
273
+ wavelength in nm
274
+ aperture_size: float
275
+ aperture size in 1/nm
276
+
277
+ Returns:
278
+ --------
279
+ object function: numpy array (nx x ny)
280
+ extent: list
281
+ """
282
+
283
+ wavelength = ab['wavelength']
284
+ # Reciprocal plane in 1/nm
285
+ dk = 1 / field_of_view
286
+ t_xv, t_yv = np.mgrid[int(-nx / 2):int(nx / 2), int(-ny / 2):int(ny / 2)] * dk
287
+
288
+ # define reciprocal plane in angles
289
+ phi = np.arctan2(t_yv, t_xv)
290
+ theta = np.arctan2(np.sqrt(t_xv ** 2 + t_yv ** 2), 1 / wavelength)
291
+
292
+ mask = theta < aperture_size * wavelength
293
+
294
+ # calculate chi
295
+ chi = make_chi(theta, phi, ab)
296
+
297
+ extent = [-nx / 2 * dk, nx / 2 * dk, -nx / 2 * dk, nx / 2 * dk]
298
+ return np.exp(-1j * chi) * mask, extent