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
@@ -0,0 +1,432 @@
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
+
21
+ import os
22
+
23
+ import json
24
+ import numpy as np
25
+ import scipy
26
+
27
+ import xraylib as xr
28
+
29
+ from .config_dir import config_path
30
+ from .utilities import elements as elements_list
31
+ from .utilities import get_z
32
+
33
+
34
+ def get_atomic_number(z):
35
+ """Returns the atomic number independent of input as a string or number"""
36
+ return get_z(z)
37
+
38
+ line_list = []
39
+ for _key in xr.__dict__:
40
+ if '_LINE' in _key:
41
+ line_list.append(_key.split('_')[0])
42
+
43
+ shell_list = []
44
+ for _key in xr.__dict__:
45
+ if '_SHELL' in _key:
46
+ shell_list.append(_key.split('_')[0])
47
+
48
+ auger_list = []
49
+ for _key in xr.__dict__:
50
+ if '_AUGER' in _key:
51
+ auger_list.append(_key.split('_')[:-1])
52
+
53
+ def get_bote_salvat_dict(z=0):
54
+ """Load the Bote & Salvat cross section data from JSON file."""
55
+ filename = os.path.join(config_path, 'Bote_Salvat.json')
56
+ x_sections = json.load(open(filename, 'r', encoding='utf-8'))
57
+ if z > 0:
58
+ return x_sections[str(z)]
59
+ return x_sections
60
+
61
+ def bote_salvat_xsection(x_section, subshell, acceleration_energy):
62
+ """
63
+ Using Tabulated Values for electrons of:
64
+ - D. Bote and F. Salvat, "Calculations of inner-shell ionization by electron
65
+ impact with the distorted-wave and plane-wave Born approximations",
66
+ Phys. rest_energy. A77, 042701 (2008).
67
+
68
+ - Bote, David, et al. "Cross sections for ionization of K, L and M shells of
69
+ atoms by impact of electrons and positrons with energies up to 1 GeV:
70
+ Analytical formulas."
71
+ Atomic Data and Nuclear Data Tables 95.6 (2009): 871-909.
72
+
73
+ Computes the inner sub-shell ionization cross section for energetic electrons.
74
+ `subshell` is 1->K, 2->L₁, 3->L₂, ..., 9->M₅
75
+ Parameters
76
+ ----------
77
+ z : int
78
+ The atomic number z in the range 1:99
79
+ subshell : int
80
+ The atomic sub-shell being ionized 1->K, 2->L₁, 3->L₂, ..., 9->M₅
81
+ acceleration_energy : float
82
+ The kinetic energy of the incident electron in eV
83
+ edge_energy : float
84
+ The edge energy of the sub-shell in eV
85
+
86
+ Returns
87
+ -------
88
+ float
89
+ The ionization cross section in barns
90
+ """
91
+ edge_energy = x_section['edge'][subshell-1]
92
+ over_voltage = acceleration_energy / edge_energy
93
+ if over_voltage < 1.0:
94
+ return
95
+ if over_voltage <= 16:
96
+ a = np.array(x_section['A'])[subshell, :]
97
+ opu = 1.0 / (1.0 + over_voltage)
98
+ ffitlo = a[0] + a[1] * over_voltage + opu*(a[2] + opu**2*(a[3] + opu**2*a[4]))
99
+ x_ion_e = (over_voltage - 1.0) * (ffitlo / over_voltage)**2
100
+ else:
101
+ rest_ene = 5.10998918e5 # electron rest energy in eV
102
+ e_0 = acceleration_energy
103
+ beta2 = (e_0 * (e_0 + 2.0 * rest_ene)) / ((e_0 + rest_ene)**2)
104
+ x = np.sqrt(e_0 * (e_0 + 2.0 * rest_ene)) / rest_ene
105
+ g = x_section['G'][subshell-1]
106
+ ffitup = ((((2.0 * np.log(x)) - beta2) * (1.0 + g[0] / x)) + g[1] + g[2]
107
+ * np.sqrt(rest_ene / (e_0 + rest_ene)) + g[3] / x)
108
+ factr = x_section['Anlj'][subshell-1] / beta2
109
+ x_ion_e = ((factr * over_voltage) / (over_voltage + x_section['Be'][subshell-1])) * ffitup
110
+ return 4.0 * np.pi * 5.291772108e-9**2 * x_ion_e # in barns
111
+
112
+
113
+ def casnati_x_section(occupancy, edge_energy, beam_energy):
114
+ """(Casnati's equation) was found to fit cross-section data to
115
+ typically better than +-10% over the range 1<=Uk<=20 and 6<=Z<=79."
116
+ Note: This result is for K shell. L & M are much less wellcharacterized.
117
+ C. Powell indicated in conversation with Richie that he believed that Casnati's
118
+ expression was the best available for L & M also.
119
+ """
120
+ rest_energy = 5.10998918e5 # electron rest energy in eV
121
+ res = 0.0
122
+ ee = edge_energy
123
+ u = beam_energy / ee
124
+ if u > 1.0:
125
+ phi = 10.57 * np.exp((-1.736 / u) + (0.317 / u**2))
126
+ psi = np.power(ee / scipy.constants.R, -0.0318 + (0.3160 / u) + (-0.1135 / u**2))
127
+ i = ee / rest_energy
128
+ t = beam_energy / rest_energy
129
+ f = ((2.0 + i) / (2.0 + t)) * np.square((1.0 + t) / (1.0 + i))
130
+ f *= np.power(((i + t) * (2.0 + t) * np.square(1.0 + i))
131
+ / ((t * (2.0 + t) * np.square(1.0 + i))
132
+ + (i * (2.0 + i))), 1.5)
133
+ res = ((occupancy * np.sqrt((scipy.constants.value('Bohr radius')
134
+ * scipy.constants.Rydberg) / ee)
135
+ * f * psi * phi * np.log(u)) / u)
136
+ return res
137
+
138
+
139
+ def browning_x_section(z, acceleration_energy):
140
+ """
141
+ Browning, N. D., et al. "A model for calculating cross sections for electron
142
+ impact ionization of atoms from threshold to 10 MeV."
143
+ Journal of Applied Physics 83.11 (1998): 5736-5744.
144
+
145
+ Computes the total ionization cross section for energetic electrons.
146
+ Parameters
147
+ ----------
148
+ z : int
149
+ The atomic number z in the range 1:99
150
+ acceleration_energy : float
151
+ The kinetic energy of the incident electron in eV
152
+
153
+ Returns
154
+ -------
155
+ float
156
+ The ionization cross section in square meters
157
+ """
158
+ e = acceleration_energy
159
+ x_section = (3.0e-22 * z**1.7) / (e + (0.005 * z**1.7 * np.sqrt(e))
160
+ + ((0.0007 * z**2) / np.sqrt(e)))
161
+ return x_section
162
+
163
+
164
+ def get_transition_dict(shell):
165
+ """Get the transition dictionary for a given shell."""
166
+ transition_dict = {}
167
+ for index, line in enumerate(line_list):
168
+ if line[:-2] == shell:
169
+ # lines have negative !!!! indices in xraylib
170
+ transition_dict[line] = -index - 1
171
+ return transition_dict
172
+
173
+
174
+ def radiation_ionization(element, shell):
175
+ """Radiative transitions from higher shells to the given shell"""
176
+ end_family = shell[0]
177
+ ionization = 0.0
178
+ for index, line in enumerate(shell_list):
179
+ start_family = line[0]
180
+ if end_family > start_family:
181
+ tr_dict = get_transition_dict(line)
182
+ try:
183
+ fy = xr.FluorYield(element, index)
184
+ rr = xr.RadRate(element, tr_dict[line + shell])
185
+ ionization += fy * rr
186
+ except ValueError:
187
+ pass
188
+ return ionization
189
+
190
+
191
+ def get_all_subshells(line_family):
192
+ """Get all subshells for a given line family."""
193
+ subshells = []
194
+ for subshell in shell_list:
195
+ if line_family[0] == subshell[0]:
196
+ subshells.append(subshell)
197
+ return subshells
198
+
199
+
200
+ def full_auger_ionization (element, shell) :
201
+ """ Auger transitions from lower shells to the given shell"""
202
+ ionization = 0
203
+ for i in range(shell_list.index(shell)) :
204
+ ionization += auger_ionization(element, shell, shell_list[i])
205
+ return ionization
206
+
207
+
208
+ def get_auger_dict(shell, lower_shell) :
209
+ """Get the Auger transition dictionary for a given shell and lower shell."""
210
+ auger_dict = {}
211
+ for key, item in xr.__dict__.items():
212
+ if '_AUGER' in key:
213
+ if key[:2] == lower_shell and key[3:5] == shell:
214
+ auger_dict[key[:7]] = item
215
+ return auger_dict
216
+
217
+
218
+ def get_trans_dictionary(shell):
219
+ """Get the transition dictionary for a given shell."""
220
+ trans = {}
221
+ shell_group = shell[0]
222
+ if shell[0] == 'K':
223
+ return trans
224
+ for i in range(1, int(shell[1])):
225
+ trans[f"{shell_group}{i}{shell[1]}"] = xr.__dict__[f"F{shell_group}{i}{shell[1]}_TRANS"]
226
+ return trans
227
+
228
+
229
+ def coster_kronic_ionization (tags, shell) :
230
+ """ Coster Kronig transitions
231
+ For the L1, there is not coster kronig
232
+ transitions from higher to lower shells are added
233
+ """
234
+ element = int(tags['atomic_number'])
235
+
236
+ ionization = 0
237
+ if shell[0] == 'K':
238
+ return ionization
239
+ for trans, item in get_trans_dictionary(shell).items():
240
+ try:
241
+ ionization += tags[trans[:2]] * xr.CosKronTransProb(element, item)
242
+ except ValueError:
243
+ pass
244
+ return ionization
245
+
246
+
247
+ def auger_ionization (element, shell, lower_shell):
248
+ """ Auger transitions from lower shells to the given shell"""
249
+ if shell == "K":
250
+ return 0
251
+ else:
252
+ auger_dict = get_auger_dict(shell, lower_shell)
253
+ try:
254
+ auger_yield = xr.AugerYield(element, shell_list.index(lower_shell))
255
+ auger_rate = 0
256
+ for trans in auger_dict.values():
257
+ try:
258
+ auger_rate += xr.AugerRate(element, trans)
259
+ except ValueError:
260
+ pass
261
+ return auger_rate * auger_yield
262
+ except ValueError:
263
+ return 0
264
+
265
+
266
+ def radiation_emission(element, shell):
267
+ """Radiative transitions from the given shell to higher shells
268
+ Does not work"""
269
+
270
+ start_family = shell[0]
271
+ if start_family > 'M':
272
+ return 0.0
273
+ tr_dict = get_transition_dict(shell)
274
+ radiation_propability = 0.0
275
+ for index, line in enumerate(shell_list):
276
+ end_family = line[0]
277
+ if end_family > start_family and end_family < 'N':
278
+ try:
279
+ fluorescent_yield = xr.FluorYield(element, index)
280
+ rate = xr.RadRate(element, tr_dict[shell + line])
281
+ radiation_propability += fluorescent_yield * rate
282
+ except ValueError:
283
+ pass
284
+ return radiation_propability
285
+
286
+
287
+ def family_ionization(tags, line_family, acceleration_energy=200000):
288
+ """Calculate ionization cross sections for all subshells in a line family."""
289
+ element_z = tags['atomic_number']
290
+ x_sections = get_bote_salvat_dict(z=element_z)
291
+ # direct
292
+ for subshell in get_all_subshells(line_family):
293
+ index = shell_list.index(subshell)
294
+ tags[subshell] = bote_salvat_xsection(x_sections, index, acceleration_energy)
295
+ # cascade corrections
296
+ for subshell in get_all_subshells(line_family):
297
+ index = shell_list.index(subshell)
298
+ coster_kronig = coster_kronic_ionization (tags, subshell)
299
+ cascade_ionization = 1 + radiation_ionization(element_z, subshell)
300
+ cascade_ionization += full_auger_ionization(element_z, subshell)
301
+ tags[subshell] *= cascade_ionization
302
+ tags[subshell] += coster_kronig
303
+ return tags
304
+
305
+
306
+ def get_families(spectrum):
307
+ """Get the line families for all elements in the spectrum."""
308
+ spectrum.metadata['EDS'].setdefault('GUI', {})
309
+ for key in spectrum.metadata['EDS']:
310
+ if key in ['detector', 'quantification']:
311
+ pass
312
+ elif isinstance(spectrum.metadata['EDS'][key], dict) and key in elements_list:
313
+ family = spectrum.metadata['EDS'][key].get('GUI', {}).get('symmetry', None)
314
+ if family is None:
315
+ if 'K-family' in spectrum.metadata['EDS'][key]:
316
+ family = 'K-family'
317
+ elif 'L-family' in spectrum.metadata['EDS'][key]:
318
+ family = 'L-family'
319
+ elif 'M-family' in spectrum.metadata['EDS'][key]:
320
+ family = 'M-family'
321
+ spectrum.metadata['EDS']['GUI'][key] = {'symmetry': family}
322
+ return spectrum.metadata['EDS']['GUI']
323
+
324
+ def quantify_cross_section(spectrum, x_section=None, mask=None):
325
+ """Calculate quantification for EDS spectrum with cross sections."""
326
+ spectrum.metadata['EDS'].setdefault('GUI', {})
327
+ acceleration_voltage = spectrum.metadata.get('experiment', {}).get('acceleration_voltage',
328
+ 200000)
329
+ families = get_families(spectrum)
330
+ total = 0
331
+ total_amu = 0
332
+ for key, family in families.items():
333
+ if key in mask:
334
+ continue
335
+ amu = spectrum.metadata['EDS'][key]['atomic_weight']
336
+ intensity = spectrum.metadata['EDS'][key][family['symmetry']].get('areal_density', 0)
337
+ z = get_atomic_number(key)
338
+ peaks = spectrum.metadata['EDS'][key][family['symmetry']].get('peaks', np.array([1]))
339
+ f_yield = spectrum.metadata['EDS'][key][family['symmetry']].get('fluorescent_yield',0)
340
+ tags = {'atomic_number':z}
341
+ tags = family_ionization(tags, family['symmetry'], acceleration_voltage)
342
+ x_sect = tags['K']*1e14 / f_yield / peaks.max()
343
+ # print(x_sect)
344
+ # x_sect = get_cross_section(x_section[str(z)], family['symmetry'],
345
+ # acceleration_voltage)*1e14/ f_yield/peaks.max()
346
+ spectrum.metadata['EDS']['GUI'][key]['cross_section'] = x_sect
347
+ spectrum.metadata['EDS']['GUI'][key]['composition_counts'] = intensity*x_sect
348
+ # print(key, ' - ', family['symmetry'], intensity, x_sect, f_yield, peaks.max())
349
+
350
+ total += intensity * x_sect
351
+ total_amu += intensity * x_sect * amu
352
+
353
+ for key, family in families.items():
354
+ if key in mask:
355
+ continue
356
+ amu = spectrum.metadata['EDS'][key]['atomic_weight']
357
+ intensity = spectrum.metadata['EDS'][key][family['symmetry']].get('areal_density', 0)
358
+ x_sect = spectrum.metadata['EDS']['GUI'][key]['cross_section']
359
+ spectrum.metadata['EDS']['GUI'][key] = {'atom%': intensity*x_sect/total,
360
+ 'weight%': intensity*x_sect*amu/total}
361
+ out_text = f"{key:2}: {intensity*x_sect/total*100:.2f} at% "
362
+ out_text += f"{intensity*x_sect*amu/total_amu*100:.2f} wt%"
363
+ print(out_text)
364
+
365
+
366
+ def quantification_k_factors(spectrum, mask=None):
367
+ """Calculate quantification for EDS spectrum with k-factors."""
368
+ tags = {}
369
+ if not isinstance(mask, list) or mask is None:
370
+ mask = []
371
+ atom_sum = 0.
372
+ weight_sum = 0.
373
+ spectrum.metadata['EDS'].setdefault('GUI', {})
374
+ for key in spectrum.metadata['EDS']:
375
+ intensity = 0.
376
+ k_factor = 0.
377
+ if key in mask + ['detector', 'quantification']:
378
+ pass
379
+ elif isinstance(spectrum.metadata['EDS'][key], dict) and key in elements_list:
380
+ family = spectrum.metadata['EDS'][key].get('GUI', {}).get('symmetry', None)
381
+ if family is None:
382
+ if 'K-family' in spectrum.metadata['EDS'][key]:
383
+ family = 'K-family'
384
+ elif 'L-family' in spectrum.metadata['EDS'][key]:
385
+ family = 'L-family'
386
+ elif 'M-family' in spectrum.metadata['EDS'][key]:
387
+ family = 'M-family'
388
+ spectrum.metadata['EDS']['GUI'][key] = {'symmetry': family}
389
+
390
+ intensity = spectrum.metadata['EDS'][key][family]['areal_density'] # /peaks_max
391
+ k_factor = spectrum.metadata['EDS'][key][family]['k_factor']
392
+ atomic_weight = spectrum.metadata['EDS'][key]['atomic_weight']
393
+ tags[key] = {'atom%': intensity*k_factor/ atomic_weight,
394
+ 'weight%': (intensity*k_factor) ,
395
+ 'k_factor': k_factor,
396
+ 'intensity': intensity}
397
+ atom_sum += intensity*k_factor/ atomic_weight
398
+ weight_sum += intensity*k_factor
399
+ tags['sums'] = {'atom%': atom_sum, 'weight%': weight_sum}
400
+
401
+ spectrum.metadata['EDS']['quantification'] = tags
402
+ eds_dict = spectrum.metadata['EDS']
403
+ for key in eds_dict['quantification']:
404
+ if key != 'sums':
405
+ tags = eds_dict['quantification']
406
+ out_string = f"{key:2}: {tags[key]['atom%']/tags['sums']['atom%']*100:.2f} at%"
407
+ out_string += f" {tags[key]['weight%']/tags['sums']['weight%']*100:.2f} wt%"
408
+ if key in eds_dict['GUI']:
409
+ eds_dict['GUI'][key]['atom%'] = tags[key]['atom%']/tags['sums']['atom%']*100
410
+ eds_dict['GUI'][key]['weight%'] = tags[key]['weight%']/tags['sums']['weight%']*100
411
+ print(out_string)
412
+ print('excluded from quantification ', mask)
413
+
414
+ return tags
415
+
416
+
417
+ def get_emission (element, line_family, accelerating_energy=200000):
418
+ """Calculate the total emission for a given element and line family."""
419
+ element_z = get_z(element)
420
+ tags = {element:{'atomic_number': element_z}}
421
+ family_ionization(tags[element], line_family, accelerating_energy)
422
+
423
+ tags[element][f'{line_family}_emission'] = 0.
424
+ for line in shell_list:
425
+ if line_family in line:
426
+ print(line, radiation_emission(element_z, line))
427
+ print('emission', tags[element][line] * radiation_emission(element_z, line))
428
+ tags[element][f'{line_family}_emission'] += (tags[element][line]
429
+ * radiation_emission(element_z, line))
430
+ # tags[element][f'{line_family}_emission']
431
+
432
+ return tags
@@ -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_wave_length, 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_wave_length',
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']