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