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.
- build/lib/pyTEMlib/__init__.py +33 -0
- build/lib/pyTEMlib/animation.py +640 -0
- build/lib/pyTEMlib/atom_tools.py +238 -0
- build/lib/pyTEMlib/config_dir.py +31 -0
- build/lib/pyTEMlib/crystal_tools.py +1219 -0
- build/lib/pyTEMlib/diffraction_plot.py +756 -0
- build/lib/pyTEMlib/dynamic_scattering.py +293 -0
- build/lib/pyTEMlib/eds_tools.py +826 -0
- build/lib/pyTEMlib/eds_xsections.py +432 -0
- build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
- build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
- build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
- build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
- build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
- build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
- build/lib/pyTEMlib/file_reader.py +274 -0
- build/lib/pyTEMlib/file_tools.py +811 -0
- build/lib/pyTEMlib/get_bote_salvat.py +69 -0
- build/lib/pyTEMlib/graph_tools.py +1153 -0
- build/lib/pyTEMlib/graph_viz.py +599 -0
- build/lib/pyTEMlib/image/__init__.py +37 -0
- build/lib/pyTEMlib/image/image_atoms.py +270 -0
- build/lib/pyTEMlib/image/image_clean.py +197 -0
- build/lib/pyTEMlib/image/image_distortion.py +299 -0
- build/lib/pyTEMlib/image/image_fft.py +277 -0
- build/lib/pyTEMlib/image/image_graph.py +926 -0
- build/lib/pyTEMlib/image/image_registration.py +316 -0
- build/lib/pyTEMlib/image/image_utilities.py +309 -0
- build/lib/pyTEMlib/image/image_window.py +421 -0
- build/lib/pyTEMlib/image_tools.py +699 -0
- build/lib/pyTEMlib/interactive_image.py +1 -0
- build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
- build/lib/pyTEMlib/microscope.py +61 -0
- build/lib/pyTEMlib/probe_tools.py +906 -0
- build/lib/pyTEMlib/sidpy_tools.py +153 -0
- build/lib/pyTEMlib/simulation_tools.py +104 -0
- build/lib/pyTEMlib/test.py +437 -0
- build/lib/pyTEMlib/utilities.py +314 -0
- build/lib/pyTEMlib/version.py +5 -0
- build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
- pyTEMlib/__init__.py +25 -3
- pyTEMlib/animation.py +31 -22
- pyTEMlib/atom_tools.py +29 -34
- pyTEMlib/config_dir.py +2 -28
- pyTEMlib/crystal_tools.py +129 -165
- pyTEMlib/eds_tools.py +559 -342
- pyTEMlib/eds_xsections.py +432 -0
- pyTEMlib/eels_tools/__init__.py +44 -0
- pyTEMlib/eels_tools/core_loss_tools.py +751 -0
- pyTEMlib/eels_tools/eels_database.py +134 -0
- pyTEMlib/eels_tools/low_loss_tools.py +655 -0
- pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
- pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
- pyTEMlib/file_reader.py +274 -0
- pyTEMlib/file_tools.py +260 -1130
- pyTEMlib/get_bote_salvat.py +69 -0
- pyTEMlib/graph_tools.py +101 -174
- pyTEMlib/graph_viz.py +150 -0
- pyTEMlib/image/__init__.py +37 -0
- pyTEMlib/image/image_atoms.py +270 -0
- pyTEMlib/image/image_clean.py +197 -0
- pyTEMlib/image/image_distortion.py +299 -0
- pyTEMlib/image/image_fft.py +277 -0
- pyTEMlib/image/image_graph.py +926 -0
- pyTEMlib/image/image_registration.py +316 -0
- pyTEMlib/image/image_utilities.py +309 -0
- pyTEMlib/image/image_window.py +421 -0
- pyTEMlib/image_tools.py +154 -928
- pyTEMlib/kinematic_scattering.py +1 -1
- pyTEMlib/probe_tools.py +1 -1
- pyTEMlib/test.py +437 -0
- pyTEMlib/utilities.py +314 -0
- pyTEMlib/version.py +2 -3
- pyTEMlib/xrpa_x_sections.py +14 -10
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
- pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
- pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
- pyTEMlib/core_loss_widget.py +0 -721
- pyTEMlib/eels_dialog.py +0 -754
- pyTEMlib/eels_dialog_utilities.py +0 -1199
- pyTEMlib/eels_tools.py +0 -2359
- pyTEMlib/file_tools_qt.py +0 -193
- pyTEMlib/image_dialog.py +0 -158
- pyTEMlib/image_dlg.py +0 -146
- pyTEMlib/info_widget.py +0 -1086
- pyTEMlib/info_widget3.py +0 -1120
- pyTEMlib/low_loss_widget.py +0 -479
- pyTEMlib/peak_dialog.py +0 -1129
- pyTEMlib/peak_dlg.py +0 -286
- pytemlib-0.2025.4.2.dist-info/RECORD +0 -38
- pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
- {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
pyTEMlib/kinematic_scattering.py
CHANGED
|
@@ -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()
|