pyTEMlib 0.2025.4.2__py3-none-any.whl → 0.2025.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyTEMlib might be problematic. Click here for more details.

Files changed (94) hide show
  1. build/lib/pyTEMlib/__init__.py +33 -0
  2. build/lib/pyTEMlib/animation.py +640 -0
  3. build/lib/pyTEMlib/atom_tools.py +238 -0
  4. build/lib/pyTEMlib/config_dir.py +31 -0
  5. build/lib/pyTEMlib/crystal_tools.py +1219 -0
  6. build/lib/pyTEMlib/diffraction_plot.py +756 -0
  7. build/lib/pyTEMlib/dynamic_scattering.py +293 -0
  8. build/lib/pyTEMlib/eds_tools.py +826 -0
  9. build/lib/pyTEMlib/eds_xsections.py +432 -0
  10. build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
  11. build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  12. build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
  13. build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  14. build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  15. build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  16. build/lib/pyTEMlib/file_reader.py +274 -0
  17. build/lib/pyTEMlib/file_tools.py +811 -0
  18. build/lib/pyTEMlib/get_bote_salvat.py +69 -0
  19. build/lib/pyTEMlib/graph_tools.py +1153 -0
  20. build/lib/pyTEMlib/graph_viz.py +599 -0
  21. build/lib/pyTEMlib/image/__init__.py +37 -0
  22. build/lib/pyTEMlib/image/image_atoms.py +270 -0
  23. build/lib/pyTEMlib/image/image_clean.py +197 -0
  24. build/lib/pyTEMlib/image/image_distortion.py +299 -0
  25. build/lib/pyTEMlib/image/image_fft.py +277 -0
  26. build/lib/pyTEMlib/image/image_graph.py +926 -0
  27. build/lib/pyTEMlib/image/image_registration.py +316 -0
  28. build/lib/pyTEMlib/image/image_utilities.py +309 -0
  29. build/lib/pyTEMlib/image/image_window.py +421 -0
  30. build/lib/pyTEMlib/image_tools.py +699 -0
  31. build/lib/pyTEMlib/interactive_image.py +1 -0
  32. build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
  33. build/lib/pyTEMlib/microscope.py +61 -0
  34. build/lib/pyTEMlib/probe_tools.py +906 -0
  35. build/lib/pyTEMlib/sidpy_tools.py +153 -0
  36. build/lib/pyTEMlib/simulation_tools.py +104 -0
  37. build/lib/pyTEMlib/test.py +437 -0
  38. build/lib/pyTEMlib/utilities.py +314 -0
  39. build/lib/pyTEMlib/version.py +5 -0
  40. build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
  41. pyTEMlib/__init__.py +25 -3
  42. pyTEMlib/animation.py +31 -22
  43. pyTEMlib/atom_tools.py +29 -34
  44. pyTEMlib/config_dir.py +2 -28
  45. pyTEMlib/crystal_tools.py +129 -165
  46. pyTEMlib/eds_tools.py +559 -342
  47. pyTEMlib/eds_xsections.py +432 -0
  48. pyTEMlib/eels_tools/__init__.py +44 -0
  49. pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  50. pyTEMlib/eels_tools/eels_database.py +134 -0
  51. pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  52. pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  53. pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  54. pyTEMlib/file_reader.py +274 -0
  55. pyTEMlib/file_tools.py +260 -1130
  56. pyTEMlib/get_bote_salvat.py +69 -0
  57. pyTEMlib/graph_tools.py +101 -174
  58. pyTEMlib/graph_viz.py +150 -0
  59. pyTEMlib/image/__init__.py +37 -0
  60. pyTEMlib/image/image_atoms.py +270 -0
  61. pyTEMlib/image/image_clean.py +197 -0
  62. pyTEMlib/image/image_distortion.py +299 -0
  63. pyTEMlib/image/image_fft.py +277 -0
  64. pyTEMlib/image/image_graph.py +926 -0
  65. pyTEMlib/image/image_registration.py +316 -0
  66. pyTEMlib/image/image_utilities.py +309 -0
  67. pyTEMlib/image/image_window.py +421 -0
  68. pyTEMlib/image_tools.py +154 -928
  69. pyTEMlib/kinematic_scattering.py +1 -1
  70. pyTEMlib/probe_tools.py +1 -1
  71. pyTEMlib/test.py +437 -0
  72. pyTEMlib/utilities.py +314 -0
  73. pyTEMlib/version.py +2 -3
  74. pyTEMlib/xrpa_x_sections.py +14 -10
  75. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
  76. pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
  77. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
  78. pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
  79. pyTEMlib/core_loss_widget.py +0 -721
  80. pyTEMlib/eels_dialog.py +0 -754
  81. pyTEMlib/eels_dialog_utilities.py +0 -1199
  82. pyTEMlib/eels_tools.py +0 -2359
  83. pyTEMlib/file_tools_qt.py +0 -193
  84. pyTEMlib/image_dialog.py +0 -158
  85. pyTEMlib/image_dlg.py +0 -146
  86. pyTEMlib/info_widget.py +0 -1086
  87. pyTEMlib/info_widget3.py +0 -1120
  88. pyTEMlib/low_loss_widget.py +0 -479
  89. pyTEMlib/peak_dialog.py +0 -1129
  90. pyTEMlib/peak_dlg.py +0 -286
  91. pytemlib-0.2025.4.2.dist-info/RECORD +0 -38
  92. pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
  93. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
  94. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,153 @@
1
+ """utility functions for sidpy; will move to sidpy"""
2
+ import numpy as np
3
+ import sidpy
4
+ import h5py
5
+ import pyNSID
6
+ import os
7
+ import ipywidgets as widgets
8
+ from IPython.display import display
9
+ import json
10
+
11
+
12
+ class ChooseDataset(object):
13
+ """Widget to select dataset object """
14
+
15
+ def __init__(self, input_object, show_dialog=True):
16
+ if isinstance(input_object, sidpy.Dataset):
17
+ if isinstance(input_object.h5_dataset, h5py.Dataset):
18
+ self.current_channel = input_object.h5_dataset.parent
19
+ elif isinstance(input_object, h5py.Group):
20
+ self.current_channel = input_object
21
+ elif isinstance(input_object, h5py.Dataset):
22
+ self.current_channel = input_object.parent
23
+ else:
24
+ raise ValueError('Need hdf5 group or sidpy Dataset to determine image choices')
25
+ self.dataset_names = []
26
+ self.dataset_list = []
27
+ self.dataset_type = None
28
+ self.dataset = None
29
+ self.reader = pyNSID.NSIDReader(self.current_channel.file.filename)
30
+
31
+ self.get_dataset_list()
32
+ self.select_image = widgets.Dropdown(options=self.dataset_names,
33
+ value=self.dataset_names[0],
34
+ description='select dataset:',
35
+ disabled=False,
36
+ button_style='')
37
+ if show_dialog:
38
+ display(self.select_image)
39
+
40
+ self.select_image.observe(self.set_dataset, names='value')
41
+ self.set_dataset(0)
42
+ self.select_image.index = (len(self.dataset_names) - 1)
43
+
44
+ def get_dataset_list(self):
45
+ """ Get by Log number sorted list of datasets"""
46
+ datasets = self.reader.read()
47
+ order = []
48
+ for dset in datasets:
49
+ if self.dataset_type is None or dset.data_type == self.data_type:
50
+ if 'Log' in dset.title:
51
+ position = dset.title.find('Log_') + 4
52
+ order.append(int(dset.title[position:position + 3])+1)
53
+ else:
54
+ order.append(0)
55
+ for index in np.argsort(order):
56
+ dset = datasets[index]
57
+ self.dataset_names.append('/'.join(dset.title.replace('-', '_').split('/')[-1:]))
58
+ self.dataset_list.append(dset)
59
+
60
+ def set_dataset(self, b):
61
+ index = self.select_image.index
62
+ self.dataset = self.dataset_list[index]
63
+ # Find
64
+ self.dataset.title = self.dataset.title.split('/')[-1]
65
+
66
+
67
+ def get_dimensions_by_order(dims_in, dataset):
68
+ """get dimension
69
+
70
+ Parameters
71
+ ----------
72
+ dims_in: int or list of int
73
+ the dimensions by numerical order
74
+ dataset: sidpy.Dataset
75
+
76
+ Returns
77
+ -------
78
+ dims_out: list of dimensions
79
+ """
80
+
81
+ if isinstance(dims_in, int):
82
+ dims_in = [dims_in]
83
+ dims_out = []
84
+ for item in dims_in:
85
+ if isinstance(item, int):
86
+ if item in dataset._axes:
87
+ dims_out.append([item, dataset._axes[item]])
88
+ return dims_out
89
+
90
+
91
+ def get_dimensions_by_type(dims_in, dataset):
92
+ """ get dimension by dimension_type name
93
+
94
+ Parameters
95
+ ----------
96
+ dims_in: dimension_type or list of dimension_types
97
+ the dimensions by numerical order
98
+ dataset: sidpy.Dataset
99
+
100
+ Returns
101
+ -------
102
+ dims_out: list of dimensions
103
+ """
104
+
105
+ if isinstance(dims_in, (str, sidpy.DimensionType)):
106
+ dims_in = [dims_in]
107
+ for i in range(len(dims_in)):
108
+ if isinstance(dims_in[i], str):
109
+ dims_in[i] = sidpy.DimensionType[dims_in[i].upper()]
110
+ dims_out = []
111
+ for dim, axis in dataset._axes.items():
112
+ if axis.dimension_type in dims_in:
113
+ dims_out.append([dim, dataset._axes[dim]])
114
+ return dims_out
115
+
116
+
117
+ def make_dummy_dataset(value_type):
118
+ """Make a dummy sidpy.Dataset """
119
+
120
+ assert isinstance(value_type, sidpy.DataType)
121
+ if type == sidpy.DataType.SPECTRUM:
122
+ dataset = sidpy.Dataset.from_array(np.arange(100))
123
+ dataset.data_type = 'spectrum'
124
+ dataset.units = 'counts'
125
+ dataset.quantity = 'intensity'
126
+
127
+ dataset.set_dimension(0, sidpy.Dimension(np.arange(dataset.shape[0]) + 70, name='energy_scale'))
128
+ dataset.dim_0.dimension_type = 'spectral'
129
+ dataset.dim_0.units = 'eV'
130
+ dataset.dim_0.quantity = 'energy loss'
131
+ else:
132
+ raise NotImplementedError('not implemented')
133
+ return dataset
134
+
135
+
136
+ def plot(dataset):
137
+ dataset.plot()
138
+
139
+
140
+ def get_image_dims(dataset):
141
+ """Get all spatial dimensions"""
142
+
143
+ image_dims = []
144
+ for dim, axis in dataset._axes.items():
145
+ if axis.dimension_type == sidpy.DimensionType.SPATIAL:
146
+ image_dims.append(dim)
147
+ return image_dims
148
+
149
+
150
+ def get_extent(dataset):
151
+ """get extent to plot with matplotlib"""
152
+ image_dims = get_image_dims(dataset)
153
+ return dataset.get_extent(image_dims)
@@ -0,0 +1,104 @@
1
+ """ dft simulations tools
2
+
3
+ Part of pyTEMlib
4
+ by Gerd Duscher
5
+ created 10/29/2020
6
+
7
+ Supports the conversion of DFT data to simulated EELS spectra
8
+
9
+ - exciting_get_spectra: importing dielectric function from the exciting program
10
+ - final_state_broadening: apply final state broadening to loss-spectra
11
+ """
12
+
13
+ import numpy as np
14
+ from lxml import etree
15
+
16
+
17
+ def exciting_get_spectra(file):
18
+ """get EELS spectra from exciting calculation"""
19
+
20
+ tags = {'data': {}}
21
+
22
+ tree = etree.ElementTree(file=file)
23
+ root = tree.getroot()
24
+
25
+ data = tags['data']
26
+
27
+ if root.tag in ['loss', 'dielectric']:
28
+ print(' reading ', root.tag, ' function from file ', file)
29
+ # print(root[0].tag, root[0].text)
30
+ map_def = root[0]
31
+ i = 0
32
+ v = {}
33
+ for child_of_root in map_def:
34
+ data[child_of_root.tag] = child_of_root.attrib
35
+ v[child_of_root.tag] = []
36
+ i += 1
37
+
38
+ for elem in tree.iter(tag='map'):
39
+ m_dict = elem.attrib
40
+ for key in m_dict:
41
+ v[key].append(float(m_dict[key]))
42
+
43
+ for key in data:
44
+ data[key]['data'] = np.array(v[key])
45
+ data['type'] = root.tag+' function'
46
+ return tags
47
+
48
+
49
+ def final_state_broadening(x, y, start, instrument):
50
+ """Final state smearing of ELNES edges
51
+
52
+ Parameters
53
+ ----------
54
+ x: numpy array
55
+ x or energy loss axis of density of states
56
+ y: numpy array
57
+ y or intensity axis of density of states
58
+ start: float
59
+ start energy of edge
60
+ instrument: float
61
+ instrument broadening
62
+
63
+ Return
64
+ ------
65
+ out_data: numpy array
66
+ smeared intensity according to final state and instrument broadening
67
+ """
68
+
69
+ # Getting the smearing
70
+ a_i = 107.25*5
71
+ b_i = 0.04688*2.
72
+ x = np.array(x)-start
73
+ zero = int(-x[0]/(x[1]-x[0]))+1
74
+ smear_i = x*0.0
75
+ smear_i[zero:-1] = (a_i/x[zero:-1]**2)+b_i*np.sqrt(x[zero:-1])
76
+ h_bar = 6.58e-16 # h/2pi
77
+ pre = 1.0
78
+ m = 6.58e-31
79
+ smear = x*0.0
80
+ smear[zero:-1] = pre*(h_bar/(smear_i[zero:-1]*0.000000001))*np.sqrt((2*x[zero:-1]*1.6E-19)/m)
81
+
82
+ def lorentzian(xx, pp):
83
+ yy = ((0.5 * pp[1]/3.14)/((xx-pp[0])**2 + ((pp[1]/2)**2)))
84
+ return yy/sum(yy)
85
+
86
+ p = [0, instrument]
87
+ in_data = y.copy()
88
+ out_data = np.array(y)*0.0
89
+ for i in range(zero+5, len(x)):
90
+ p[0] = x[i]
91
+ p[1] = smear[i]/1.0
92
+ lor = lorentzian(x+1e-9, p)
93
+ out_data[i] = sum(in_data*lor)
94
+ if np.isnan(out_data[i]):
95
+ out_data[i] = 0.0
96
+
97
+ p[1] = instrument
98
+ in_data = out_data.copy()
99
+ for i in range(zero-5, len(x)):
100
+ p[0] = x[i]
101
+ lor = lorentzian(x+1e-9, p)
102
+ out_data[i] = sum(in_data*lor)
103
+ # print(out_data[i],in_data[i], lor[i],in_data[i-1], lor[i-1], )
104
+ return out_data
@@ -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()