pyTEMlib 0.2025.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. build/lib/pyTEMlib/__init__.py +36 -0
  2. build/lib/pyTEMlib/animation.py +667 -0
  3. build/lib/pyTEMlib/atom_tools.py +229 -0
  4. build/lib/pyTEMlib/config_dir.py +43 -0
  5. build/lib/pyTEMlib/crystal_tools.py +1219 -0
  6. build/lib/pyTEMlib/diffraction_plot.py +757 -0
  7. build/lib/pyTEMlib/dynamic_scattering.py +298 -0
  8. build/lib/pyTEMlib/eds_tools.py +901 -0
  9. build/lib/pyTEMlib/eds_xsections.py +268 -0
  10. build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
  11. build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  12. build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
  13. build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  14. build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  15. build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  16. build/lib/pyTEMlib/file_reader.py +275 -0
  17. build/lib/pyTEMlib/file_tools.py +816 -0
  18. build/lib/pyTEMlib/get_bote_salvat.py +69 -0
  19. build/lib/pyTEMlib/graph_tools.py +1153 -0
  20. build/lib/pyTEMlib/graph_viz.py +599 -0
  21. build/lib/pyTEMlib/image/__init__.py +37 -0
  22. build/lib/pyTEMlib/image/image_atoms.py +270 -0
  23. build/lib/pyTEMlib/image/image_clean.py +198 -0
  24. build/lib/pyTEMlib/image/image_distortion.py +299 -0
  25. build/lib/pyTEMlib/image/image_fft.py +278 -0
  26. build/lib/pyTEMlib/image/image_graph.py +926 -0
  27. build/lib/pyTEMlib/image/image_registration.py +316 -0
  28. build/lib/pyTEMlib/image/image_utilities.py +308 -0
  29. build/lib/pyTEMlib/image/image_window.py +421 -0
  30. build/lib/pyTEMlib/image_tools.py +701 -0
  31. build/lib/pyTEMlib/interactive_image.py +1 -0
  32. build/lib/pyTEMlib/kinematic_scattering.py +1145 -0
  33. build/lib/pyTEMlib/microscope.py +62 -0
  34. build/lib/pyTEMlib/probe_tools.py +928 -0
  35. build/lib/pyTEMlib/sidpy_tools.py +153 -0
  36. build/lib/pyTEMlib/simulation_tools.py +104 -0
  37. build/lib/pyTEMlib/test.py +437 -0
  38. build/lib/pyTEMlib/utilities.py +431 -0
  39. build/lib/pyTEMlib/version.py +5 -0
  40. build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
  41. pyTEMlib/__init__.py +36 -0
  42. pyTEMlib/animation.py +667 -0
  43. pyTEMlib/atom_tools.py +229 -0
  44. pyTEMlib/config_dir.py +43 -0
  45. pyTEMlib/crystal_tools.py +1219 -0
  46. pyTEMlib/data/k_factors_Bruker_15keV.json +293 -0
  47. pyTEMlib/data/k_factors_Thermo_200keV.json +494 -0
  48. pyTEMlib/data/xrays_X_section_100kV.json +19334 -0
  49. pyTEMlib/data/xrays_X_section_200kV.json +18711 -0
  50. pyTEMlib/data/xrays_X_section_300kV.json +18347 -0
  51. pyTEMlib/data/xrays_X_section_60kV.json +20202 -0
  52. pyTEMlib/diffraction_plot.py +757 -0
  53. pyTEMlib/dynamic_scattering.py +298 -0
  54. pyTEMlib/eds_tools.py +901 -0
  55. pyTEMlib/eds_xsections.py +268 -0
  56. pyTEMlib/eels_tools/__init__.py +44 -0
  57. pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  58. pyTEMlib/eels_tools/eels_database.py +134 -0
  59. pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  60. pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  61. pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  62. pyTEMlib/file_reader.py +275 -0
  63. pyTEMlib/file_tools.py +816 -0
  64. pyTEMlib/get_bote_salvat.py +69 -0
  65. pyTEMlib/graph_tools.py +1153 -0
  66. pyTEMlib/graph_viz.py +599 -0
  67. pyTEMlib/image/__init__.py +37 -0
  68. pyTEMlib/image/image_atoms.py +270 -0
  69. pyTEMlib/image/image_clean.py +198 -0
  70. pyTEMlib/image/image_distortion.py +299 -0
  71. pyTEMlib/image/image_fft.py +278 -0
  72. pyTEMlib/image/image_graph.py +926 -0
  73. pyTEMlib/image/image_registration.py +316 -0
  74. pyTEMlib/image/image_utilities.py +308 -0
  75. pyTEMlib/image/image_window.py +421 -0
  76. pyTEMlib/image_tools.py +701 -0
  77. pyTEMlib/interactive_image.py +1 -0
  78. pyTEMlib/kinematic_scattering.py +1145 -0
  79. pyTEMlib/microscope.py +62 -0
  80. pyTEMlib/probe_tools.py +928 -0
  81. pyTEMlib/sidpy_tools.py +153 -0
  82. pyTEMlib/simulation_tools.py +104 -0
  83. pyTEMlib/test.py +437 -0
  84. pyTEMlib/utilities.py +431 -0
  85. pyTEMlib/version.py +5 -0
  86. pyTEMlib/xrpa_x_sections.py +20976 -0
  87. pytemlib-0.2025.12.0.dist-info/METADATA +100 -0
  88. pytemlib-0.2025.12.0.dist-info/RECORD +92 -0
  89. pytemlib-0.2025.12.0.dist-info/WHEEL +5 -0
  90. pytemlib-0.2025.12.0.dist-info/entry_points.txt +2 -0
  91. pytemlib-0.2025.12.0.dist-info/licenses/LICENSE +21 -0
  92. pytemlib-0.2025.12.0.dist-info/top_level.txt +5 -0
@@ -0,0 +1,268 @@
1
+ """
2
+ Module for calculating electron ionization cross sections for EDS quantification.
3
+ Based on the Bote & Salvat (2008, 2009) cross sections for inner shell ionization
4
+ by electron impact.
5
+ Also includes cascade corrections for Coster-Kronig, Auger, and fluorescence
6
+ emission.
7
+ Values from xraylib package are used for fluorescence yields, radiative rates,
8
+ Coster-Kronig probabilities, and Auger yields/rates.
9
+ References:
10
+ - D. Bote and F. Salvat, "Calculations of inner-shell ionization by electron
11
+ impact with the distorted-wave and plane-wave Born approximations","
12
+ - Bote, David, et al. "Cross sections for ionization of K, L and M shells of
13
+ atoms by impact of electrons and positrons with energies up to 1 GeV:
14
+ Analytical formulas."
15
+ Atomic Data and Nuclear Data Tables 95.6 (2009): 871-909.
16
+ - Browning, N. D., et al. "A model for calculating cross sections for electron
17
+ impact ionization of atoms from threshold to 10 MeV."
18
+ Journal of Applied Physics 83.11 (1998): 5736-5744.
19
+ """
20
+ import os
21
+ import json
22
+ import numpy as np
23
+ import scipy
24
+
25
+ from .config_dir import config_path
26
+ from .utilities import elements as elements_list
27
+ from .utilities import get_z
28
+
29
+
30
+ def get_atomic_number(z):
31
+ """Returns the atomic number independent of input as a string or number"""
32
+ return str(get_z(z))
33
+
34
+
35
+ def casnati_x_section(occupancy, edge_energy, beam_energy):
36
+ """(Casnati's equation) was found to fit cross-section data to
37
+ typically better than +-10% over the range 1<=Uk<=20 and 6<=Z<=79."
38
+ Note: This result is for K shell. L & M are much less wellcharacterized.
39
+ C. Powell indicated in conversation with Richie that he believed that Casnati's
40
+ expression was the best available for L & M also.
41
+ """
42
+ rest_energy = 5.10998918e5 # electron rest energy in eV
43
+ res = 0.0
44
+ ee = edge_energy
45
+ u = beam_energy / ee
46
+ if u > 1.0:
47
+ phi = 10.57 * np.exp((-1.736 / u) + (0.317 / u**2))
48
+ psi = np.power(ee / scipy.constants.R, -0.0318 + (0.3160 / u) + (-0.1135 / u**2))
49
+ i = ee / rest_energy
50
+ t = beam_energy / rest_energy
51
+ f = ((2.0 + i) / (2.0 + t)) * np.square((1.0 + t) / (1.0 + i))
52
+ f *= np.power(((i + t) * (2.0 + t) * np.square(1.0 + i))
53
+ / ((t * (2.0 + t) * np.square(1.0 + i))
54
+ + (i * (2.0 + i))), 1.5)
55
+ res = ((occupancy * np.sqrt((scipy.constants.value('Bohr radius')
56
+ * scipy.constants.Rydberg) / ee)
57
+ * f * psi * phi * np.log(u)) / u)
58
+ return res
59
+
60
+
61
+ def browning_x_section(z, acceleration_energy):
62
+ """
63
+ Browning, N. D., et al. "A model for calculating cross sections for electron
64
+ impact ionization of atoms from threshold to 10 MeV."
65
+ Journal of Applied Physics 83.11 (1998): 5736-5744.
66
+
67
+ Computes the total ionization cross section for energetic electrons.
68
+ Parameters
69
+ ----------
70
+ z : int
71
+ The atomic number z in the range 1:99
72
+ acceleration_energy : float
73
+ The kinetic energy of the incident electron in eV
74
+
75
+ Returns
76
+ -------
77
+ float
78
+ The ionization cross section in square meters
79
+ """
80
+ e = acceleration_energy
81
+ x_section = (3.0e-22 * z**1.7) / (e + (0.005 * z**1.7 * np.sqrt(e))
82
+ + ((0.0007 * z**2) / np.sqrt(e)))
83
+ return x_section
84
+
85
+ def bote_salvat_xsection(acceleration_voltage, ):
86
+ """
87
+ Using Tabulated Values for electrons of:
88
+ - D. Bote and F. Salvat, "Calculations of inner-shell ionization by electron
89
+ impact with the distorted-wave and plane-wave Born approximations",
90
+ Phys. Rev. A77, 042701 (2008).
91
+
92
+ - Bote, David, et al. "Cross sections for ionization of K, L and M shells of
93
+ atoms by impact of electrons and positrons with energies up to 1 GeV:
94
+ Analytical formulas."
95
+ Atomic Data and Nuclear Data Tables 95.6 (2009): 871-909.
96
+
97
+ computed by emtables: https://github.com/adriente/emtables
98
+ with line=True option and stored in a json file in ~/.pyTEMlib directory
99
+ Parameters
100
+ ----------
101
+ z : int
102
+ The atomic number z in the range 1:99
103
+ subshell : str
104
+ The atomic sub-shell being ionized K1, L₁, L₂, ..., M₅
105
+ acceleration_energy : float
106
+ The kinetic energy of the incident electron in eV
107
+
108
+
109
+ Returns
110
+ -------
111
+ dict
112
+ The ionization cross section in barns per line
113
+ """
114
+ filename = os.path.join(config_path, f'xrays_X_section_{int(acceleration_voltage/1000)}kV.json')
115
+
116
+ return json.load(open(filename, 'r', encoding='utf-8'))
117
+
118
+
119
+ def get_bote_salvat_dict(acceleration_voltage, z=0):
120
+ """ Get Bote and Salvat X-ray cross section dictionary."""
121
+ filename = os.path.join(config_path, f'xrays_X_section_{int(acceleration_voltage/1000)}kV.json')
122
+ # print('Loading cross sections from ', filename)
123
+ with open(filename, 'r', encoding='utf-8') as file:
124
+ x_sections = json.load(file)
125
+ if z > 0:
126
+ return x_sections['table'][str(z)]
127
+ return x_sections
128
+
129
+
130
+ def get_families(spectrum):
131
+ """Get the line families for all elements in the spectrum."""
132
+ spectrum.metadata['EDS'].setdefault('GUI', {})
133
+ for key in spectrum.metadata['EDS']:
134
+ if key in ['detector', 'quantification']:
135
+ pass
136
+ elif isinstance(spectrum.metadata['EDS'][key], dict) and key in elements_list:
137
+ family = spectrum.metadata['EDS'][key].get('GUI', {}).get('symmetry', None)
138
+ if family is None:
139
+ if 'K-family' in spectrum.metadata['EDS'][key]:
140
+ family = 'K-family'
141
+ elif 'L-family' in spectrum.metadata['EDS'][key]:
142
+ family = 'L-family'
143
+ elif 'M-family' in spectrum.metadata['EDS'][key]:
144
+ family = 'M-family'
145
+ spectrum.metadata['EDS']['GUI'][key] = {'symmetry': family}
146
+ return spectrum.metadata['EDS']['GUI']
147
+
148
+ def quantify_cross_section(spectrum, mask=None):
149
+ """Calculate quantification for EDS spectrum with cross sections."""
150
+ spectrum.metadata['EDS'].setdefault('GUI', {})
151
+ acceleration_voltage = spectrum.metadata.get('experiment', {}).get('acceleration_voltage',
152
+ 200000)
153
+ families = get_families(spectrum)
154
+
155
+ total = 0
156
+ total_amu = 0
157
+ for key, family in families.items():
158
+ if key in mask:
159
+ continue
160
+ amu = spectrum.metadata['EDS'][key]['atomic_weight']
161
+ intensity = spectrum.metadata['EDS'][key][family['symmetry']].get('areal_density', 0)
162
+ z = get_atomic_number(key)
163
+ x_sect = family_ionization(z, family['symmetry'][0], acceleration_voltage)*1e23
164
+
165
+ spectrum.metadata['EDS']['GUI'][key]['cross_section'] = x_sect
166
+ spectrum.metadata['EDS']['GUI'][key]['composition_counts'] = intensity/x_sect
167
+ total += intensity / x_sect
168
+ total_amu += intensity / x_sect * amu
169
+
170
+ for key, family in families.items():
171
+ intensity = spectrum.metadata['EDS'][key][family['symmetry']].get('areal_density', 0)
172
+ if key in mask:
173
+ spectrum.metadata['EDS']['GUI'][key] = {'atom%': 0,
174
+ 'weight%': 0,
175
+ 'excluded': True,
176
+ 'intensity': intensity,
177
+ 'symmetry': family['symmetry']}
178
+ continue
179
+ amu = spectrum.metadata['EDS'][key]['atomic_weight']
180
+ x_sect = spectrum.metadata['EDS']['GUI'][key]['cross_section']
181
+ spectrum.metadata['EDS']['GUI'][key] = {'atom%': intensity/x_sect/total*100,
182
+ 'weight%': intensity/x_sect*amu/total_amu*100,
183
+ 'excluded': False,
184
+ 'intensity': intensity,
185
+ 'symmetry': family['symmetry']}
186
+ element = spectrum.metadata['EDS']['GUI'][key]
187
+ out_text = f"{key:2}: {element['atom%']:.2f} at% {element['weight%']:.2f} wt%"
188
+ print(out_text)
189
+
190
+
191
+ def quantification_k_factors(spectrum, mask=None):
192
+ """Calculate quantification for EDS spectrum with k-factors."""
193
+ tags = {}
194
+ if not isinstance(mask, list) or mask is None:
195
+ mask = []
196
+ atom_sum = 0.
197
+ weight_sum = 0.
198
+ spectrum.metadata['EDS'].setdefault('GUI', {})
199
+ for key in spectrum.metadata['EDS']:
200
+ intensity = 0.
201
+ k_factor = 0.
202
+ if key in ['detector', 'quantification']:
203
+ pass
204
+ elif isinstance(spectrum.metadata['EDS'][key], dict) and key in elements_list:
205
+ family = spectrum.metadata['EDS'].get('GUI', {}).get(key, {}).get('symmetry', None)
206
+ if family is None:
207
+ if 'K-family' in spectrum.metadata['EDS'][key]:
208
+ family = 'K-family'
209
+ elif 'L-family' in spectrum.metadata['EDS'][key]:
210
+ family = 'L-family'
211
+ elif 'M-family' in spectrum.metadata['EDS'][key]:
212
+ family = 'M-family'
213
+ spectrum.metadata['EDS']['GUI'][key] = {'symmetry': family}
214
+ intensity = spectrum.metadata['EDS'][key][family].get('areal_density', 0)
215
+ k_factor = spectrum.metadata['EDS'][key][family].get('k_factor', 0)
216
+ atomic_weight = spectrum.metadata['EDS'][key]['atomic_weight']
217
+ if key in mask:
218
+ spectrum.metadata['EDS']['GUI'][key] = {'atom%': 0,
219
+ 'weight%': 0,
220
+ 'excluded': True,
221
+ 'symmetry': family,
222
+ 'k_factor': k_factor,
223
+ 'intensity': intensity}
224
+ continue
225
+
226
+ tags[key] = {'atom%': intensity*k_factor/ atomic_weight,
227
+ 'weight%': (intensity*k_factor) ,
228
+ 'k_factor': k_factor,
229
+ 'intensity': intensity,
230
+ 'family': family,
231
+ 'excluded': False}
232
+ atom_sum += intensity*k_factor/ atomic_weight
233
+ weight_sum += intensity*k_factor
234
+ tags['sums'] = {'atom%': atom_sum, 'weight%': weight_sum}
235
+
236
+ spectrum.metadata['EDS']['quantification'] = tags
237
+ eds_dict = spectrum.metadata['EDS']
238
+ for key in eds_dict['quantification']:
239
+ if key != 'sums':
240
+ tags = eds_dict['quantification']
241
+ out_string = f"{key:2}: {tags[key]['atom%']/tags['sums']['atom%']*100:.2f} at%"
242
+ out_string += f" {tags[key]['weight%']/tags['sums']['weight%']*100:.2f} wt%"
243
+ if key in eds_dict['GUI']:
244
+ eds_dict['GUI'][key]['atom%'] = tags[key]['atom%']/tags['sums']['atom%']*100
245
+ eds_dict['GUI'][key]['weight%'] = tags[key]['weight%']/tags['sums']['weight%']*100
246
+ print(out_string)
247
+ eds_dict['GUI'][key]['excluded'] = tags[key]['excluded']
248
+ eds_dict['GUI'][key]['k_factor'] = tags[key]['k_factor']
249
+ eds_dict['GUI'][key]['intensity'] = tags[key]['intensity']
250
+ print('excluded from quantification ', mask)
251
+
252
+ return tags
253
+
254
+ def family_ionization(element_z, family='K', acceleration_voltage=200000):
255
+ """Calculate ionization cross sections for all subshells in a line family."""
256
+ element_z = get_z(element_z)
257
+ x_sections = get_bote_salvat_dict(acceleration_voltage, element_z)
258
+ family_x_sections = {}
259
+ for key, value in x_sections.items():
260
+ if len(key) > 2: # must be a line
261
+ family_x_sections[key[0]+'-family'] = family_x_sections.get(key[0]+'-family', 0)
262
+ family_x_sections[key[0]+'-max'] = family_x_sections.get(key[0]+'-max', 0)
263
+ family_x_sections[key[0]+'-family'] += value['cs']
264
+ family_x_sections[key[0]+'-max'] = max(family_x_sections[key[0]+'-max'], value['cs'])
265
+ x_sections.update(family_x_sections)
266
+ if f"{family}-family" in family_x_sections:
267
+ return family_x_sections[f"{family}-family"]
268
+ return family_x_sections
@@ -0,0 +1,44 @@
1
+ """
2
+ eels_tools - A collection of tools to analyze EELS data
3
+ Model based quantification of electron energy-loss data
4
+ part of pyTEMlib
5
+ Copyright by Gerd Duscher
6
+ seperated into different files 09/2025
7
+ """
8
+ from ..utilities import major_edges, all_edges, first_close_edges, elements
9
+ from ..utilities import get_wavelength, effective_collection_angle, set_default_metadata
10
+ from ..utilities import lorentz, gauss, get_x_sections, get_z, get_spectrum
11
+
12
+ from .zero_loss_tools import zero_loss_function, get_resolution_functions
13
+ from .zero_loss_tools import get_zero_loss_energy, shift_energy, align_zero_loss
14
+
15
+ from .low_loss_tools import drude_simulation, kroeger_core
16
+ from .low_loss_tools import get_plasmon_losses, drude, drude_lorentz
17
+ from .low_loss_tools import energy_loss_function, angle_correction, fit_plasmon
18
+ from .low_loss_tools import fit_multiple_scattering, multiple_scattering
19
+ from .low_loss_tools import inelastic_mean_free_path, model3, add_peaks
20
+
21
+ from .peak_fit_tools import model_smooth, gaussian_mixture_model, find_peaks, find_maxima
22
+ from .peak_fit_tools import sort_peaks
23
+
24
+ from .core_loss_tools import make_cross_sections, fit_edges2, power_law_background
25
+ from .core_loss_tools import list_all_edges, find_all_edges, find_associated_edges
26
+ from .core_loss_tools import find_white_lines, find_edges, assign_likely_edges
27
+ from .core_loss_tools import auto_id_edges, identify_edges, add_element_to_dataset
28
+ from .core_loss_tools import fit_dataset, auto_chemical_composition, make_edges
29
+ from .core_loss_tools import cl_model, core_loss_model, fit_edges, xsec_xrpa
30
+
31
+
32
+ __all__ = ['major_edges', 'all_edges', 'first_close_edges', 'elements', 'get_wavelength',
33
+ 'effective_collection_angle', 'set_default_metadata', 'lorentz', 'gauss',
34
+ 'get_x_sections', 'get_z', 'get_spectrum', 'zero_loss_function',
35
+ 'get_resolution_functions', 'get_zero_loss_energy', 'shift_energy', 'align_zero_loss',
36
+ 'drude_simulation', 'kroeger_core','get_plasmon_losses', 'drude', 'drude_lorentz',
37
+ 'energy_loss_function', 'angle_correction', 'fit_plasmon', 'fit_multiple_scattering',
38
+ 'multiple_scattering', 'inelastic_mean_free_path', 'model3', 'add_peaks',
39
+ 'model_smooth', 'gaussian_mixture_model', 'find_peaks', 'find_maxima', 'sort_peaks',
40
+ 'make_cross_sections', 'fit_edges2', 'power_law_background', 'list_all_edges',
41
+ 'find_all_edges', 'find_associated_edges', 'find_white_lines', 'find_edges',
42
+ 'assign_likely_edges', 'auto_id_edges', 'identify_edges', 'add_element_to_dataset',
43
+ 'fit_dataset', 'auto_chemical_composition', 'make_edges', 'cl_model', 'core_loss_model',
44
+ 'fit_edges', 'xsec_xrpa']