pyTEMlib 0.2020.11.0__py3-none-any.whl → 0.2024.8.4__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 (59) hide show
  1. pyTEMlib/__init__.py +11 -11
  2. pyTEMlib/animation.py +631 -0
  3. pyTEMlib/atom_tools.py +240 -222
  4. pyTEMlib/config_dir.py +57 -29
  5. pyTEMlib/core_loss_widget.py +658 -0
  6. pyTEMlib/crystal_tools.py +1255 -0
  7. pyTEMlib/diffraction_plot.py +756 -0
  8. pyTEMlib/dynamic_scattering.py +293 -0
  9. pyTEMlib/eds_tools.py +609 -0
  10. pyTEMlib/eels_dialog.py +749 -486
  11. pyTEMlib/{interactive_eels.py → eels_dialog_utilities.py} +1199 -1524
  12. pyTEMlib/eels_tools.py +2031 -1731
  13. pyTEMlib/file_tools.py +1276 -491
  14. pyTEMlib/file_tools_qt.py +193 -0
  15. pyTEMlib/graph_tools.py +1166 -450
  16. pyTEMlib/graph_viz.py +449 -0
  17. pyTEMlib/image_dialog.py +158 -0
  18. pyTEMlib/image_dlg.py +146 -0
  19. pyTEMlib/image_tools.py +1399 -956
  20. pyTEMlib/info_widget.py +933 -0
  21. pyTEMlib/interactive_image.py +1 -0
  22. pyTEMlib/kinematic_scattering.py +1196 -0
  23. pyTEMlib/low_loss_widget.py +176 -0
  24. pyTEMlib/microscope.py +61 -78
  25. pyTEMlib/peak_dialog.py +1047 -350
  26. pyTEMlib/peak_dlg.py +286 -248
  27. pyTEMlib/probe_tools.py +653 -202
  28. pyTEMlib/sidpy_tools.py +153 -129
  29. pyTEMlib/simulation_tools.py +104 -87
  30. pyTEMlib/version.py +6 -3
  31. pyTEMlib/xrpa_x_sections.py +20972 -0
  32. {pyTEMlib-0.2020.11.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/LICENSE +21 -21
  33. pyTEMlib-0.2024.8.4.dist-info/METADATA +93 -0
  34. pyTEMlib-0.2024.8.4.dist-info/RECORD +37 -0
  35. {pyTEMlib-0.2020.11.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/WHEEL +6 -5
  36. {pyTEMlib-0.2020.11.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/entry_points.txt +0 -1
  37. pyTEMlib/KinsCat.py +0 -2685
  38. pyTEMlib/__version__.py +0 -2
  39. pyTEMlib/data/TEMlibrc +0 -68
  40. pyTEMlib/data/edges_db.csv +0 -189
  41. pyTEMlib/data/edges_db.pkl +0 -0
  42. pyTEMlib/data/fparam.txt +0 -103
  43. pyTEMlib/data/microscopes.csv +0 -7
  44. pyTEMlib/data/microscopes.xml +0 -167
  45. pyTEMlib/data/path.txt +0 -1
  46. pyTEMlib/defaults_parser.py +0 -86
  47. pyTEMlib/dm3_reader.py +0 -609
  48. pyTEMlib/edges_db.py +0 -76
  49. pyTEMlib/eels_dlg.py +0 -240
  50. pyTEMlib/hdf_utils.py +0 -481
  51. pyTEMlib/image_tools1.py +0 -2194
  52. pyTEMlib/info_dialog.py +0 -227
  53. pyTEMlib/info_dlg.py +0 -205
  54. pyTEMlib/nion_reader.py +0 -293
  55. pyTEMlib/nsi_reader.py +0 -165
  56. pyTEMlib/structure_tools.py +0 -316
  57. pyTEMlib-0.2020.11.0.dist-info/METADATA +0 -20
  58. pyTEMlib-0.2020.11.0.dist-info/RECORD +0 -42
  59. {pyTEMlib-0.2020.11.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/top_level.txt +0 -0
pyTEMlib/__init__.py CHANGED
@@ -1,11 +1,11 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Created on Sat Jan 19 10:07:35 2019
4
-
5
- @author: gduscher
6
- """
7
- from .version import _version as __version__
8
-
9
- __all__ = ['__version__']
10
-
11
- name = "pyTEMlib"
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Sat Jan 19 10:07:35 2019
4
+
5
+ @author: gduscher
6
+ """
7
+ from .version import _version as __version__
8
+
9
+ __all__ = ['__version__']
10
+
11
+ name = "pyTEMlib"
pyTEMlib/animation.py ADDED
@@ -0,0 +1,631 @@
1
+ """Figures and Animations for TEM in jupyter notebooks
2
+ part of MSE 672 course at UTK
3
+
4
+ Author: Gerd Duscher
5
+ revision: 01/11/2021
6
+ 03/17/2021 added Aberration Animation
7
+ """
8
+
9
+ import numpy as np
10
+ import matplotlib.pyplot as plt
11
+ import matplotlib.patches as patches
12
+
13
+ from ipywidgets import widgets
14
+ from IPython.display import display
15
+
16
+ import pyTEMlib.kinematic_scattering as ks
17
+
18
+
19
+ def geometric_ray_diagram(focal_length=1., magnification=False):
20
+ """ Sketch of geometric ray diagram od one lens
21
+
22
+ Parameters
23
+ ----------
24
+ focal_length: float
25
+ focal length of lens
26
+ magnification: boolean
27
+ draw magnification on the side
28
+
29
+ Returns
30
+ -------
31
+ matplotlib figure
32
+ """
33
+
34
+ f = focal_length
35
+
36
+ u = 1.5
37
+ v = 1 / (1 / f - 1 / u)
38
+ m = v / u
39
+ if magnification:
40
+ line_strong = .5
41
+ else:
42
+ line_strong = 2
43
+
44
+ x = 0.4
45
+
46
+ fig, ax = plt.subplots()
47
+
48
+ # add an ellipse
49
+ ellipse = patches.Ellipse((0.0, 0.0), 3.4, 0.3, alpha=0.3, color='blue')
50
+ ax.add_patch(ellipse)
51
+ ax.plot([1.5, -1.5], [0, 0], '--', color='black')
52
+ ax.plot([0, 0], [u, -v], '--', color='black')
53
+ single_prop = dict(arrowstyle="->", shrinkA=0, shrinkB=0)
54
+ double_prop = dict(arrowstyle="<->", shrinkA=0, shrinkB=0)
55
+
56
+ if magnification:
57
+ ax.annotate("", xy=(-x, u), xytext=(x, u), arrowprops=single_prop)
58
+ ax.annotate("", xy=(x * m, -v), xytext=(-x * m, -v), arrowprops=single_prop)
59
+
60
+ else:
61
+ ax.annotate("", xy=(-x, u), xytext=(0, u), arrowprops=single_prop)
62
+ ax.annotate("", xy=(x * m, -v), xytext=(0, -v), arrowprops=single_prop)
63
+
64
+ ax.text(x + 0.1, u, 'object plane', va='center')
65
+ ax.plot([1, -1], [-f, -f], '--', color='black')
66
+ ax.text(1.1, -f, 'back focal\n plane', va='center')
67
+ ax.text(x * m + 0.1, -v, 'image plane', va='center')
68
+
69
+ ax.annotate("", xy=(-.9, 0), xytext=(-.9, -f), arrowprops=double_prop)
70
+ ax.text(-1, -f / 2, 'f')
71
+ if magnification:
72
+ ax.annotate("", xy=(-1.8, 0), xytext=(-1.8, -v), arrowprops=double_prop)
73
+ ax.text(-1.7, -v / 2, 'v')
74
+ ax.annotate("", xy=(-1.8, 0), xytext=(-1.8, u), arrowprops=double_prop)
75
+ ax.text(-1.7, u / 2, 'u')
76
+
77
+ ax.plot([-x, x * m], [u, -v], color='black', linewidth=line_strong)
78
+ ax.plot([-x, -x], [u, 0], color='black', linewidth=line_strong)
79
+ ax.plot([-x, x * m], [0, -v], color='black', linewidth=line_strong)
80
+
81
+ ax.plot([-x, -2 * x], [u, 0], color='black', linewidth=0.5)
82
+ ax.plot([-2 * x, x * m], [0, -v], color='black', linewidth=0.5)
83
+ if magnification:
84
+ ax.plot([x, -x * m], [u, -v], color='black', linewidth=0.5)
85
+ ax.plot([x, x], [u, 0], color='black', linewidth=0.5)
86
+ ax.plot([x, -x * m], [0, -v], color='black', linewidth=0.5)
87
+
88
+ ax.plot([x, 2 * x], [u, 0], color='black', linewidth=0.5)
89
+ ax.plot([2 * x, -x * m], [0, -v], color='black', linewidth=0.5)
90
+ else:
91
+ ax.plot([-x, x * m], [u, 0], color='black', linewidth=0.5)
92
+ ax.plot([x * m, x * m], [0, -v], color='black', linewidth=0.5)
93
+
94
+ ax.set_xlim(-2, 3)
95
+ ax.set_ylim(-3.5, 2)
96
+ ax.set_aspect('equal')
97
+
98
+
99
+ # ----------------------------------------------------------------
100
+ # Modified from Michael Fairchild :simply draws a thin-lens at the provided location parameters:
101
+ # - z: location along the optical axis (in mm)
102
+ # - f: focal length (in mm, can be negative if div. lens)
103
+ # - diam: lens diameter in mm
104
+ # - lens_labels: label to identify the lens on the drawing
105
+ # ----------------------------------------------------------------
106
+ def add_lens(z, f, diam, lens_labels):
107
+ """add lens to propagate beam plot"""
108
+ ww, tw, rad = diam / 10.0, diam / 3.0, diam / 2.0
109
+ plt.plot([z, z], [-rad, rad], 'k', linewidth=2)
110
+ plt.plot([z, z + tw], [-rad, -rad + np.sign(f) * ww], 'k', linewidth=2)
111
+ plt.plot([z, z - tw], [-rad, -rad + np.sign(f) * ww], 'k', linewidth=2)
112
+ plt.plot([z, z + tw], [rad, rad - np.sign(f) * ww], 'k', linewidth=2)
113
+ plt.plot([z, z - tw], [rad, rad - np.sign(f) * ww], 'k', linewidth=2)
114
+ plt.plot([z + f, z + f], [-ww, ww], 'k', linewidth=2)
115
+ plt.plot([z - f, z - f], [-ww, ww], 'k', linewidth=2)
116
+ plt.text(z, rad + 5.0, lens_labels, fontsize=12)
117
+ plt.text(z, rad + 2.0, 'f=' + str(int(f)), fontsize=10)
118
+
119
+
120
+ def add_aperture(z, diam, radius, lens_labels):
121
+ """add aperture to propagate beam plot"""
122
+
123
+ ww, tw, rad = diam / 10.0, diam / 3.0, diam / 2.0
124
+ radius = radius / 2
125
+ plt.plot([z, z], [-rad, -radius], 'k', linewidth=2)
126
+ plt.plot([z, z], [rad, radius], 'k', linewidth=2)
127
+ plt.text(z, -rad - 2.0, lens_labels, fontsize=12)
128
+
129
+
130
+ def propagate_beam(source_position, numerical_aperture, number_of_rays, lens_positions, focal_lengths,
131
+ lens_labels='', color='b'):
132
+ """geometrical propagation of light rays from given source
133
+
134
+ Parameters
135
+ ----------
136
+ source_position: list
137
+ location of the source (z0, x0) along and off axis (in mm)
138
+ numerical_aperture: float
139
+ numerical aperture of the beam (in degrees)
140
+ number_of_rays: int
141
+ number of rays to trace
142
+ lens_positions: numpy array
143
+ array with the location of the lenses
144
+ focal_lengths: numpy array
145
+ array with the focal length of lenses
146
+ lens_labels: list of string
147
+ label for the nature of lenses
148
+ color: str
149
+ color of the rays on plot
150
+ """
151
+
152
+ plt.figure()
153
+ z_max = 1600.
154
+
155
+ # aperture (maximum angle) in radians
156
+ apa = numerical_aperture * np.pi / 180.0
157
+
158
+ for i in range(np.size(lens_positions)):
159
+ add_lens(lens_positions[i], focal_lengths[i], 25, lens_labels[i])
160
+
161
+ add_aperture(840, 25, 7, 'CA')
162
+
163
+ # position of source is z0,x0
164
+ z0 = source_position[0]
165
+ if np.size(source_position) == 2:
166
+ x0 = source_position[1]
167
+ else:
168
+ x0 = 0.0
169
+
170
+ # list of lens positions
171
+ zl1, ff1 = lens_positions[(z0 < lens_positions)], focal_lengths[(z0 < lens_positions)]
172
+ nl = np.size(zl1) # number of lenses
173
+
174
+ zz, xx, tani = np.zeros(nl + 2), np.zeros(nl + 2), np.zeros(nl + 2)
175
+ tan0 = np.tan(apa / 2.0) - np.tan(apa) * np.arange(number_of_rays) / (number_of_rays - 1)
176
+
177
+ for i in range(number_of_rays):
178
+ tani[0] = tan0[i] # initial incidence angle
179
+ zz[0], xx[0] = z0, x0
180
+ for j in range(nl):
181
+ zz[j + 1] = zl1[j]
182
+ xx[j + 1] = xx[j] + (zz[j + 1] - zz[j]) * tani[j]
183
+ tani[j + 1] = tani[j] - xx[j + 1] / ff1[j]
184
+
185
+ zz[nl + 1] = z_max
186
+ xx[nl + 1] = xx[nl] + (zz[nl + 1] - zz[nl]) * tani[nl]
187
+ plt.plot(zz, xx, color)
188
+ plt.axis([-20, z_max, -20, 20])
189
+
190
+
191
+ def deficient_holz_line(exact_bragg=False, shift=False, laue_zone=1, color='black'):
192
+ """
193
+ Ewald sphere construction to explain Laue Circle and deficient HOLZ lines
194
+
195
+ Parameters:
196
+ exact_bragg: boolean
197
+ whether to tilt into exact Bragg condition or along zone axis
198
+ shift: boolean
199
+ whether to shift exact Bragg-condition onto zone axis origin
200
+ laue_zone: int
201
+ first or second Laue zone only
202
+ color: string
203
+ color of wave vectors and Ewald sphere
204
+ """
205
+
206
+ k_0 = [0, 1 / ks.get_wavelength(600)]
207
+
208
+ d = 5. # lattice parameter in nm
209
+
210
+ if laue_zone == 0:
211
+ s_g = 1 / d + 0.06
212
+ else:
213
+ s_g = .1
214
+
215
+ g = np.linspace(-5, 6, 12) * 1 / d
216
+ g_d = np.array([5. / d + laue_zone * 1 / d / 2, laue_zone * 1 / d])
217
+ g_sg = g_d.copy()
218
+ g_sg[1] = g_d[1] + s_g # point on Ewald sphere
219
+
220
+ # reciprocal lattice
221
+ plt.scatter(g[:-1], [0] * 11, color='red')
222
+ plt.scatter(g - 1 / d / 2, [1 / d] * 12, color='blue')
223
+
224
+ shift_x = shift_y = 0.
225
+ d_theta = d_theta1 = d_theta2 = 0
226
+
227
+ if exact_bragg:
228
+
229
+ d_theta1 = np.arctan((1 / d * laue_zone + s_g) / g_d[0])
230
+ d_theta2 = np.arctan((1 / d * laue_zone) / g_d[0])
231
+ d_theta = -(d_theta1 - d_theta2)
232
+ s_g = 0
233
+ s = np.sin(d_theta)
234
+ c = np.cos(d_theta)
235
+ k_0 = [-s * k_0[1], c * k_0[1]]
236
+ if shift:
237
+ shift_x = -k_0[0]
238
+ shift_y = np.linalg.norm(k_0) - k_0[1]
239
+ d_theta = np.degrees(d_theta)
240
+
241
+ k_0[0] += shift_x
242
+ k_0[1] += shift_y
243
+
244
+ # Ewald Sphere
245
+ ewald_sphere = patches.Circle((k_0[0], k_0[1]), radius=np.linalg.norm(k_0), clip_on=False, zorder=10, linewidth=1,
246
+ edgecolor=color, fill=False)
247
+ plt.gca().add_artist(ewald_sphere)
248
+
249
+ plt.gca().arrow(g[-1] + .1 / d / 4, 1 / d / 2, 0, 1 / d / 2, head_width=0.03, head_length=0.04, fc='k', ec='k',
250
+ length_includes_head=True)
251
+ plt.gca().arrow(g[-1] + .1 / d / 4, 1 / d / 2, 0, -1 / d / 2, head_width=0.03, head_length=0.04, fc='k', ec='k',
252
+ length_includes_head=True)
253
+ plt.gca().annotate("$|g_{HOLZ}|$", xytext=(g[-1] + .1 / d / 3, 1 / d / 3), xy=(g[-1] + 1 / d / 3, 1 / d / 3))
254
+
255
+ # k_0
256
+ plt.scatter(k_0[0], k_0[1])
257
+ plt.gca().arrow(k_0[0], k_0[1], -k_0[0] + shift_x, -k_0[1] + shift_y, head_width=0.03, head_length=0.04, fc=color,
258
+ ec=color, length_includes_head=True)
259
+ plt.gca().annotate("K$_0$", xytext=(k_0[0] / 2, k_0[1] / 3), xy=(k_0[0] / 2, k_0[1] / 2))
260
+
261
+ # K_d Bragg of HOLZ reflection
262
+ plt.gca().arrow(k_0[0], k_0[1], -k_0[0] + g_d[0] + shift_x, -k_0[1] + g_d[1] + s_g + shift_y, head_width=0.03,
263
+ head_length=0.04, fc=color,
264
+ ec=color, length_includes_head=True)
265
+ plt.gca().annotate("K$_d$", xytext=(k_0[0] + (g_d[0] - k_0[0]) / 2, k_0[1] / 2), xy=(6.5 / d / 2, k_0[1] / 2))
266
+
267
+ # s_g excitation Error of HOLZ reflection
268
+ if s_g > 0:
269
+ plt.gca().arrow(g_d[0], g_d[1], 0, s_g, head_width=0.03, head_length=0.04, fc='k',
270
+ ec='k', length_includes_head=True)
271
+ plt.gca().annotate("s$_g$", xytext=(g_d[0] * 1.01, g_d[1] + s_g / 3), xy=(g_d[0] * 1.01, g_d[1] + s_g / 3))
272
+
273
+ # Bragg angle
274
+ g_sg = g_d
275
+ g_sg[1] = g_d[1] + s_g
276
+ plt.plot([0 + shift_x, g_sg[0] + shift_x], [0 + shift_y, g_d[1] + shift_y], color=color, linewidth=1, alpha=0.5,
277
+ linestyle='--')
278
+ plt.plot([k_0[0], g_sg[0] / 2 + shift_x], [k_0[1], g_sg[1] / 2 + shift_y], color=color, linewidth=1, alpha=0.5,
279
+ linestyle='--')
280
+ # d_theta = np.degrees(np.arctan(k_0[0]/k_0[1]))
281
+ bragg_angle = patches.Arc((k_0[0], k_0[1]), width=k_0[1], height=k_0[1], theta1=-90 + d_theta,
282
+ theta2=-90 + d_theta + np.degrees(np.arcsin(np.linalg.norm(g_sg / 2) / k_0[1])), fc=color,
283
+ ec=color)
284
+
285
+ plt.gca().annotate(r"$\theta $", xytext=(k_0[0] / 1.3, k_0[1] / 1.5), xy=(k_0[0] / 2 + g_d[0] / 4, k_0[1] / 2))
286
+ plt.gca().add_patch(bragg_angle)
287
+
288
+ # deviation/tilt angle
289
+ if np.abs(d_theta) > 0:
290
+ if shift:
291
+ deviation_angle = patches.Arc((k_0[0], k_0[1]), width=k_0[1] * 1.5, height=k_0[1] * 1.5,
292
+ theta1=-90 + d_theta,
293
+ theta2=-90,
294
+ fc=color, ec=color, linewidth=3)
295
+ plt.gca().annotate(r"$d \theta $", xytext=(k_0[0] - .13, k_0[1] / 3.7),
296
+ xy=(k_0[0] + g_d[0] / 4, k_0[1] / 2))
297
+ plt.gca().arrow(shift_x, -.2, 0, .2, head_width=0.05, head_length=0.06, fc=color, ec='black',
298
+ length_includes_head=True, linewidth=3)
299
+ plt.gca().annotate("deficient line", xytext=(shift_x * 2, -.2), xy=(shift_x, 0))
300
+ else:
301
+ deviation_angle = patches.Arc((0, 0), width=k_0[1], height=k_0[1],
302
+ theta1=np.degrees(d_theta2),
303
+ theta2=np.degrees(d_theta1),
304
+ fc=color, ec=color, linewidth=3)
305
+ plt.gca().annotate(r"$d \theta $", xytext=(g_d[0] * .8, 1 / d / 3), xy=(g_d[0], 1 / d))
306
+
307
+ plt.gca().add_patch(deviation_angle)
308
+ plt.gca().set_aspect('equal')
309
+ plt.gca().set_ylim(-.5, 2.2)
310
+ plt.gca().set_xlim(-1.1, 1.6)
311
+
312
+
313
+ def deficient_kikuchi_line(s_g=0., color_b='black'):
314
+ k_len = 1 / ks.get_wavelength(20)
315
+ d = 2 # lattice parameter in nm
316
+
317
+ g = np.linspace(-2, 2, 5) * 1 / d
318
+ g_d = np.array([1 / d, 0])
319
+
320
+ # reciprocal lattice
321
+ plt.scatter(g, [0] * 5, color='blue')
322
+
323
+ alpha = -np.arctan(s_g / g_d[0])
324
+ theta = -np.arcsin(g_d[0] / 2 / k_len)
325
+
326
+ k_0 = np.array([-np.sin(theta - alpha) * k_len, np.cos(theta - alpha) * k_len])
327
+ k_d = np.array([-np.sin(-theta - alpha) * k_len, np.cos(-theta - alpha) * k_len])
328
+ k_i = np.array([-np.sin(theta - alpha) * 1., np.cos(theta - alpha) * 1.])
329
+ k_i_t = np.array([-np.sin(-alpha), np.cos(-alpha)])
330
+
331
+ kk_e = np.array([-np.sin(-theta) * k_len, np.cos(-theta) * k_len])
332
+ kk_d = np.array([-np.sin(theta) * k_len, np.cos(theta) * k_len])
333
+
334
+ # Ewald Sphere
335
+ ewald_sphere = patches.Circle((k_0[0], k_0[1]), radius=np.linalg.norm(k_0), clip_on=False, zorder=10, linewidth=1,
336
+ edgecolor=color_b, fill=False)
337
+ plt.gca().add_artist(ewald_sphere)
338
+
339
+ # K_0
340
+ plt.plot([k_0[0], k_0[0]], [k_0[1], k_0[1] + .4], color='gray', linestyle='-', alpha=0.3)
341
+
342
+ plt.gca().arrow(k_0[0] + k_i[0], k_0[1] + k_i[1], -k_i[0], -k_i[1], head_width=0.01, head_length=0.015, fc=color_b,
343
+ ec=color_b, length_includes_head=True)
344
+ plt.plot([k_0[0] + k_i_t[0], k_0[0] - k_i_t[0]], [k_0[1] + k_i_t[1], k_0[1] - k_i_t[1]], color='black',
345
+ linestyle='--', alpha=0.5)
346
+ plt.scatter(k_0[0], k_0[1], color='black')
347
+ plt.gca().arrow(k_0[0], k_0[1], -k_0[0], -k_0[1], head_width=0.01, head_length=0.015, fc=color_b,
348
+ ec=color_b, length_includes_head=True)
349
+ plt.gca().annotate("K$_0$", xytext=(-k_0[0] / 2, 0), xy=(k_0[0] / 2, 0))
350
+
351
+ plt.gca().arrow(k_0[0], k_0[1], -k_d[0], -k_d[1], head_width=0.01, head_length=0.015, fc=color_b,
352
+ ec=color_b, length_includes_head=True)
353
+ # K_e excess line
354
+ plt.gca().arrow(k_0[0], k_0[1], -kk_e[0], -kk_e[1], head_width=0.01, head_length=0.015, fc='red',
355
+ ec='red', length_includes_head=True)
356
+ plt.gca().annotate("excess", xytext=(k_0[0] - kk_e[0], -1), xy=(-kk_e[0] + k_0[0], 0))
357
+ plt.plot([k_0[0] - kk_e[0], k_0[0] - kk_e[0]], [-.1, .1], color='red')
358
+
359
+ # k_d deficient line
360
+ plt.gca().arrow(k_0[0], k_0[1], -kk_d[0], -kk_d[1], head_width=0.01, head_length=0.015, fc='blue',
361
+ ec='blue', length_includes_head=True)
362
+ plt.plot([k_0[0] - kk_d[0], k_0[0] - kk_d[0]], [-.1, .1], color='blue')
363
+ plt.gca().annotate("deficient", xytext=(k_0[0] - kk_d[0], -1), xy=(k_0[0] - kk_d[0], 0))
364
+
365
+ # s_g excitation Error of HOLZ reflection
366
+ plt.gca().arrow(g_d[0], g_d[1], 0, s_g, head_width=0.01, head_length=0.015, fc='k',
367
+ ec='k', length_includes_head=True)
368
+ plt.gca().annotate("s$_g$", xytext=(g_d[0] * 1.01, g_d[1] + s_g / 3), xy=(g_d[0] * 1.01, g_d[1] + s_g / 3))
369
+
370
+ theta = np.degrees(theta)
371
+ alpha = np.degrees(alpha)
372
+
373
+ bragg_angle = patches.Arc((k_0[0], k_0[1]), width=.55, height=.55,
374
+ theta1=90 + theta - alpha, theta2=90 - alpha, fc='black', ec='black')
375
+ if alpha > 0:
376
+ deviation_angle = patches.Arc((k_0[0], k_0[1]), width=.6, height=.6,
377
+ theta1=90 - alpha, theta2=90, fc='black', ec='red')
378
+ else:
379
+ deviation_angle = patches.Arc((k_0[0], k_0[1]), width=.6, height=.6,
380
+ theta1=90, theta2=90 - alpha, fc='black', ec='red')
381
+
382
+ plt.gca().annotate(r"$\theta$", xytext=(k_0[0] + k_i_t[0] / 20, k_0[1] + .2), xy=(k_0[0] + k_i_t[0], k_0[1] + .2))
383
+ plt.gca().annotate(r"$\alpha$", xytext=(k_0[0] + k_i_t[0] / 10, k_0[1] + .3), xy=(k_0[0] + k_i_t[0], k_0[1] + .3),
384
+ color='red')
385
+ plt.gca().add_patch(bragg_angle)
386
+ plt.gca().add_patch(deviation_angle)
387
+
388
+ plt.gca().set_aspect('equal')
389
+ plt.gca().set_xlabel('angle (1/$\AA$)')
390
+ plt.gca().set_ylim(-.1, k_0[1] * 2.2)
391
+ plt.gca().set_xlim(-.2, 1.03)
392
+
393
+
394
+ class InteractiveAberration(object):
395
+ """
396
+ ### Interactive explanation of aberrations
397
+
398
+ """
399
+
400
+ def __init__(self, horizontal=True):
401
+
402
+ box_layout = widgets.Layout(display='flex',
403
+ flex_flow='row',
404
+ align_items='stretch',
405
+ width='100%')
406
+
407
+ self.words = ['ideal rays', 'aberrated rays', 'aberrated wavefront', 'aberration function']
408
+
409
+ self.buttons = [widgets.ToggleButton(value=False, description=word, disabled=False) for word in self.words]
410
+ box = widgets.Box(children=self.buttons, layout=box_layout)
411
+ display(box)
412
+
413
+ # Button(description='edge_quantification')
414
+ for button in self.buttons:
415
+ button.observe(self.on_button_clicked, 'value') # on_click(self.on_button_clicked)
416
+
417
+ self.figure = plt.figure()
418
+ self.ax = plt.gca()
419
+ self.horizontal = horizontal
420
+ self.ax.set_aspect('equal')
421
+ self.analysis = []
422
+ self.update()
423
+ # self.cid = self.figure.canvas.mpl_connect('button_press_event', self.onclick)
424
+
425
+ def on_button_clicked(self, b):
426
+ # print(b['owner'].description)
427
+ selection = b['owner'].description
428
+ if selection in self.analysis:
429
+ self.analysis.remove(selection)
430
+ else:
431
+ self.analysis.append(selection)
432
+ self.update()
433
+
434
+ def update(self):
435
+ ax = self.ax
436
+ ax.clear()
437
+ selection = self.analysis
438
+ ax.plot([0, 15], [0, 0], color='black')
439
+ ax.plot([9, 9], [-.3, .3], color='black')
440
+ lens = patches.Ellipse((2, 0),
441
+ width=.4,
442
+ height=7,
443
+ facecolor='gray')
444
+ ax.add_patch(lens)
445
+ ax.set_ylim(-6.5, 6.5)
446
+ ax.set_aspect('equal')
447
+
448
+ if self.words[0] in selection:
449
+ color = 'gray'
450
+ ax.plot([0, 2], [1, 1], color=color)
451
+ ax.plot([0, 2], [-1, -1], color=color)
452
+ ax.plot([2, 9], [1, 0], color=color)
453
+ ax.plot([2, 9], [-1, 0], color=color)
454
+
455
+ gauss = patches.Ellipse((9, 0),
456
+ width=12,
457
+ height=12,
458
+ fill=False)
459
+ ax.add_patch(gauss)
460
+
461
+ if self.words[1] in selection:
462
+ color = 'blue'
463
+ ax.plot([0, 2], [2, 2], color=color)
464
+ ax.plot([0, 2], [-2, -2], color=color)
465
+ ax.plot([2, 7], [2, 0], color=color)
466
+ ax.plot([2, 7], [-2, 0], color=color)
467
+ gauss2 = patches.Ellipse((7, 0),
468
+ width=8,
469
+ height=8,
470
+ fill=False,
471
+ color=color, linestyle='--')
472
+ plt.gca().add_patch(gauss2)
473
+
474
+ if self.words[2] in selection:
475
+ color = 'red'
476
+ ax.plot([0, 2], [2, 2], color=color)
477
+ ax.plot([0, 2], [-2, -2], color=color)
478
+ ax.plot([2, 7], [2, 0], color=color)
479
+ ax.plot([2, 7], [-2, 0], color=color)
480
+ ax.plot([0, 2], [1, 1], color=color)
481
+ ax.plot([0, 2], [-1, -1], color=color)
482
+ ax.plot([2, 9], [1, 0], color=color)
483
+ ax.plot([2, 9], [-1, 0], color=color)
484
+ gauss3 = patches.Ellipse((9, 0),
485
+ width=12,
486
+ height=9.7,
487
+ fill=False,
488
+ color=color)
489
+ plt.gca().add_patch(gauss3)
490
+
491
+ if self.words[3] in selection:
492
+ color = 'green'
493
+ x = np.arange(100) / 100 - 6
494
+ x2 = np.arange(100) / 100 * 1.5 - 6
495
+ b = 4.8
496
+ a = 6
497
+ y = np.sqrt(a ** 2 - x ** 2)
498
+ y2 = b / a * np.sqrt(a ** 2 - x2 ** 2)
499
+
500
+ x = np.append(x[::-1], x[1:])
501
+ y = np.append(y[::-1], -y[1:])
502
+ x2 = np.append(x2[::-1], x2[1:])
503
+ y2 = np.append(y2[::-1], -y2[1:])
504
+
505
+ dif = y2 - y
506
+
507
+ x = np.append(x[::-1], x2)
508
+ y = np.append(y[::-1], y2)
509
+ aberration = patches.Polygon(np.array([x + 9, y]).T,
510
+ fill=True,
511
+ color=color, alpha=.5)
512
+
513
+ aberration2 = patches.Polygon(np.array(
514
+ [np.append(np.abs(dif), [0, 0]) * 2 + 2.5, np.append(np.linspace(-3.3, 3.3, len(dif)), [3.3, -3.3])]).T,
515
+ fill=True,
516
+ color=color, alpha=.9)
517
+
518
+ plt.gca().add_patch(aberration)
519
+ plt.gca().add_patch(aberration2)
520
+
521
+
522
+ class InteractiveRonchigramMagnification(object):
523
+ """
524
+ ### Interactive explanation of magnification
525
+
526
+ """
527
+
528
+ def __init__(self, horizontal=True):
529
+
530
+ box_layout = widgets.Layout(display='flex',
531
+ flex_flow='row',
532
+ align_items='stretch',
533
+ width='100%')
534
+
535
+ self.words = ['ideal rays', 'radial circle rays', 'axial circle rays', 'over-focused rays']
536
+
537
+ self.buttons = [widgets.ToggleButton(value=False, description=word, disabled=False) for word in self.words]
538
+ box = widgets.Box(children=self.buttons, layout=box_layout)
539
+ display(box)
540
+
541
+ # Button(description='edge_quantification')
542
+ for button in self.buttons:
543
+ button.observe(self.on_button_clicked, 'value') # on_click(self.on_button_clicked)
544
+
545
+ self.figure = plt.figure()
546
+ self.ax = plt.gca()
547
+ self.horizontal = horizontal
548
+ self.ax.set_aspect('equal')
549
+ self.analysis = []
550
+ self.update()
551
+ # self.cid = self.figure.canvas.mpl_connect('button_press_event', self.onclick)
552
+
553
+ def on_button_clicked(self, b):
554
+ # print(b['owner'].description)
555
+ selection = b['owner'].description
556
+ if selection in self.analysis:
557
+ self.analysis.remove(selection)
558
+ else:
559
+ self.analysis.append(selection)
560
+ self.update()
561
+
562
+ def update(self):
563
+ ax = self.ax
564
+ ax.clear()
565
+ selection = self.analysis
566
+ ax.plot([0, 24], [0, 0], color='black')
567
+ ax.plot([14, 14], [-.3, .3], color='black')
568
+ ax.text(14, 1, s='f')
569
+ lens = patches.Ellipse((4, 0),
570
+ width=.8,
571
+ height=14,
572
+ facecolor='gray')
573
+ ax.add_patch(lens)
574
+ ax.text(4, 8, s='lens')
575
+ sample = patches.Rectangle((10, -2),
576
+ width=.2,
577
+ height=4,
578
+ facecolor='gray')
579
+
580
+ ax.add_patch(sample)
581
+ ax.text(9, 3, s='sample')
582
+ ax.set_ylim(-10, 10)
583
+ ax.set_aspect('equal')
584
+
585
+ if self.words[0] in selection:
586
+ color = 'gray'
587
+ ax.plot([0, 4], [1, 1], color=color)
588
+ ax.plot([0, 4], [-1, -1], color=color)
589
+ ax.plot([4, 24], [1, -1], color=color)
590
+ ax.plot([4, 24], [-1, 1], color=color)
591
+
592
+ circle1 = patches.Ellipse((24, 0), width=.2, height=2, fill=False, color=color)
593
+ ax.add_patch(circle1)
594
+
595
+ if self.words[1] in selection:
596
+ color = 'red'
597
+ ax.plot([0, 4], [3, 3], color=color)
598
+ ax.plot([0, 4], [-3, -3], color=color)
599
+ ax.plot([4, 24], [3, -4], color=color)
600
+ ax.plot([4, 24], [-3, 4], color=color)
601
+ ax.plot([0, 4], [2.5, 2.5], color=color)
602
+ ax.plot([0, 4], [-2.50, -2.5], color=color)
603
+ ax.plot([4, 24], [2.5, -2.8], color=color)
604
+ ax.plot([4, 24], [-2.5, 2.8], color=color)
605
+
606
+ circle2 = patches.Ellipse((24, 0), width=.9, height=8, fill=False, color=color)
607
+ ax.add_patch(circle2)
608
+ circle3 = patches.Ellipse((24, 0), width=.6, height=5.6, fill=False, color=color)
609
+ ax.add_patch(circle3)
610
+ circle3 = patches.Ellipse((24, 0), width=.7, height=7.3, fill=False, color=color, linewidth=5, alpha=.5)
611
+ ax.add_patch(circle3)
612
+
613
+ if self.words[2] in selection:
614
+ color = 'orange'
615
+ ax.plot([0, 4], [4, 4], color=color)
616
+ ax.plot([0, 4], [-4, -4], color=color)
617
+ ax.plot([4, 24], [4, -9.25], color=color)
618
+ ax.plot([4, 24], [-4, 9.25], color=color)
619
+
620
+ circle4 = patches.Ellipse((24, 0), width=2, height=18.5, fill=False, color=color)
621
+ plt.gca().add_patch(circle4)
622
+
623
+ if self.words[3] in selection:
624
+ color = 'green'
625
+ ax.plot([0, 4], [5, 5], color=color, linestyle='--')
626
+ ax.plot([0, 4], [-5, -5], color=color, linestyle='--')
627
+ ax.plot([4, 24], [5, -13], color=color, linestyle='--')
628
+ ax.plot([4, 24], [-5, 13], color=color, linestyle='--')
629
+
630
+ circle6 = patches.Ellipse((24, 0), width=4, height=26, fill=False, color=color, linestyle='--')
631
+ plt.gca().add_patch(circle6)