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,906 @@
1
+ """Functions to calculate electron probe"""
2
+ import numpy as np
3
+ import pyTEMlib.image_tools
4
+ import scipy.ndimage as ndimage
5
+ import skimage
6
+
7
+ get_wavelength = pyTEMlib.image_tools.get_wavelength
8
+
9
+
10
+
11
+ def make_gauss(size_x, size_y, width=1.0, x0=0.0, y0=0.0, intensity=1.0):
12
+ """Make a Gaussian shaped probe """
13
+ size_x = size_x / 2
14
+ size_y = size_y / 2
15
+ x, y = np.mgrid[-size_x:size_x, -size_y:size_y]
16
+ g = np.exp(-((x - x0) ** 2 + (y - y0) ** 2) / 2.0 / width ** 2)
17
+ probe = g / (g.sum()+ 0.00001) * intensity
18
+
19
+ return probe
20
+
21
+
22
+ def make_lorentz(size_x, size_y, gamma=1.0, x0=0., y0=0., intensity=1.):
23
+ """Make a Lorentzian shaped probe """
24
+
25
+ size_x = np.floor(size_x / 2)
26
+ size_y = np.floor(size_y / 2)
27
+ x, y = np.mgrid[-size_x:size_x, -size_y:size_y]
28
+ g = gamma / (2 * np.pi) / np.power(((x - x0) ** 2 + (y - y0) ** 2 + gamma ** 2), 1.5)
29
+ probe = g / g.sum() * intensity
30
+
31
+ return probe
32
+
33
+
34
+ def zero_loss_peak_weight():
35
+ # US100 zero_loss peak for Cc of aberrations
36
+ x = np.linspace(-0.5, 0.9, 29)
37
+ y = [0.0143, 0.0193, 0.0281, 0.0440, 0.0768, 0.1447, 0.2785, 0.4955, 0.7442, 0.9380, 1.0000, 0.9483, 0.8596,
38
+ 0.7620, 0.6539, 0.5515, 0.4478, 0.3500, 0.2683, 0.1979, 0.1410, 0.1021, 0.0752, 0.0545, 0.0401, 0.0300,
39
+ 0.0229, 0.0176, 0.0139]
40
+
41
+ return x, y
42
+
43
+
44
+ def make_chi(phi, theta, aberrations):
45
+ maximum_aberration_order = 5
46
+ chi = np.zeros(theta.shape)
47
+ for n in range(maximum_aberration_order + 1): # First Sum up to fifth order
48
+ term_first_sum = np.power(theta, n + 1) / (n + 1) # term in first sum
49
+
50
+ second_sum = np.zeros(theta.shape) # second Sum initialized with zeros
51
+ for m in range((n + 1) % 2, n + 2, 2):
52
+ if m > 0:
53
+ if f'C{n}{m}a' not in aberrations: # Set non existent aberrations coefficient to zero
54
+ aberrations[f'C{n}{m}a'] = 0.
55
+ if f'C{n}{m}b' not in aberrations:
56
+ aberrations[f'C{n}{m}b'] = 0.
57
+
58
+ # term in second sum
59
+ second_sum = second_sum + aberrations[f'C{n}{m}a'] * np.cos(m * phi) + aberrations[
60
+ f'C{n}{m}b'] * np.sin(m * phi)
61
+ else:
62
+ if f'C{n}{m}' not in aberrations: # Set non existent aberrations coefficient to zero
63
+ aberrations[f'C{n}{m}'] = 0.
64
+
65
+ # term in second sum
66
+ second_sum = second_sum + aberrations[f'C{n}{m}']
67
+ chi = chi + term_first_sum * second_sum * 2 * np.pi / aberrations['wavelength']
68
+
69
+ return chi
70
+
71
+
72
+ def get_chi(ab, size_x, size_y, verbose=False):
73
+ """ Get aberration function chi without defocus spread
74
+
75
+ # Internally reciprocal lattice vectors in 1/nm or rad.
76
+ # All calculations of chi in angles.
77
+ # All aberration coefficients in nm
78
+ """
79
+ aperture_angle = ab['convergence_angle'] / 1000.0 # in rad
80
+
81
+ wavelength = pyTEMlib.image_tools.get_wavelength(ab['acceleration_voltage'])
82
+ if verbose:
83
+ print(f"Acceleration voltage {ab['acceleration_voltage'] / 1000:}kV => wavelength {wavelength * 1000.:.2f}pm")
84
+
85
+ ab['wavelength'] = wavelength
86
+
87
+ # Reciprocal plane in 1/nm
88
+ dk = 1 / ab['FOV']
89
+ k_x = np.array(dk * (-size_x / 2. + np.arange(size_x)))
90
+ k_y = np.array(dk * (-size_y / 2. + np.arange(size_y)))
91
+ t_x_v, t_y_v = np.meshgrid(k_x, k_y)
92
+
93
+ # define reciprocal plane in angles
94
+ phi = np.arctan2(t_x_v, t_y_v)
95
+ theta = np.arctan2(np.sqrt(t_x_v ** 2 + t_y_v ** 2), 1 / wavelength)
96
+
97
+ # calculate chi
98
+ chi = make_chi(phi, theta, ab)
99
+
100
+ # Aperture function
101
+ print(aperture_angle)
102
+ mask = theta >= aperture_angle
103
+
104
+ aperture = np.ones((size_x, size_y), dtype=float)
105
+ aperture[mask] = 0.
106
+
107
+ return chi, aperture
108
+
109
+
110
+
111
+ def print_aberrations(ab):
112
+ from IPython.display import HTML, display
113
+ output = '<html><body>'
114
+ output += f"Aberrations [nm] for acceleration voltage: {ab['acceleration_voltage'] / 1e3:.0f} kV"
115
+ output += '<table>'
116
+ output += f"<tr><td> C10 </td><td> {ab['C10']:.1f} </tr>"
117
+ output += f"<tr><td> C12a (A1) </td><td> {ab['C12a']:20.1f} <td> C12b (A1) </td><td> {ab['C12b']:20.1f} </tr>"
118
+ output += f"<tr><td> C21a (B2) </td><td> {ab['C21a']:.1f} <td> C21b (B2)</td><td> {ab['C21b']:.1f} "
119
+ output += f" <td> C23a (A2) </td><td> {ab['C23a']:.1f} <td> C23b (A2) </td><td> {ab['C23b']:.1f} </tr>"
120
+ output += f"<tr><td> C30 </td><td> {ab['C30']:.1f} </tr>"
121
+ output += f"<tr><td> C32a (S3) </td><td> {ab['C32a']:20.1f} <td> C32b (S3)</td><td> {ab['C32b']:20.1f} "
122
+ output += f"<td> C34a (A3) </td><td> {ab['C34a']:20.1f} <td> C34b (A3) </td><td> {ab['C34b']:20.1f} </tr>"
123
+ output += f"<tr><td> C41a (B4) </td><td> {ab['C41a']:.3g} <td> C41b (B4) </td><td> {ab['C41b']:.3g} "
124
+ output += f" <td> C43a (D4) </td><td> {ab['C43a']:.3g} <td> C43b (D4) </td><td> {ab['C41b']:.3g} "
125
+ output += f" <td> C45a (A4) </td><td> {ab['C45a']:.3g} <td> C45b (A4)</td><td> {ab['C45b']:.3g} </tr>"
126
+ output += f"<tr><td> C50 </td><td> {ab['C50']:.3g} </tr>"
127
+ output += f"<tr><td> C52a </td><td> {ab['C52a']:20.1f} <td> C52b </td><td> {ab['C52b']:20.1f} "
128
+ output += f"<td> C54a </td><td> {ab['C54a']:20.1f} <td> C54b </td><td> {ab['C54b']:20.1f} "
129
+ output += f"<td> C56a </td><td> {ab['C56a']:20.1f} <td> C56b </td><td> {ab['C56b']:20.1f} </tr>"
130
+ output += f"<tr><td> Cc </td><td> {ab['Cc']:.3g} </tr>"
131
+
132
+ output += '</table></body></html>'
133
+
134
+ display(HTML(output))
135
+
136
+
137
+
138
+ def print_aberrations_polar(ab):
139
+ from IPython.display import HTML, display
140
+
141
+ ab['C12_r'], ab['C12_phi'] = cart2pol(ab['C12a'], ab['C12b'])
142
+ ab['C21_r'], ab['C21_phi'] = cart2pol(ab['C21a'], ab['C21b'])
143
+ ab['C23_r'], ab['C23_phi'] = cart2pol(ab['C23a'], ab['C23b'])
144
+ ab['C32_r'], ab['C32_phi'] = cart2pol(ab['C32a'], ab['C32b'])
145
+ ab['C34_r'], ab['C34_phi'] = cart2pol(ab['C34a'], ab['C34b'])
146
+ ab['C41_r'], ab['C41_phi'] = cart2pol(ab['C41a'], ab['C41b'])
147
+ ab['C43_r'], ab['C43_phi'] = cart2pol(ab['C43a'], ab['C43b'])
148
+ ab['C45_r'], ab['C45_phi'] = cart2pol(ab['C45a'], ab['C45b'])
149
+ ab['C52_r'], ab['C52_phi'] = cart2pol(ab['C52a'], ab['C52b'])
150
+ ab['C54_r'], ab['C54_phi'] = cart2pol(ab['C54a'], ab['C54b'])
151
+ ab['C56_r'], ab['C56_phi'] = cart2pol(ab['C56a'], ab['C56b'])
152
+
153
+ output = '<html><body>'
154
+ output += f"Aberrations [nm] for acceleration voltage: {ab['acceleration_voltage'] / 1e3:.0f} kV"
155
+ output += '<table>'
156
+ output += f"<tr><td> C10 </td><td> {ab['C10']:.1f} </tr>"
157
+ output += f"<tr><td> C12(A1): r </td><td> {ab['C12_r']:20.1f} <td> φ </td><td> {ab['C12_phi']:20.1f} </tr>"
158
+ output += f"<tr><td> C21a (B2): r</td><td> {ab['C21_r']:20.1f} <td> φ </td><td> {ab['C21_phi']:20.1f} "
159
+ output += f" <td> C23a (A2) </td><td> {ab['C23_r']:20.1f} <td> φ </td><td> {ab['C23_phi']:20.1f} </tr>"
160
+ output += f"<tr><td> C30 </td><td> {ab['C30']:.1f} </tr>"
161
+ output += f"<tr><td> C32 (S3) </td><td> {ab['C32_r']:20.1f} <td> φ </td><td> {ab['C32_phi']:20.1f} "
162
+ output += f"<td> C34a (A3) </td><td> {ab['C34a']:20.1f} <td> φ </td><td> {ab['C34_phi']:20.1f} </tr>"
163
+ output += f"<tr><td> C41 (B4) </td><td> {ab['C41_r']:.3g} <td> φ </td><td> {ab['C41_phi']:20.1f} "
164
+ output += f" <td> C43 (D4) </td><td> {ab['C43_r']:.3g} <td> φ (D4) </td><td> {ab['C43_phi']:20.1f} "
165
+ output += f" <td> C45 (A4) </td><td> {ab['C45_r']:.3g} <td> φ (A4)</td><td> {ab['C45_phi']:20.1f} </tr>"
166
+ output += f"<tr><td> C50 </td><td> {ab['C50']:.3g} </tr>"
167
+ output += f"<tr><td> C52 </td><td> {ab['C52a']:.3g} <td> φ </td><td> {ab['C52_phi']:20.1f} "
168
+ output += f"<td> C54 </td><td> {ab['C54_r']:.3g} <td> C54 φ </td><td> {ab['C54_phi']:.1f} "
169
+ output += f"<td> C56 </td><td> {ab['C56_r']:.3g} <td> C56 </td><td> {ab['C56_phi']:.1f} </tr>"
170
+ output += f"<tr><td> Cc </td><td> {ab['Cc']:.3g} </tr>"
171
+
172
+ output += '</table></body></html>'
173
+
174
+ display(HTML(output))
175
+
176
+
177
+ def pol2cart(rho, theta):
178
+ x = rho * np.cos(theta)
179
+ y = rho * np.sin(theta)
180
+ return x, y
181
+
182
+ def ceos_to_nion(ab):
183
+ aberrations = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
184
+ 'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
185
+ 'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
186
+ 'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0., 'C70': 0.}
187
+ aberrations['acceleration_voltage'] = 200000
188
+ for key in ab.keys():
189
+ if key == 'C1':
190
+ aberrations['C10'] = ab['C10']
191
+ elif key == 'A1-a':
192
+ x, y = pol2cart(ab['A1-a'], ab['A1-p'])
193
+ aberrations['C12a'] = x
194
+ aberrations['C12b'] = y
195
+ elif key == 'B2-a':
196
+ x, y = pol2cart(ab['B2-a'], ab['B2-p'])
197
+ aberrations['C21a'] = 3 * x
198
+ aberrations['C21b'] = 3 * y
199
+ elif key == 'A2-a':
200
+ x, y = pol2cart(ab['A2-a'], ab['A2-p'])
201
+ aberrations['C23a'] = x
202
+ aberrations['C23b'] = y
203
+ elif key == 'C3':
204
+ aberrations['C30'] = ab['C3']
205
+ elif key == 'S3-a':
206
+ x, y = pol2cart(ab['S3-a'], ab['S3-p'])
207
+ aberrations['C32a'] = 4 * x
208
+ aberrations['C32b'] = 4 * y
209
+ elif key == 'A3-a':
210
+ x, y = pol2cart(ab['A3-a'], ab['A3-p'])
211
+ aberrations['C34a'] = x
212
+ aberrations['C34b'] = y
213
+ elif key == 'B4-a':
214
+ x, y = pol2cart(ab['B4-a'], ab['B4-p'])
215
+ aberrations['C41a'] = 4 * x
216
+ aberrations['C41b'] = 4 * y
217
+ elif key == 'D4-a':
218
+ x, y = pol2cart(ab['D4-a'], ab['D4-p'])
219
+ aberrations['C43a'] = 4 * x
220
+ aberrations['C43b'] = 4 * y
221
+ elif key == 'A4-a':
222
+ x, y = pol2cart(ab['A4-a'], ab['A4-p'])
223
+ aberrations['C45a'] = x
224
+ aberrations['C45b'] = y
225
+ elif key == 'C5':
226
+ aberrations['C50'] = ab['C5']
227
+ elif key == 'A5-a':
228
+ x, y = pol2cart(ab['A5-a'], ab['A5-p'])
229
+ aberrations['C56a'] = x
230
+ aberrations['C56b'] = y
231
+ return aberrations
232
+
233
+ def ceos_carth_to_nion(ab):
234
+ aberrations = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
235
+ 'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
236
+ 'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
237
+ 'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0., 'C70': 0.}
238
+ aberrations['acceleration_voltage'] = 200000
239
+ for key in ab.keys():
240
+ if key == 'C1':
241
+ aberrations['C10'] = ab['C1'][0]*1e9
242
+ elif key == 'A1':
243
+ aberrations['C12a'] = ab['A1'][0]*1e9
244
+ aberrations['C12b'] = ab['A1'][1]*1e9
245
+ elif key == 'B2':
246
+ print('B2', ab['B2'])
247
+ aberrations['C21a'] = 3 * ab['B2'][0]*1e9
248
+ aberrations['C21b'] = 3 * ab['B2'][1]*1e9
249
+ elif key == 'A2':
250
+ aberrations['C23a'] = ab['A2'][0]*1e9
251
+ aberrations['C23b'] = ab['A2'][1]*1e9
252
+ elif key == 'C3':
253
+ aberrations['C30'] = ab['C3'][0]*1e9
254
+ elif key == 'S3':
255
+ aberrations['C32a'] = 4 * ab['S3'][0]*1e9
256
+ aberrations['C32b'] = 4 * ab['S3'][1]*1e9
257
+ elif key == 'A3':
258
+ aberrations['C34a'] = ab['A3'][0]*1e9
259
+ aberrations['C34b'] = ab['A3'][1]*1e9
260
+ elif key == 'B4':
261
+ aberrations['C41a'] = 4 * ab['B4'][0]*1e9
262
+ aberrations['C41b'] = 4 * ab['B4'][1]*1e9
263
+ elif key == 'D4':
264
+ aberrations['C43a'] = 4 * ab['D4'][0]*1e9
265
+ aberrations['C43b'] = 4 * ab['D4'][1]*1e9
266
+ elif key == 'A4':
267
+ aberrations['C45a'] = ab['A4'][0]*1e9
268
+ aberrations['C45b'] = ab['A4'][1]*1e9
269
+ elif key == 'C5':
270
+ aberrations['C50'] = ab['C5'][0]*1e9
271
+ elif key == 'A5':
272
+ aberrations['C56a'] = ab['A5'][0]*1e9
273
+ aberrations['C56b'] = ab['A5'][1]*1e9
274
+
275
+
276
+ return aberrations
277
+
278
+ def cart2pol(x, y):
279
+ theta = np.arctan2(y, x)
280
+ rho = np.hypot(x, y)
281
+ return theta, rho
282
+
283
+ def nion_to_ceos(ab):
284
+ aberrations = {'C1': 0, 'A1-a': 0, 'A1-b': 0, 'B2-a': 0, 'B2-p': 0, 'A2-a': 0, 'A2-p': 0, 'C3': 0.,
285
+ 'S3-a': 0., 'S3-p': -0., 'A3-a': 0., 'A3-p': 0., 'B4-a': 0., 'B4-p': -0., 'D4-a': 0.,
286
+ 'D4-p': -0., 'A4-s': -0., 'A4-p': -0., 'C5': 0., 'A5-a': -0., 'A5-p': 0.}
287
+ aberrations['acceleration_voltage'] = 200000
288
+ for key in ab.keys():
289
+ if key == 'C10':
290
+ aberrations['C1'] = ab['C10']
291
+ elif key == 'C12a':
292
+ r, p = cart2pol(ab['C12a'], ab['C12b'])
293
+ aberrations['A1-a'] = r
294
+ aberrations['A1-p'] = p
295
+ elif key == 'C21a':
296
+ r, p = cart2pol(ab['C21a'], ab['C21b'])
297
+ aberrations['B2-a'] = r/3
298
+ aberrations['B2-p'] = p
299
+ elif key == 'C23a':
300
+ r, p = cart2pol(ab['C23a'], ab['C23b'])
301
+ aberrations['A2-a'] = r
302
+ aberrations['A2-p'] = p
303
+ elif key == 'C30':
304
+ aberrations['C3'] = ab['C30']
305
+ elif key == 'C32a':
306
+ r, p = cart2pol(ab['C32a'], ab['C32b'])
307
+ aberrations['S3-a'] = r/4
308
+ aberrations['S3-p'] = p
309
+ elif key == 'C34a':
310
+ r, p = cart2pol(ab['C34a'], ab['C34b'])
311
+ aberrations['A3-a'] = r
312
+ aberrations['A3-p'] = p
313
+ elif key == 'C41a':
314
+ r, p = cart2pol(ab['C41a'], ab['C41b'])
315
+ aberrations['B4-a'] = r/4
316
+ aberrations['B4-p'] = p
317
+ elif key == 'C43a':
318
+ r, p = cart2pol(ab['C43a'], ab['C43b'])
319
+ aberrations['D4-a'] = r/4
320
+ aberrations['D4-p'] = p
321
+ elif key == 'C31a':
322
+ r, p = cart2pol(ab['C41a'], ab['C41b'])
323
+ aberrations['A4-a'] = r
324
+ aberrations['A4-p'] = p
325
+ elif key == 'C50':
326
+ aberrations['C5'] = ab['C50']
327
+ elif key == 'C56a':
328
+ r, p = cart2pol(ab['C56a'], ab['C56b'])
329
+ aberrations['A5-a'] = r
330
+ aberrations['A5-p'] = p
331
+
332
+ return aberrations
333
+
334
+ def get_ronchigram(size, ab, scale='mrad'):
335
+ """ Get Ronchigram
336
+
337
+ """
338
+ size_x = size_y = size
339
+ chi, A_k = get_chi(ab, size_x, size_y)
340
+
341
+ v_noise = np.random.rand(size_x, size_y)
342
+ smoothing = 5
343
+ phi_r = ndimage.gaussian_filter(v_noise, sigma=(smoothing, smoothing), order=0)
344
+
345
+ sigma = 6 # 6 for carbon and thin
346
+
347
+ q_r = np.exp(-1j * sigma * phi_r)
348
+ # q_r = 1-phi_r * sigma
349
+
350
+ T_k = A_k * (np.exp(-1j * chi))
351
+ t_r = (np.fft.ifft2(np.fft.fftshift(T_k)))
352
+
353
+ psi_k = np.fft.fftshift(np.fft.fft2(q_r * t_r))
354
+
355
+ ronchigram = np.absolute(psi_k * np.conjugate(psi_k))
356
+
357
+ fov_reciprocal = 1 / ab['FOV'] * size_x / 2
358
+ if scale == '1/nm':
359
+ extent = [-fov_reciprocal, fov_reciprocal, -fov_reciprocal, fov_reciprocal]
360
+ ylabel = 'reciprocal distance [1/nm]'
361
+ else:
362
+ fov_mrad = fov_reciprocal * ab['wavelength'] * 1000
363
+ extent = [-fov_mrad, fov_mrad, -fov_mrad, fov_mrad]
364
+ ylabel = 'reciprocal distance [mrad]'
365
+
366
+ ab['chi'] = chi
367
+ ab['ronchi_extent'] = extent
368
+ ab['ronchi_label'] = ylabel
369
+ return ronchigram
370
+
371
+
372
+ def make_probe (chi, aperture):
373
+ chi2 = np.fft.ifftshift(chi)
374
+ chiT = np.fft.ifftshift (np.vectorize(complex)(np.cos(chi2), -np.sin(chi2)) )
375
+ ## Aply aperture function
376
+ chiT = chiT*aperture
377
+ ## inverse fft of aberration function
378
+ i2 = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift (chiT)))
379
+ ## intensity
380
+ probe = np.real(i2 * np.conjugate(i2))
381
+
382
+ return probe
383
+
384
+
385
+ def get_probe( ab, sizeX, sizeY, scale = 'mrad', verbose= True):
386
+
387
+ chi, A_k = get_chi( ab, sizeX, sizeY, verbose= False)
388
+ probe = make_probe (chi, A_k)
389
+
390
+ return probe, A_k, chi
391
+
392
+
393
+ def get_probe_large(ab):
394
+ ab['FOV'] = 20
395
+ sizeX = 512*2
396
+ probe, A_k, chi = pyTEMlib.probe_tools.get_probe( ab, sizeX, sizeX, scale = 'mrad', verbose= True)
397
+
398
+ res = np.zeros((512, 512))
399
+ res[256-32:256+32, 256-32:256+32 ] = skimage.transform.resize(probe, (64, 64))
400
+
401
+ return res
402
+
403
+
404
+ def get_chi_2(ab, u, v):
405
+ chi1 = ab['C10'] * (u ** 2 + v ** 2) / 2 \
406
+ + ab['C12a'] * (u ** 2 - v ** 2) / 2 \
407
+ - ab['C12b'] * u * v
408
+
409
+ chi2 = ab['C21a'] * (u ** 3 + u * v ** 2) / 3 \
410
+ - ab['C21b'] * (u ** 2 * v + v ** 3) / 3 \
411
+ + ab['C23a'] * (u ** 3 - 3 * u * v ** 2) / 3 \
412
+ - ab['C23b'] * (3 * u ** 2 * v - v ** 3) / 3
413
+
414
+ chi3 = ab['C30'] * (u ** 4 + 2 * u ** 2 * v ** 2 + v ** 4) / 4 \
415
+ + ab['C32a'] * (u ** 4 - v ** 4) / 4 \
416
+ - ab['C32b'] * (u ** 3 * v + u * v ** 3) / 2 \
417
+ + ab['C34a'] * (u ** 4 - 6 * u ** 2 * v ** 2 + v ** 4) / 4 \
418
+ - ab['C34b'] * (4 * u ** 3 * v - 4 * u * v ** 3) / 4
419
+
420
+ chi4 = ab['C41a'] * (u ** 5 + 2 * u ** 3 * v ** 2 + u * v ** 4) / 5 \
421
+ - ab['C41b'] * (u ** 4 * v + 2 * u ** 2 * v ** 3 + v ** 5) / 5 \
422
+ + ab['C43a'] * (u ** 5 - 2 * u ** 3 * v ** 2 - 3 * u * v ** 4) / 5 \
423
+ - ab['C43b'] * (3 * u ** 4 * v + 2 * u ** 2 * v ** 3 - v ** 5) / 5 \
424
+ + ab['C45a'] * (u ** 5 - 10 * u ** 3 * v ** 2 + 5 * u * v ** 4) / 5 \
425
+ - ab['C45b'] * (5 * u ** 4 * v - 10 * u ** 2 * v ** 3 + v ** 5) / 5
426
+
427
+ chi5 = ab['C50'] * (u ** 6 + 3 * u ** 4 * v ** 2 + 3 * u ** 2 * v ** 4 + v ** 6) / 6 \
428
+ + ab['C52a'] * (u ** 6 + u ** 4 * v ** 2 - u ** 2 * v ** 4 - v ** 6) / 6 \
429
+ - ab['C52b'] * (2 * u ** 5 * v + 4 * u ** 3 * v ** 3 + 2 * u * v ** 5) / 6 \
430
+ + ab['C54a'] * (u ** 6 - 5 * u ** 4 * v ** 2 - 5 * u ** 2 * v ** 4 + v ** 6) / 6 \
431
+ - ab['C54b'] * (4 * u ** 5 * v - 4 * u * v ** 5) / 6 \
432
+ + ab['C56a'] * (u ** 6 - 15 * u ** 4 * v ** 2 + 15 * u ** 2 * v ** 4 - v ** 6) / 6 \
433
+ - ab['C56b'] * (6 * u ** 5 * v - 20 * u ** 3 * v ** 3 + 6 * u * v ** 5) / 6
434
+
435
+ chi = chi1 + chi2 + chi3 + chi4 + chi5
436
+ return chi * 2 * np.pi / ab['wavelength']
437
+
438
+
439
+ def get_d2chidu2(ab, u, v):
440
+ d2chi1du2 = ab['C10'] + ab['C12a']
441
+
442
+ d2chi2du2 = ab['C21a'] * 2 * u \
443
+ - ab['C21b'] * 2 / 3 * v \
444
+ + ab['C23a'] * 2 * u \
445
+ - ab['C23b'] * 2 * v
446
+
447
+ d2chi3du2 = ab['C30'] * (3 * u ** 2 + v ** 2) \
448
+ + ab['C32a'] * 3 * u ** 2 \
449
+ - ab['C32b'] * 3 * u * v \
450
+ + ab['C34a'] * (3 * u ** 2 - 3 * v ** 2) \
451
+ - ab['C34b'] * 6 * u * v
452
+
453
+ d2chi4du2 = ab['C41a'] * 4 / 5 * (5 * u ** 3 + 3 * u * v ** 2) \
454
+ - ab['C41b'] * 4 / 5 * (3 * u ** 2 * v + v ** 3) \
455
+ + ab['C43a'] * 4 / 5 * (5 * u ** 3 - 3 * u * v ** 2) \
456
+ - ab['C43b'] * 4 / 5 * (9 * u ** 2 * v + v ** 3) \
457
+ + ab['C45a'] * 4 * (u ** 3 - 3 * u * v ** 2) \
458
+ - ab['C45b'] * 4 * (3 * u ** 2 * v - v ** 3)
459
+
460
+ d2chi5du2 = ab['C50'] * (5 * u ** 4 + 6 * u ** 2 * v ** 2 + v ** 4) \
461
+ + ab['C52a'] * (15 * u ** 4 + 6 * u ** 2 * v ** 2 - v ** 4) / 3 \
462
+ - ab['C52b'] * (20 * u ** 3 * v + 12 * u * v ** 3) / 3 \
463
+ + ab['C54a'] * 5 / 3 * (3 * u ** 4 - 6 * u ** 2 * v ** 2 - v ** 4) \
464
+ - ab['C54b'] * 5 / 3 * (8 * u ** 3 * v) \
465
+ + ab['C56a'] * 5 * (u ** 4 - 6 * u ** 2 * v ** 2 + v ** 4) \
466
+ - ab['C56b'] * 20 * (u ** 3 * v - u * v ** 3)
467
+
468
+ d2chidu2 = d2chi1du2 + d2chi2du2 + d2chi3du2 + d2chi4du2 + d2chi5du2
469
+ return d2chidu2
470
+
471
+
472
+ def get_d2chidudv(ab, u, v):
473
+ d2chi1dudv = -ab['C12b']
474
+
475
+ d2chi2dudv = ab['C21a'] * 2 / 3 * v \
476
+ - ab['C21b'] * 2 / 3 * u \
477
+ - ab['C23a'] * 2 * v \
478
+ - ab['C23b'] * 2 * u
479
+
480
+ d2chi3dudv = ab['C30'] * 2 * u * v \
481
+ + ab['C32a'] * 0 \
482
+ - ab['C32b'] * 3 / 2 * (u ** 2 + v ** 2) \
483
+ - ab['C34a'] * 6 * u * v \
484
+ - ab['C34b'] * 3 * (u ** 2 - v ** 2)
485
+
486
+ d2chi4dudv = ab['C41a'] * 4 / 5 * (3 * u ** 2 * v + v ** 3) \
487
+ - ab['C41b'] * 4 / 5 * (u ** 3 + 3 * u * v ** 2) \
488
+ - ab['C43a'] * 12 / 5 * (u ** 2 * v + v ** 3) \
489
+ - ab['C43b'] * 12 / 5 * (u ** 3 + u * v ** 2) \
490
+ - ab['C45a'] * 4 * (3 * u ** 2 * v - v ** 3) \
491
+ - ab['C45b'] * 4 * (u ** 3 - 3 * u * v ** 2)
492
+
493
+ d2chi5dudv = ab['C50'] * 4 * u * v * (u ** 2 + v ** 2) \
494
+ + ab['C52a'] * 4 / 3 * (u ** 3 * v - u * v ** 3) \
495
+ - ab['C52b'] * (5 * u ** 4 + 18 * u ** 2 * v ** 2 + 5 * v ** 4) / 3 \
496
+ - ab['C54a'] * 20 / 3 * (u ** 3 * v + u * v ** 3) \
497
+ - ab['C54b'] * 10 / 3 * (u ** 4 - v ** 4) \
498
+ - ab['C56a'] * 20 * (u ** 3 * v - u * v ** 3) \
499
+ - ab['C56b'] * 5 * (u ** 4 - 6 * u ** 2 * v ** 2 + v ** 4)
500
+
501
+ d2chidudv = d2chi1dudv + d2chi2dudv + d2chi3dudv + d2chi4dudv + d2chi5dudv
502
+ return d2chidudv
503
+
504
+
505
+ def get_d2chidv2(ab, u, v):
506
+ d2chi1dv2 = ab['C10'] - ab['C12a']
507
+
508
+ d2chi2dv2 = ab['C21a'] * 2 / 3 * u \
509
+ - ab['C21b'] * 2 * v \
510
+ - ab['C23a'] * 2 * u \
511
+ + ab['C23b'] * 2 * v
512
+
513
+ d2chi3dv2 = ab['C30'] * (u ** 2 + 3 * v ** 2) \
514
+ - ab['C32a'] * 3 * v ** 2 \
515
+ - ab['C32b'] * 3 * v * u \
516
+ - ab['C34a'] * 3 * (u ** 2 - v ** 2) \
517
+ + ab['C34b'] * 6 * u * v
518
+
519
+ d2chi4dv2 = ab['C41a'] * 4 / 5 * (u ** 3 + 3 * u * v ** 2) \
520
+ - ab['C41b'] * 4 / 5 * (3 * u ** 2 * v + 5 * v ** 3) \
521
+ - ab['C43a'] * 4 / 5 * (u ** 3 + 9 * u * v ** 2) \
522
+ - ab['C43b'] * 4 / 5 * (3 * u ** 2 * v - 5 * v ** 3) \
523
+ - ab['C45a'] * 4 * (u ** 3 - 3 * u * v ** 2) \
524
+ + ab['C45b'] * 4 * (3 * u ** 2 * v - v ** 3)
525
+
526
+ d2chi5dv2 = ab['C50'] * (u ** 4 + 6 * u ** 2 * v ** 2 + 5 * v ** 4) \
527
+ + ab['C52a'] * (u ** 4 - 6 * u ** 2 * v ** 2 - 15 * v ** 4) / 3 \
528
+ - ab['C52b'] * (12 * u ** 3 * v + 20 * u * v ** 3) / 3 \
529
+ - ab['C54a'] * 5 / 3 * (u ** 4 + 6 * u ** 2 * v ** 2 - 3 * v ** 4) \
530
+ + ab['C54b'] * 40 / 3 * u * v ** 3 \
531
+ - ab['C56a'] * 5 * (u ** 4 - 6 * u ** 2 * v ** 2 + v ** 4) \
532
+ + ab['C56b'] * 20 * (u ** 3 * v - u * v ** 3)
533
+
534
+ d2chidv2 = d2chi1dv2 + d2chi2dv2 + d2chi3dv2 + d2chi4dv2 + d2chi5dv2
535
+ return d2chidv2
536
+
537
+
538
+ def get_source_energy_spread():
539
+ x = np.linspace(-0.5, .9, 29)
540
+ y = [0.0143, 0.0193, 0.0281, 0.0440, 0.0768, 0.1447, 0.2785, 0.4955, 0.7442, 0.9380, 1.0000, 0.9483, 0.8596, 0.7620,
541
+ 0.6539, 0.5515, 0.4478, 0.3500, 0.2683, 0.1979, 0.1410, 0.1021, 0.0752, 0.0545, 0.0401, 0.0300, 0.0229, 0.0176,
542
+ 0.0139]
543
+
544
+ return x, y
545
+
546
+
547
+ def get_target_aberrations(TEM_name, acceleration_voltage):
548
+ ab = {}
549
+ if TEM_name == 'NionUS200':
550
+ if int(acceleration_voltage) == 200000:
551
+ print(f' **** Using Target Values at {acceleration_voltage / 1000}kV for Aberrations of {TEM_name}****')
552
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': -335., 'C21b': 283., 'C23a': -34., 'C23b': 220.,
553
+ 'C30': -8080.,
554
+ 'C32a': 18800., 'C32b': -2260., 'C34a': 949., 'C34b': 949., 'C41a': 54883., 'C41b': -464102.,
555
+ 'C43a': 77240.5,
556
+ 'C43b': -540842., 'C45a': -79844.4, 'C45b': -76980.8, 'C50': 9546970., 'C52a': -2494290.,
557
+ 'C52b': 2999910.,
558
+ 'C54a': -2020140., 'C54b': -2019630., 'C56a': -535079., 'C56b': 1851850.}
559
+ ab['source_size'] = 0.051
560
+ ab['acceleration_voltage'] = acceleration_voltage
561
+ ab['convergence_angle'] = 30
562
+
563
+ ab['Cc'] = 1.3e6 # // Cc in nm
564
+
565
+ if int(acceleration_voltage) == 100000:
566
+ print(f' **** Using Target Values at {acceleration_voltage / 1000}kV for Aberrations of {TEM_name}****')
567
+
568
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 157., 'C21b': 169, 'C23a': -173., 'C23b': 48.7, 'C30': 201.,
569
+ 'C32a': 1090., 'C32b': 6840., 'C34a': 1010., 'C34b': 79.9, 'C41a': -210696., 'C41b': -262313.,
570
+ 'C43a': 348450., 'C43b': -9.7888e4, 'C45a': 6.80247e4, 'C45b': -3.14637e1, 'C50': -193896.,
571
+ 'C52a': -1178950, 'C52b': -7414340, 'C54a': -1753890, 'C54b': -1753890, 'C56a': -631786,
572
+ 'C56b': -165705}
573
+ ab['source_size'] = 0.051
574
+ ab['acceleration_voltage'] = acceleration_voltage
575
+ ab['convergence_angle'] = 30
576
+ ab['Cc'] = 1.3e6
577
+
578
+ if int(acceleration_voltage) == 60000:
579
+ print(f' **** Using Target Values at {acceleration_voltage / 1000}kV for Aberrations of {TEM_name}****')
580
+
581
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 11.5, 'C21b': 113, 'C23a': -136., 'C23b': 18.2, 'C30': 134.,
582
+ 'C32a': 1080., 'C32b': 773., 'C34a': 1190., 'C34b': -593., 'C41a': -179174., 'C41b': -350378.,
583
+ 'C43a': 528598, 'C43b': -257349., 'C45a': 63853.4, 'C45b': 1367.98, 'C50': 239021., 'C52a': 1569280.,
584
+ 'C52b': -6229310., 'C54a': -3167620., 'C54b': -449198., 'C56a': -907315., 'C56b': -16281.9}
585
+ ab['source_size'] = 0.081
586
+ ab['acceleration_voltage'] = acceleration_voltage
587
+ ab['convergence_angle'] = 30
588
+ ab['Cc'] = 1.3e6 # // Cc in nm
589
+
590
+ ab['origin'] = 'target aberrations'
591
+ ab['TEM_name'] = TEM_name
592
+ ab['wavelength'] = pyTEMlib.image_tools.get_wavelength(ab['acceleration_voltage'])
593
+
594
+ if TEM_name == 'NionUS100':
595
+ if int(acceleration_voltage) == 100000:
596
+ print(f' **** Using Target Values at {acceleration_voltage / 1000}kV for Aberrations of {TEM_name}****')
597
+
598
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 157., 'C21b': 169, 'C23a': -173., 'C23b': 48.7, 'C30': 201.,
599
+ 'C32a': 1090., 'C32b': 6840., 'C34a': 1010., 'C34b': 79.9, 'C41a': -210696., 'C41b': -262313.,
600
+ 'C43a': 348450., 'C43b': -9.7888e4, 'C45a': 6.80247e4, 'C45b': -3.14637e1, 'C50': -193896.,
601
+ 'C52a': -1178950, 'C52b': -7414340, 'C54a': -1753890, 'C54b': -1753890, 'C56a': -631786,
602
+ 'C56b': -165705}
603
+ ab['source_size'] = 0.051
604
+ ab['acceleration_voltage'] = acceleration_voltage
605
+ ab['convergence_angle'] = 30
606
+ ab['Cc'] = 1.3e6 # // Cc in nm
607
+
608
+ if int(acceleration_voltage) == 60000:
609
+ print(f' **** Using Target Values at {acceleration_voltage / 1000}kV for Aberrations of {TEM_name}****')
610
+
611
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 11.5, 'C21b': 113, 'C23a': -136., 'C23b': 18.2, 'C30': 134.,
612
+ 'C32a': 1080., 'C32b': 773., 'C34a': 1190., 'C34b': -593., 'C41a': -179174., 'C41b': -350378.,
613
+ 'C43a': 528598, 'C43b': -257349., 'C45a': 63853.4, 'C45b': 1367.98, 'C50': 239021., 'C52a': 1569280.,
614
+ 'C52b': -6229310., 'C54a': -3167620., 'C54b': -449198., 'C56a': -907315., 'C56b': -16281.9}
615
+ ab['source_size'] = 0.081
616
+ ab['acceleration_voltage'] = acceleration_voltage
617
+ ab['convergence_angle'] = 30
618
+ ab['Cc'] = 1.3e6 # // Cc in nm
619
+
620
+ ab['origin'] = 'target aberrations'
621
+ ab['TEM_name'] = TEM_name
622
+ ab['wavelength'] = pyTEMlib.image_tools.get_wavelength(ab['acceleration_voltage'])
623
+
624
+ if TEM_name == 'ZeissMC200':
625
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0, 'C21a': 0, 'C21b': 0, 'C23a': 0, 'C23b': 0, 'C30': 0.,
626
+ 'C32a': 0., 'C32b': -0., 'C34a': 0., 'C34b': 0., 'C41a': 0., 'C41b': -0., 'C43a': 0.,
627
+ 'C43b': -0., 'C45a': -0., 'C45b': -0., 'C50': 0., 'C52a': -0., 'C52b': 0.,
628
+ 'C54a': -0., 'C54b': -0., 'C56a': -0., 'C56b': 0.}
629
+ ab['C30'] = 2.2 * 1e6
630
+
631
+ ab['Cc'] = 2.0 * 1e6
632
+
633
+ ab['source_size'] = 0.2
634
+ ab['acceleration_voltage'] = acceleration_voltage
635
+ ab['convergence_angle'] = 10
636
+
637
+ ab['origin'] = 'target aberrations'
638
+ ab['TEM_name'] = TEM_name
639
+
640
+ ab['wavelength'] = pyTEMlib.image_tools.get_wavelength(ab['acceleration_voltage'])
641
+
642
+ if TEM_name == 'Spectra300':
643
+ ab = {'C10': 0, 'C12a': 0, 'C12b': 0.38448128113770325,
644
+ 'C21a': -68.45251255685642, 'C21b': 64.85359774641199, 'C23a': 11.667578600494137, 'C23b': -29.775627778458194,
645
+ 'C30': 123,
646
+ 'C32a': 95.3047364258614, 'C32b': -189.72105710231244, 'C34a': -47.45099594807912, 'C34b': -94.67424667529909,
647
+ 'C41a': -905.31842572806, 'C41b': 981.316128853203, 'C43a': 4021.8433526960034, 'C43b': 131.72716642732158,
648
+ 'C45a': -4702.390968272048, 'C45b': -208.25028574642903, 'C50': 552000., 'C52a': -0., 'C52b': 0.,
649
+ 'C54a': -0., 'C54b': -0., 'C56a': -36663.643489934424, 'C56b': 21356.079837905396,
650
+ 'acceleration_voltage': 200000,
651
+ 'FOV': 34.241659495148205,
652
+ 'Cc': 1* 1e6,
653
+ 'convergence_angle': 30,
654
+ 'wavelength': 0.0025079340450548005}
655
+ return ab
656
+
657
+
658
+ def get_ronchigram_2(size, ab, scale='mrad', threshold=3):
659
+ aperture_angle = ab['convergence_angle'] / 1000.0 # in rad
660
+
661
+ wavelength = pyTEMlib.image_tools.get_wavelength(ab['acceleration_voltage'])
662
+ # if verbose:
663
+ # print(f"Acceleration voltage {ab['acceleration_voltage']/1000:}kV => wavelength {wavelength*1000.:.2f}pm")
664
+
665
+ ab['wavelength'] = wavelength
666
+
667
+ size_x = size_y = size
668
+
669
+ # Reciprocal plane in 1/nm
670
+ dk = ab['reciprocal_FOV'] / size
671
+ k_x = np.array(dk * (-size_x / 2. + np.arange(size_x)))
672
+ k_y = np.array(dk * (-size_y / 2. + np.arange(size_y)))
673
+ t_x_v, t_y_v = np.meshgrid(k_x, k_y)
674
+
675
+ chi = get_chi_2(ab, t_x_v, t_y_v) # , verbose= True)
676
+ # define reciprocal plane in angles
677
+ phi = np.arctan2(t_x_v, t_y_v)
678
+ theta = np.arctan2(np.sqrt(t_x_v ** 2 + t_y_v ** 2), 1 / wavelength)
679
+
680
+ # Aperture function
681
+ mask = theta >= aperture_angle
682
+
683
+ aperture = np.ones((size_x, size_y), dtype=float)
684
+ aperture[mask] = 0.
685
+
686
+ v_noise = np.random.rand(size_x, size_y)
687
+ smoothing = 10
688
+ phi_r = ndimage.gaussian_filter(v_noise, sigma=(smoothing, smoothing), order=0)
689
+
690
+ sigma = 6 # 6 for carbon and thin
691
+
692
+ q_r = np.exp(-1j * sigma * phi_r)
693
+ # q_r = 1-phi_r * sigma
694
+
695
+ T_k = aperture * (np.exp(-1j * chi))
696
+ t_r = np.fft.ifft2(np.fft.fftshift(T_k))
697
+
698
+ Psi_k = np.fft.fftshift(np.fft.fft2(q_r * t_r))
699
+
700
+ ronchigram = I_k = np.absolute(Psi_k * np.conjugate(Psi_k))
701
+
702
+ fov_reciprocal = ab['reciprocal_FOV']
703
+ if scale == '1/nm':
704
+ extent = [-fov_reciprocal, fov_reciprocal, -fov_reciprocal, fov_reciprocal]
705
+ ylabel = 'reciprocal distance [1/nm]'
706
+ else:
707
+ fov_mrad = fov_reciprocal * ab['wavelength'] * 1000
708
+ extent = [-fov_mrad, fov_mrad, -fov_mrad, fov_mrad]
709
+ ylabel = 'reciprocal distance [mrad]'
710
+
711
+ ab['chi'] = chi
712
+ ab['ronchi_extent'] = extent
713
+ ab['ronchi_label'] = ylabel
714
+
715
+ h = np.zeros([chi.shape[0], chi.shape[1], 2, 2])
716
+ h[:, :, 0, 0] = get_d2chidu2(ab, t_x_v, t_y_v)
717
+ h[:, :, 0, 1] = get_d2chidudv(ab, t_x_v, t_y_v)
718
+ h[:, :, 1, 0] = get_d2chidudv(ab, t_x_v, t_y_v)
719
+ h[:, :, 1, 1] = get_d2chidv2(ab, t_x_v, t_y_v)
720
+
721
+ # get Eigenvalues
722
+ _, s, _ = np.linalg.svd(h)
723
+
724
+ # get smallest Eigenvalue per pixel
725
+ infinite_magnification = np.min(s, axis=2)
726
+
727
+ # set all values below a threshold value to one, otherwise 0
728
+ infinite_magnification[infinite_magnification <= threshold] = 1
729
+ infinite_magnification[infinite_magnification > threshold] = 0
730
+
731
+ return ronchigram, infinite_magnification
732
+
733
+
734
+ # ## Aberration Function for Probe calculations
735
+ def make_chi1(phi, theta, wavelength, ab, c1_include):
736
+ """
737
+ # ##
738
+ # Aberration function chi without defocus
739
+ # ##
740
+ """
741
+ t0 = np.power(theta, 1) / 1 * (float(ab['C01a']) * np.cos(1 * phi) + float(ab['C01b']) * np.sin(1 * phi))
742
+
743
+ if c1_include == 1: # First and second terms
744
+ t1 = np.power(theta, 2) / 2 * (ab['C10'] + ab['C12a'] * np.cos(2 * phi) + ab['C12b'] * np.sin(2 * phi))
745
+ elif c1_include == 2: # Second terms only
746
+ t1 = np.power(theta, 2) / 2 * (ab['C12a'] * np.cos(2 * phi) + ab['C12b'] * np.sin(2 * phi))
747
+ else: # none for zero
748
+ t1 = t0 * 0.
749
+
750
+ t2 = np.power(theta, 3) / 3 * (ab['C21a'] * np.cos(1 * phi) + ab['C21b'] * np.sin(1 * phi)
751
+ + ab['C23a'] * np.cos(3 * phi) + ab['C23b'] * np.sin(3 * phi))
752
+
753
+ t3 = np.power(theta, 4) / 4 * (ab['C30']
754
+ + ab['C32a'] * np.cos(2 * phi)
755
+ + ab['C32b'] * np.sin(2 * phi)
756
+ + ab['C34a'] * np.cos(4 * phi)
757
+ + ab['C34b'] * np.sin(4 * phi))
758
+
759
+ t4 = np.power(theta, 5) / 5 * (ab['C41a'] * np.cos(1 * phi)
760
+ + ab['C41b'] * np.sin(1 * phi)
761
+ + ab['C43a'] * np.cos(3 * phi)
762
+ + ab['C43b'] * np.sin(3 * phi)
763
+ + ab['C45a'] * np.cos(5 * phi)
764
+ + ab['C45b'] * np.sin(5 * phi))
765
+
766
+ t5 = np.power(theta, 6) / 6 * (ab['C50']
767
+ + ab['C52a'] * np.cos(2 * phi)
768
+ + ab['C52b'] * np.sin(2 * phi)
769
+ + ab['C54a'] * np.cos(4 * phi)
770
+ + ab['C54b'] * np.sin(4 * phi)
771
+ + ab['C56a'] * np.cos(6 * phi)
772
+ + ab['C56b'] * np.sin(6 * phi))
773
+
774
+ chi = t0 + t1 + t2 + t3 + t4 + t5
775
+ if 'C70' in ab:
776
+ chi += np.power(theta, 8) / 8 * (ab['C70'])
777
+
778
+ return chi * 2 * np.pi / wavelength # np.power(theta,6)/6*( ab['C50'] )
779
+
780
+
781
+ def probe2(ab, size_x, size_y, tags, verbose=False):
782
+ """
783
+
784
+ * This function creates an incident STEM probe
785
+ * at position (0,0)
786
+ * with parameters given in ab dictionary
787
+ *
788
+ * The following Aberration functions are being used:
789
+ * 1) ddf = Cc*de/E but not + Cc2*(de/E)^2,
790
+ * Cc, Cc2 = chrom. Aber. (1st, 2nd order) [1]
791
+ * 2) chi(qx,qy) = (2*pi/lambda)*{0.5*C1*(qx^2+qy^2)+
792
+ * 0.5*C12a*(qx^2-qy^2)+
793
+ * C12b*qx*qy+
794
+ * C21a/3*qx*(qx^2+qy^2)+
795
+ * ...
796
+ * +0.5*C3*(qx^2+qy^2)^2
797
+ * +0.125*C5*(qx^2+qy^2)^3
798
+ * ... (need to finish)
799
+ *
800
+ *
801
+ * qx = acos(k_x/K), qy = acos(k_y/K)
802
+ *
803
+ * References:
804
+ * [1] J. Zach, M. Haider,
805
+ * "Correction of spherical and Chromatic Aberration
806
+ * in a low Voltage SEM", Optik 98 (3), 112-118 (1995)
807
+ * [2] O.L. Krivanek, N. Delby, A.R. Lupini,
808
+ * "Towards sub-Angstrom Electron Beams",
809
+ * Ultramicroscopy 78, 1-11 (1999)
810
+ *
811
+
812
+
813
+ # Internally reciprocal lattice vectors in 1/nm or rad.
814
+ # All calculations of chi in angles.
815
+ # All aberration coefficients in nm
816
+ """
817
+
818
+ if 'fov' not in ab:
819
+ if 'fov' not in tags:
820
+ print(' need field of view in tags ')
821
+ else:
822
+ ab['fov'] = tags['fov']
823
+
824
+ if 'convAngle' not in ab:
825
+ ab['convAngle'] = 30 # in mrad
826
+
827
+ ap_angle = ab['convAngle'] / 1000.0 # in rad
828
+
829
+ e0 = ab['EHT'] = float(ab['EHT']) # acceleration voltage in ev
830
+
831
+ # defocus = ab['C10']
832
+
833
+ if 'C01a' not in ab:
834
+ ab['C01a'] = 0.
835
+ if 'C01b' not in ab:
836
+ ab['C01b'] = 0.
837
+
838
+ if 'C50' not in ab:
839
+ ab['C50'] = 0.
840
+ if 'C70' not in ab:
841
+ ab['C70'] = 0.
842
+
843
+ if 'Cc' not in ab:
844
+ ab['Cc'] = 1.3e6 # Cc in nm
845
+
846
+ def get_wl():
847
+ h = 6.626 * 10 ** -34
848
+ m0 = 9.109 * 10 ** -31
849
+ ev = 1.602 * 10 ** -19 * e0
850
+ c = 2.998 * 10 ** 8
851
+ return h / np.sqrt(2 * m0 * ev * (1 + ev / (2 * m0 * c ** 2))) * 10 ** 9
852
+
853
+ wavelength = get_wl()
854
+ if verbose:
855
+ print('Acceleration voltage {0:}kV => wavelength {1:.2f}pm'.format(int(e0 / 1000), wavelength * 1000))
856
+ ab['wavelength'] = wavelength
857
+
858
+ # Reciprocal plane in 1/nm
859
+ dk = 1 / ab['fov']
860
+ k_x = np.array(dk * (-size_x / 2. + np.arange(size_x)))
861
+ k_y = np.array(dk * (-size_y / 2. + np.arange(size_y)))
862
+ t_xv, t_yv = np.meshgrid(k_x, k_y)
863
+
864
+ # define reciprocal plane in angles
865
+ phi = np.arctan2(t_xv, t_yv)
866
+ theta = np.arctan2(np.sqrt(t_xv ** 2 + t_yv ** 2), 1 / wavelength)
867
+
868
+ # calculate chi but omit defocus
869
+ chi = np.fft.ifftshift(make_chi1(phi, theta, wavelength, ab, 2))
870
+ probe = np.zeros((size_x, size_y))
871
+
872
+ # Aperture function
873
+ mask = theta >= ap_angle
874
+
875
+ # Calculate probe with Cc
876
+
877
+ for i in range(len(ab['zeroLoss'])):
878
+ df = ab['C10'] + ab['Cc'] * ab['zeroEnergy'][i] / e0
879
+ if verbose:
880
+ print('defocus due to Cc: {0:.2f} nm with weight {1:.2f}'.format(df, ab['zeroLoss'][i]))
881
+ # Add defocus
882
+ chi2 = chi + np.power(theta, 2) / 2 * df
883
+ # Calculate exponent of - i * chi
884
+ chi_t = np.fft.ifftshift(np.vectorize(complex)(np.cos(chi2), -np.sin(chi2)))
885
+ # Apply aperture function
886
+ chi_t[mask] = 0.
887
+ # inverse fft of aberration function
888
+ i2 = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift(chi_t)))
889
+ # add intensities
890
+ probe = probe + np.real(i2 * np.conjugate(i2)).T * ab['zeroLoss'][i]
891
+
892
+ ab0 = {}
893
+ for key in ab:
894
+ ab0[key] = 0.
895
+ # chiIA = np.fft.fftshift(make_chi1(phi, theta, wavelength, ab0, 0)) # np.ones(chi2.shape)*2*np.pi/wavelength
896
+ chi_i = np.ones((size_y, size_x))
897
+ chi_i[mask] = 0.
898
+ i2 = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift(chi_i)))
899
+ ideal = np.real(i2 * np.conjugate(i2))
900
+
901
+ probe_f = np.fft.fft2(probe, probe.shape) + 1e-12
902
+ ideal_f = np.fft.fft2(ideal, probe.shape)
903
+ fourier_space_division = ideal_f / probe_f
904
+ probe_r = (np.fft.ifft2(fourier_space_division, probe.shape))
905
+
906
+ return probe / sum(ab['zeroLoss']), np.real(probe_r)