pyTEMlib 0.2025.4.1__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 -915
  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.1.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.1.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.1.dist-info/RECORD +0 -38
  92. pytemlib-0.2025.4.1.dist-info/top_level.txt +0 -1
  93. {pytemlib-0.2025.4.1.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
  94. {pytemlib-0.2025.4.1.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -41,7 +41,7 @@ from pyTEMlib.diffraction_plot import *
41
41
 
42
42
  _version_ = "0.2022.1.0"
43
43
 
44
- print('Using kinematic_scattering library version {_version_ } by G.Duscher')
44
+ print(f'Using kinematic_scattering library version {_version_ } by G.Duscher')
45
45
 
46
46
  inputKeys = ['acceleration_voltage_V', 'zone_hkl', 'Sg_max', 'hkl_max']
47
47
  optional_inputKeys = ['crystal', 'lattice_parameter_nm', 'convergence_angle_mrad', 'mistilt', 'thickness',
pyTEMlib/probe_tools.py CHANGED
@@ -14,7 +14,7 @@ def make_gauss(size_x, size_y, width=1.0, x0=0.0, y0=0.0, intensity=1.0):
14
14
  size_y = size_y / 2
15
15
  x, y = np.mgrid[-size_x:size_x, -size_y:size_y]
16
16
  g = np.exp(-((x - x0) ** 2 + (y - y0) ** 2) / 2.0 / width ** 2)
17
- probe = g / g.sum() * intensity
17
+ probe = g / (g.sum()+ 0.00001) * intensity
18
18
 
19
19
  return probe
20
20
 
pyTEMlib/test.py ADDED
@@ -0,0 +1,437 @@
1
+ import sys
2
+
3
+ from matplotlib.pyplot import plot
4
+ sys.path.insert(0, './')
5
+ import pyTEMlib
6
+ print(pyTEMlib.__version__)
7
+
8
+ filename = "C:\\Users\\gduscher\\Desktop\\ESL-506-15kV.esl"
9
+ filename = "C:\\Users\\gduscher\\.pyTEMlib\\k-factors-Spectra300UTK200keV.csv"
10
+ # pyTEMlib.eds_tools.read_esl_k_factors("C:\\Users\\gduscher\\Desktop\\ESL-506-15kV.esl")
11
+
12
+ # print(pyTEMlib.eds_tools.read_csv_k_factors(filename))
13
+ # pp =pyTEMlib.eds_tools.convert_k_factor_file(filename)
14
+ # print('read bruker k-factors')
15
+ #
16
+ # print(pyTEMlib.eds_tools.read_k_factors('k_factors_Thermo_200keV.json'))
17
+
18
+ # print(pyTEMlib.eds_tools.get_k_factor_files())
19
+ import os
20
+ file = os.path.join(pyTEMlib.config_dir.config_path, 'Dirac_GOS.gosh')
21
+ import h5py
22
+
23
+ """
24
+ element = 'Si'
25
+ edge = 'L3'
26
+ with h5py.File(file, 'r') as gos_file:
27
+ gos = gos_file[element][edge]['data'][:].squeeze().T
28
+ free_energies = gos_file[element][edge]['free_energies'][:][ :] # two dimensional array
29
+ q_axis = gos_file[element][edge]['q'][:] # in [1/m]
30
+ ionization_energy = gos_file[element][edge]['metadata'].attrs['ionization_energy']
31
+ print (ionization_energy)
32
+
33
+ import matplotlib.pylab as plt
34
+ import scipy
35
+ import numpy as np
36
+
37
+ x = free_energies
38
+ y = q_axis
39
+ y = y/min(y)
40
+ z = gos
41
+ print(x.shape, y.shape, z.shape)
42
+ X = np.linspace(min(x), max(x))
43
+ Y = np.linspace(min(y), max(y))
44
+ print(min(y), max(y))
45
+ grid_x, grid_y = np.meshgrid(X, Y)
46
+
47
+ interp = scipy.interpolate.griddata(zip(x, y), z, (grid_x, grid_y), method='linear', fill_value=0)
48
+ print(interp.shape, print())
49
+
50
+ plt.imshow(interp[:,:,0].T, extent=(min(x), max(x), min(y), max(y)), origin='lower')
51
+
52
+ plt.colorbar()
53
+
54
+ plt.show()
55
+
56
+
57
+ plt.figure()
58
+ plt.pcolormesh( q_axis/1e10,free_energies+ionization_energy, gos)
59
+ plt.xlabel('q [1/A]')
60
+ plt.ylabel('Energy above ionization [eV]')
61
+ plt.colorbar()
62
+ plt.title('GOS for ' + element + ' ' + edge)
63
+ plt.show()
64
+ """
65
+ import matplotlib.pylab as plt
66
+
67
+ import scipy
68
+ import numpy as np
69
+
70
+
71
+
72
+
73
+
74
+
75
+ def getinterpolatedgos(E, q, E_axis, q_axis, GOSmatrix):
76
+ """
77
+ Gets the interpolated value of the GOS from the E and q value.
78
+ """
79
+ index_q = np.searchsorted(q_axis, q, side='left')
80
+ index_E = np.searchsorted(E_axis, E, side='left')
81
+
82
+ if index_E == 0:
83
+ return 0
84
+ if index_E == E_axis.size:
85
+ return 0.
86
+ if index_q == 0:
87
+ return GOSmatrix[index_E, 0]
88
+ if index_q == q_axis.size:
89
+ return 0.0
90
+
91
+ dE = E_axis[index_E] - E_axis[index_E - 1]
92
+ dq = q_axis[index_q] - q_axis[index_q - 1]
93
+
94
+ distE = E - E_axis[index_E - 1]
95
+ distq = q - q_axis[index_q - 1]
96
+
97
+ r = GOSmatrix[index_E - 1, index_q - 1] * (1 / (dE * dq)) * (dE - distE) * (dq - distq)
98
+ r += GOSmatrix[index_E - 1, index_q] * (1 / (dE * dq)) * (dE - distE) * (distq)
99
+ r += GOSmatrix[index_E, index_q - 1] * (1 / (dE * dq)) * (distE) * (dq - distq)
100
+ r += GOSmatrix[index_E, index_q] * (1 / (dE * dq)) * (distE) * (distq)
101
+ return r
102
+
103
+
104
+ def gaussian(E, integral, x0, sigma):
105
+ # A = integral / (np.sqrt(2 * np.pi) * sigma)
106
+ g = np.exp(-0.5 * (E - x0)**2 / sigma**2)
107
+ g = integral * g / g.sum()
108
+ return g
109
+
110
+ def getinterpolatedq(q, GOSarray, q_axis):
111
+ """
112
+ Gets the interpolated value of the GOS array as a function of q.
113
+ Usefull for the bounded states
114
+
115
+ Parameters
116
+ ----------
117
+ q: float
118
+ The q from the GOS should be interpolated
119
+ GOSarray:
120
+ dddd
121
+ q_axis: numpy array
122
+ The q axis on which the GOS is calculated
123
+
124
+
125
+ Returns
126
+ -------
127
+ interpolated GOS matrix
128
+
129
+ """
130
+ index_q = np.searchsorted(q_axis, q, side='left')
131
+
132
+ if index_q == 0:
133
+ return GOSarray[0]
134
+ if index_q == q_axis.size:
135
+ return 0.0
136
+
137
+ dq = q_axis[index_q] - q_axis[index_q - 1]
138
+
139
+ distq = q - q_axis[index_q - 1]
140
+
141
+ r0 = GOSarray[index_q - 1] * (1/dq) * (dq-distq)
142
+ r1 = GOSarray[index_q] * (1/dq) * (distq)
143
+
144
+ return r0 + r1
145
+
146
+ def correction_factor_kohl(alpha, beta, theta, min_alpha=1e-6):
147
+ """
148
+ STILL NEEDS TO BE VALIDATED
149
+ Calculates the correction factor when using a convergent
150
+ probe. For probes having is convergence angle smaller than
151
+ min_alpha no correction is applied.
152
+ Ultramicroscopy 16 (1985) 265-268:
153
+ https://doi.org/10.1016/0304-3991(85)90081-6
154
+ Parameters
155
+ ----------
156
+ alpha : float
157
+ Convergence angle in radians
158
+ beta : float
159
+ Collection angle in radians
160
+ theta : float
161
+ The angle for which the correction factor should be calculated
162
+ min_alpha : float
163
+ Minimum convergence angle for which the correction is applied
164
+
165
+ Returns
166
+ -------
167
+ corr_factor : float
168
+ correction factor used in the integration
169
+ """
170
+ if alpha < min_alpha:
171
+ corr_factor = 1.
172
+ elif theta <= np.abs(alpha - beta):
173
+ min_thetasq = min(alpha**2, beta**2)
174
+ corr_factor = min_thetasq / alpha**2
175
+ else:
176
+ x = (alpha**2 + theta**2 - beta**2) / (2. * alpha * theta)
177
+ y = (beta**2 + theta**2 - alpha**2) / (2. * beta * theta)
178
+ wortel = np.sqrt(4 * alpha**2 * beta**2 - (alpha**2 + beta**2 - theta**2)**2)
179
+ corr_factor = (1 / np.pi) * (np.arccos(x) + (beta**2 / alpha**2 * np.arccos(y)) - (
180
+ 1 / (2 * alpha**2) * wortel))
181
+ return corr_factor
182
+
183
+ def get_dirac_X_section(element = 'Si', edge = 'L3', file = file,
184
+ q_steps=100, E0=200000, beta=0.100, alpha=0):
185
+ """ Calculates the cross section from the GOS matrix"""
186
+
187
+
188
+ with h5py.File(file, 'r') as gos_file:
189
+ GOSmatrix = gos_file[element][edge]['data'][:].squeeze().T
190
+ free_energies = gos_file[element][edge]['free_energies'][:][ :] # two dimensional array
191
+ q_axis = gos_file[element][edge]['q'][:] # in [1/m]
192
+ ek = gos_file[element][edge]['metadata'].attrs['ionization_energy']
193
+
194
+ energy_axis = np.linspace(50, 850, int(800/5))
195
+ shell_occupancy = 1
196
+ pref = 1e28 * shell_occupancy
197
+
198
+ e = scipy.constants.e
199
+ c = scipy.constants.c
200
+ m = scipy.constants.electron_mass
201
+ a_0 = scipy.constants.physical_constants['Bohr radius'][0]
202
+ gamma = 1 + e * E0 / (m * c ** 2)
203
+
204
+ effective_incident_energy = E0 * (1 + gamma) / (2 * gamma**2)
205
+ T = effective_incident_energy
206
+ R = scipy.constants.Rydberg
207
+
208
+ bool0 = free_energies < 0
209
+ Ebound = free_energies[bool0] + ek
210
+
211
+ dsigma_dE = np.zeros(energy_axis.shape)
212
+ dsigma_dE_bound = np.zeros(energy_axis.shape)
213
+ sigma = 2*(energy_axis[1] - energy_axis[0])
214
+ rel_energy_axis = free_energies + ek
215
+
216
+ for i in range(Ebound.size):
217
+ E = Ebound[i]
218
+ integral = 0
219
+ # the bounded states are differently interpolated
220
+ qa0sq_min = E ** 2 / (4 * R * T) + (E ** 3) / (8 * gamma ** 3 * R * T ** 2)
221
+ qa0sq_max = qa0sq_min + 4 * gamma ** 2 * (T / R) * (np.sin((beta + alpha) / 2)) ** 2
222
+ logqa0sq_axis = np.linspace(np.log(qa0sq_min), np.log(qa0sq_max),
223
+ q_steps)
224
+ lnqa0sqstep = (logqa0sq_axis[1] - logqa0sq_axis[0])
225
+ print(i, logqa0sq_axis, lnqa0sqstep)
226
+ for j in range(logqa0sq_axis.size):
227
+ q = np.sqrt(np.exp(logqa0sq_axis[j])) / scipy.constants.physical_constants['Bohr radius'][0]
228
+ theta = 2. * np.sqrt(np.abs( R * (np.exp(logqa0sq_axis[j]) - qa0sq_min) /
229
+ (4. * gamma**2 * T)))
230
+ GOSarray = GOSmatrix[i, :]
231
+ df_dE = getinterpolatedq(q, GOSarray, q_axis)
232
+
233
+ # integral+= df_dE*lnqa0sqstep
234
+ integral += df_dE * lnqa0sqstep * correction_factor_kohl(alpha, beta, theta)
235
+
236
+ sig = 4 * np.pi * a_0 ** 2 * (R / E) * (R / T) * integral
237
+ dsigma_dE_bound += gaussian(energy_axis, sig, E, sigma)
238
+
239
+
240
+ # the for loop over the bound states
241
+ for i in range(energy_axis.size):
242
+ E = energy_axis[i]
243
+ integral = 0
244
+ if (E > ek) & (E <= rel_energy_axis[-1]):
245
+ qa0sq_min = E**2 / (4 * R * T) + (E**3) / (8 * gamma**3 * R * T**2)
246
+ qa0sq_max = qa0sq_min + 4 * gamma**2 * (T / R) * (np.sin((beta + alpha) / 2))**2
247
+ logqa0sq_axis = np.linspace(np.log(qa0sq_min), np.log(qa0sq_max), q_steps)
248
+ lnqa0sqstep = (logqa0sq_axis[1] - logqa0sq_axis[0])
249
+ for j in range(logqa0sq_axis.size):
250
+ q = np.sqrt(np.exp(logqa0sq_axis[j])) / a_0
251
+ theta = 2. * np.sqrt(np.abs(
252
+ R * (np.exp(logqa0sq_axis[j]) - qa0sq_min) / (
253
+ 4. * gamma ** 2 * T)))
254
+ df_dE = getinterpolatedgos(E, q, rel_energy_axis, q_axis, GOSmatrix)
255
+ # integral+= df_dE*lnqa0sqstep
256
+ integral += df_dE * lnqa0sqstep * correction_factor_kohl(alpha, beta, theta)
257
+ # dsigma_dE[i] = 4*np.pi*pc.a0()**2*(R/E)*(R/T)*integral*dispersion
258
+ dsigma_dE[i] = 4 * np.pi * a_0 ** 2 * (R / E) * (R / T) * integral
259
+ else:
260
+ dsigma_dE[i] = 0
261
+
262
+ cross_section = dsigma_dE + dsigma_dE_bound * pref
263
+
264
+ return cross_section
265
+
266
+ def energy2wavelength(e0: float) -> float:
267
+ """get deBroglie wavelength of electron accelerated by energy (in eV) e0"""
268
+ ev = scipy.constants.e * e0
269
+ m_e = scipy.constants.m_e
270
+ c = scipy.constants.c
271
+ h = scipy.constants.h
272
+ return h / np.sqrt(2 * m_e * ev * (1 + ev / (2 * m_e * c**2)))*1e10
273
+
274
+
275
+ def ddscs_dE_dOmega(free_energies, ek, E0, q_axis, GOSmatrix):
276
+ """scattering cross section as a function of energy loss and solid angle
277
+
278
+ Args:
279
+ free_energies: 1d numpy array
280
+ The energy axis on which the GOS table is calculated without the onset
281
+ energy [eV]
282
+ ek: float
283
+ The onset energy of the calculated edge [eV]
284
+ E0: float
285
+ The acceleration voltage of the incoming electrons [V]
286
+ q_axis: 1d numpy array
287
+ The momentum on which the GOS table are calculated. [kg m /s]?
288
+ GOSmatrix: 2d numpy array
289
+ The GOS
290
+
291
+ Returns:
292
+ np.array: scattering cross section as a function of energy loss and solid angle
293
+ """
294
+ R = scipy.constants.Rydberg
295
+ e = scipy.constants.e
296
+ e = scipy.constants.e
297
+ c = scipy.constants.c
298
+ m = scipy.constants.electron_mass
299
+ a_0 = scipy.constants.physical_constants['Bohr radius'][0]
300
+ gamma = 1 + e * E0 / (m * c ** 2)
301
+ energy_losses = free_energies + ek
302
+
303
+ k0 = 2 * np.pi / energy2wavelength(E0)
304
+
305
+ scs_list = []
306
+ for idx, epsilon in enumerate(free_energies):
307
+ kn = 2 * np.pi / energy2wavelength(E0-energy_losses[idx])
308
+ scs = (
309
+ 4
310
+ * gamma ** 2
311
+ / q_axis**2
312
+ * kn
313
+ / k0
314
+ * GOSmatrix[idx]
315
+ / energy_losses[idx]
316
+ * R
317
+ )
318
+ scs_list.append(scs)
319
+ scs_list = np.array(scs_list).squeeze()
320
+
321
+ return scs_list
322
+
323
+ def plot_ddscs(element = 'Si', edge = 'L3', file = file,
324
+ q_steps=100, E0=200000, beta=0.100, alpha=0):
325
+ with h5py.File(file, 'r') as gos_file:
326
+ GOSmatrix = gos_file[element][edge]['data'][:].squeeze().T
327
+ free_energies = gos_file[element][edge]['free_energies'][:][ :] # two dimensional array
328
+ q_axis = gos_file[element][edge]['q'][:] # in [1/m]
329
+ ek = gos_file[element][edge]['metadata'].attrs['ionization_energy']
330
+ for k in gos_file[element][edge]['metadata'].attrs.keys():
331
+ print(f"{k} => {gos_file[element][edge]['metadata'].attrs[k]}")
332
+ occupancy = gos_file[element][edge]['metadata'].attrs['occupancy_ratio']
333
+ k0 = 2 * np.pi / energy2wavelength(E0)
334
+ ddscs = ddscs_dE_dOmega(free_energies, ek, E0, q_axis/1e10, GOSmatrix)
335
+ plt.subplots(1, 2, figsize=(12, 5))
336
+ plt.subplot(121)
337
+ plt.pcolormesh(q_axis/1e10, free_energies, ddscs)
338
+ plt.ylabel('Energy loss [eV]')
339
+ plt.xlabel('Scattering vector [1/A]')
340
+ plt.colorbar()
341
+ plt.tight_layout()
342
+ plt.title('Double differential scattering cross section')
343
+ plt.subplot(122)
344
+ theta = np.arctan(q_axis/1e10 /k0)*1e3 # this is approximation
345
+ plt.pcolormesh(theta, free_energies, ddscs)
346
+ plt.xlim([0, 50])
347
+ plt.xlabel('Scattering angle [mrad]')
348
+ plt.ylabel('Energy loss [eV]')
349
+ plt.title('Double differential scattering cross section')
350
+ plt.colorbar()
351
+ plt.tight_layout()
352
+ max_q = np.searchsorted(theta, 30)
353
+ max_e = np.searchsorted(free_energies, 1000)
354
+ max_q_value = np.tan(30e-3)*k0*1e10
355
+ print(f"min_q: {q_axis[0]}, end_q: {q_axis[-1]}, max_q_value: {max_q_value}")
356
+ print(q_axis[max_q])
357
+ y_sparse = theta[:max_q]
358
+ x_sparse = free_energies[:max_e]
359
+ z_sparse = ddscs[:max_e, :max_q]
360
+ xnew = np.linspace(0, 1000, 1000)
361
+ ynew = np.linspace(0, 30, 100)
362
+
363
+ Xnew, Ynew = np.meshgrid(xnew, ynew)
364
+
365
+ # Flatten the input data for griddata
366
+ X_sparse, Y_sparse = np.meshgrid(x_sparse, y_sparse, indexing='ij')
367
+ points = np.column_stack([X_sparse.ravel(), Y_sparse.ravel()])
368
+ values = z_sparse.ravel()
369
+ znew_reggrid = scipy.interpolate.griddata(points, values, (Xnew, Ynew), method='linear', fill_value=0)
370
+ plt.figure()
371
+ plt.imshow(znew_reggrid, extent=(0, 400, 0, 30), origin='lower', aspect='auto')
372
+
373
+ plt.colorbar()
374
+
375
+ print(ddscs.shape, q_axis.shape, free_energies.shape, znew_reggrid.shape)
376
+
377
+ plt.figure()
378
+ #plt.plot(free_energies, ddscs[:, :max_q].sum(axis=1),)
379
+ plt.plot(xnew, znew_reggrid.sum(axis=0),)
380
+ plt.show()
381
+
382
+ def plot_ddscs(element = 'Si', edge = 'L3', file = file,
383
+ energy_scale = np.linspace(50, 850, int(800*3)),
384
+ q_steps=100, E0=200000, beta=0.100, alpha=0):
385
+ with h5py.File(file, 'r') as gos_file:
386
+ GOSmatrix = gos_file[element][edge]['data'][:].squeeze().T
387
+ free_energies = gos_file[element][edge]['free_energies'][:][ :] # two dimensional array
388
+ q_axis = gos_file[element][edge]['q'][:] # in [1/m]
389
+ ek = gos_file[element][edge]['metadata'].attrs['ionization_energy']
390
+ for k in gos_file[element][edge]['metadata'].attrs.keys():
391
+ print(f"{k} => {gos_file[element][edge]['metadata'].attrs[k]}")
392
+ occupancy = gos_file[element][edge]['metadata'].attrs['occupancy_ratio']
393
+
394
+
395
+ k0 = 2 * np.pi / energy2wavelength(E0)
396
+ ddscs = ddscs_dE_dOmega(free_energies, ek, E0, q_axis/1e10, GOSmatrix)
397
+ theta = np.arctan(q_axis/1e10 /k0)*1e3 # this is an approximation
398
+
399
+ max_q = np.searchsorted(theta, beta*1e3)+1
400
+ max_e = np.searchsorted(free_energies+ek, energy_scale[-1])+1
401
+ max_q_value = np.tan(beta)*k0
402
+ print(f"min_q: {q_axis[0]/1e10}, end_q: {q_axis[-1]/1e10}, max_q_value: {max_q_value}")
403
+ print(q_axis[max_q])
404
+ y_sparse = q_axis[:max_q]/1e10
405
+ x_sparse = free_energies[:max_e]+ek
406
+ z_sparse = ddscs[:max_e, :max_q]
407
+ xnew = energy_scale
408
+ ynew = np.linspace(0, max_q_value, q_steps)
409
+
410
+ Xnew, Ynew = np.meshgrid(xnew, ynew)
411
+
412
+ # Flatten the input data for griddata
413
+ X_sparse, Y_sparse = np.meshgrid(x_sparse, y_sparse, indexing='ij')
414
+ points = np.column_stack([X_sparse.ravel(), Y_sparse.ravel()])
415
+ values = z_sparse.ravel()
416
+ znew_reggrid = scipy.interpolate.griddata(points, values, (Xnew, Ynew), method='linear', fill_value=0)
417
+ plt.figure()
418
+ plt.imshow(znew_reggrid, extent=(xnew[0], xnew[-1], 0, ynew[-1]), origin='lower', aspect='auto')
419
+
420
+ plt.colorbar()
421
+ delta_q = ynew[1]-ynew[0]
422
+
423
+ print(ddscs.shape, q_axis.shape, free_energies.shape, znew_reggrid.shape)
424
+
425
+ plt.figure()
426
+ #plt.plot(free_energies, ddscs[:, :max_q].sum(axis=1),)
427
+ plt.plot(xnew, znew_reggrid.sum(axis=0)*delta_q,)
428
+ plt.show()
429
+
430
+ plot_ddscs(element='Si', edge='L3', beta=0.030, alpha=0.0)
431
+ plt.show()
432
+ #xsec = get_dirac_X_section(element='Si', edge='M2', beta=0.0001, alpha=0.0)
433
+ #print('calculated cross section')
434
+ #plt.figure()
435
+ #plt.plot(np.linspace(50, 850, int(800/5)), xsec)
436
+ #plt.xlabel('Energy loss [eV]')
437
+ #plt.show()