pyTEMlib 0.2020.11.1__py3-none-any.whl → 0.2024.8.4__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 (60) hide show
  1. pyTEMlib/__init__.py +11 -11
  2. pyTEMlib/animation.py +631 -0
  3. pyTEMlib/atom_tools.py +240 -245
  4. pyTEMlib/config_dir.py +57 -33
  5. pyTEMlib/core_loss_widget.py +658 -0
  6. pyTEMlib/crystal_tools.py +1255 -0
  7. pyTEMlib/diffraction_plot.py +756 -0
  8. pyTEMlib/dynamic_scattering.py +293 -0
  9. pyTEMlib/eds_tools.py +609 -0
  10. pyTEMlib/eels_dialog.py +749 -491
  11. pyTEMlib/{interactive_eels.py → eels_dialog_utilities.py} +1199 -1177
  12. pyTEMlib/eels_tools.py +2031 -1698
  13. pyTEMlib/file_tools.py +1276 -560
  14. pyTEMlib/file_tools_qt.py +193 -0
  15. pyTEMlib/graph_tools.py +1166 -450
  16. pyTEMlib/graph_viz.py +449 -0
  17. pyTEMlib/image_dialog.py +158 -0
  18. pyTEMlib/image_dlg.py +146 -232
  19. pyTEMlib/image_tools.py +1399 -1028
  20. pyTEMlib/info_widget.py +933 -0
  21. pyTEMlib/interactive_image.py +1 -226
  22. pyTEMlib/kinematic_scattering.py +1196 -0
  23. pyTEMlib/low_loss_widget.py +176 -0
  24. pyTEMlib/microscope.py +61 -81
  25. pyTEMlib/peak_dialog.py +1047 -410
  26. pyTEMlib/peak_dlg.py +286 -242
  27. pyTEMlib/probe_tools.py +653 -207
  28. pyTEMlib/sidpy_tools.py +153 -136
  29. pyTEMlib/simulation_tools.py +104 -87
  30. pyTEMlib/version.py +6 -3
  31. pyTEMlib/xrpa_x_sections.py +20972 -0
  32. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.8.4.dist-info}/LICENSE +21 -21
  33. pyTEMlib-0.2024.8.4.dist-info/METADATA +93 -0
  34. pyTEMlib-0.2024.8.4.dist-info/RECORD +37 -0
  35. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.8.4.dist-info}/WHEEL +6 -5
  36. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.8.4.dist-info}/entry_points.txt +0 -1
  37. pyTEMlib/KinsCat.py +0 -2758
  38. pyTEMlib/__version__.py +0 -2
  39. pyTEMlib/data/TEMlibrc +0 -68
  40. pyTEMlib/data/edges_db.csv +0 -189
  41. pyTEMlib/data/edges_db.pkl +0 -0
  42. pyTEMlib/data/fparam.txt +0 -103
  43. pyTEMlib/data/microscopes.csv +0 -7
  44. pyTEMlib/data/microscopes.xml +0 -167
  45. pyTEMlib/data/path.txt +0 -1
  46. pyTEMlib/defaults_parser.py +0 -90
  47. pyTEMlib/dm3_reader.py +0 -613
  48. pyTEMlib/edges_db.py +0 -76
  49. pyTEMlib/eels_dlg.py +0 -224
  50. pyTEMlib/hdf_utils.py +0 -483
  51. pyTEMlib/image_tools1.py +0 -2194
  52. pyTEMlib/info_dialog.py +0 -237
  53. pyTEMlib/info_dlg.py +0 -202
  54. pyTEMlib/nion_reader.py +0 -297
  55. pyTEMlib/nsi_reader.py +0 -170
  56. pyTEMlib/structure_tools.py +0 -316
  57. pyTEMlib/test.py +0 -2072
  58. pyTEMlib-0.2020.11.1.dist-info/METADATA +0 -20
  59. pyTEMlib-0.2020.11.1.dist-info/RECORD +0 -45
  60. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.8.4.dist-info}/top_level.txt +0 -0
pyTEMlib/eds_tools.py ADDED
@@ -0,0 +1,609 @@
1
+ """
2
+ eds_tools
3
+ Model based quantification of energy-dispersive X-ray spectroscopy data
4
+ Copyright by Gerd Duscher
5
+
6
+ The University of Tennessee, Knoxville
7
+ Department of Materials Science & Engineering
8
+
9
+ Sources:
10
+
11
+ Units:
12
+ everything is in SI units, except length is given in nm and angles in mrad.
13
+
14
+ Usage:
15
+ See the notebooks for examples of these routines
16
+
17
+ All the input and output is done through a dictionary which is to be found in the meta_data
18
+ attribute of the sidpy.Dataset
19
+ """
20
+ import numpy as np
21
+
22
+ import scipy
23
+ from scipy.interpolate import interp1d, splrep # splev, splint
24
+ from scipy import interpolate
25
+ from scipy.signal import peak_prominences
26
+ from scipy.ndimage import gaussian_filter
27
+ from sklearn.mixture import GaussianMixture
28
+ from sklearn.cluster import KMeans
29
+ import scipy.constants as const
30
+
31
+ from scipy import constants
32
+ import matplotlib.pyplot as plt
33
+ # import matplotlib.patches as patches
34
+
35
+ # from matplotlib.widgets import SpanSelector
36
+ # import ipywidgets as widgets
37
+ # from IPython.display import display
38
+
39
+ import requests
40
+
41
+ from scipy.optimize import leastsq # least square fitting routine fo scipy
42
+
43
+ import sidpy
44
+
45
+ import pickle # pkg_resources
46
+ import pyTEMlib.eels_tools
47
+ from pyTEMlib.xrpa_x_sections import x_sections
48
+
49
+ elements_list = pyTEMlib.eels_tools.elements
50
+
51
+ shell_occupancy = {'K1': 2, 'L1': 2, 'L2': 2, 'L3': 4, 'M1': 2, 'M2': 2, 'M3': 4, 'M4': 4, 'M5': 6,
52
+ 'N1': 2, 'N2': 2, 'N3': 4, 'N4': 4, 'N5': 6, 'N6': 6, 'N7': 8,
53
+ 'O1': 2, 'O2': 2, 'O3': 4, 'O4': 4, 'O5': 6, 'O6': 6, 'O7': 8, 'O8': 8, 'O9': 10}
54
+
55
+
56
+ def detector_response(dataset):
57
+ tags = dataset.metadata['experiment']
58
+
59
+ energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
60
+ if 'start_channel' not in tags['detector']:
61
+ tags['detector']['start_channel'] = np.searchsorted(energy_scale, 100)
62
+
63
+ start = tags['detector']['start_channel']
64
+ detector_efficiency = np.zeros(len(dataset))
65
+ detector_efficiency[start:] += get_detector_response(tags, energy_scale[start:])
66
+ tags['detector']['detector_efficiency'] = detector_efficiency
67
+ return detector_efficiency
68
+
69
+
70
+ def get_detector_response(detector_definition, energy_scale):
71
+ """
72
+ Calculates response of Si drift detector for EDS spectrum background based on detector parameters
73
+
74
+ Parameters:
75
+ ----------
76
+ detector_definition: dictionary
77
+ definition of detector
78
+ energy_scale: numpy array (1 dim)
79
+ energy scale of spectrum should start at about 100eV!!
80
+
81
+ Return:
82
+ -------
83
+ response: numpy array with length(energy_scale)
84
+ detector response
85
+
86
+ Example
87
+ -------
88
+
89
+ tags ={}
90
+ tags['acceleration_voltage'] = 200000
91
+
92
+ tags['detector'] ={}
93
+
94
+ ## layer thicknesses of commen materials in EDS detectors in m
95
+ tags['detector']['Al_thickness'] = 0.03 * 1e-6 # in m
96
+ tags['detector']['Be_thickness'] = 0. # in m
97
+ tags['detector']['Au_thickness'] = 0.0 * 1e-6 # in m
98
+ tags['detector']['Par_thickness'] = 0 *1e-6 # in m # Window
99
+
100
+ tags['detector']['SiDeadThickness'] = .03 *1e-6 # in m
101
+
102
+ tags['detector']['SiLiveThickness'] = 0.05 # in m
103
+ tags['detector']['detector_area'] = 30 * 1e-6 #in m2
104
+ tags['detector']['resolution'] = 125 # in eV
105
+
106
+ energy_scale = np.linspace(.01,20,1199)*1000 # i eV
107
+ start = np.searchsorted(spectrum.energy, 100)
108
+ energy_scale = spectrum.energy[start:]
109
+ detector_Efficiency= pyTEMlib.eds_tools.detector_response(tags, spectrum.energy[start:])
110
+
111
+ p = np.array([1, 37, .3])/10000*3
112
+ E_0= 200000
113
+ background = np.zeros(len(spectrum))
114
+ background[start:] = detector_Efficiency * (p[0] + p[1]*(E_0-energy_scale)/energy_scale + p[2]*(E_0-energy_scale)**2/energy_scale)
115
+
116
+
117
+ plt.figure()
118
+ plt.plot(spectrum.energy, spectrum, label = 'spec')
119
+ plt.plot(spectrum.energy, background, label = 'background')
120
+ plt.show()
121
+
122
+ """
123
+ response = np.ones(len(energy_scale))
124
+ x_sections = pyTEMlib.eels_tools.get_x_sections()
125
+
126
+ def get_absorption(Z, t):
127
+ photoabsorption = x_sections[str(Z)]['dat']/1e10/x_sections[str(Z)]['photoabs_to_sigma']
128
+ lin = interp1d(x_sections[str(Z)]['ene'], photoabsorption, kind='linear')
129
+ mu = lin(energy_scale) * x_sections[str(Z)]['nominal_density']*100. #1/cm -> 1/m
130
+ return np.exp(-mu * t)
131
+
132
+ if 'Al_thickness' in detector_definition['detector']:
133
+ response *= get_absorption(13, detector_definition['detector']['Al_thickness'])
134
+ if 'Be_thickness' in detector_definition['detector']:
135
+ response *= get_absorption(5, detector_definition['detector']['Be_thickness'])
136
+ if 'Au_thickness' in detector_definition['detector']:
137
+ response *= get_absorption(79, detector_definition['detector']['Au_thickness'])
138
+ if 'Par_thickness' in detector_definition['detector']:
139
+ response *= get_absorption(6, detector_definition['detector']['Par_thickness'])
140
+ if 'SiDeadThickness' in detector_definition['detector']:
141
+ response *= get_absorption(14, detector_definition['detector']['SiDeadThickness'])
142
+
143
+ if 'SiLiveThickness' in detector_definition['detector']:
144
+ response *= 1-get_absorption(14, detector_definition['detector']['SiLiveThickness'])
145
+ return response
146
+
147
+
148
+ def detect_peaks(dataset, minimum_number_of_peaks=30):
149
+ if not isinstance(dataset, sidpy.Dataset):
150
+ raise TypeError('Needs an sidpy dataset')
151
+ if not dataset.data_type.name == 'SPECTRUM':
152
+ raise TypeError('Need a spectrum')
153
+
154
+ energy_scale = dataset.get_spectral_dims(return_axis=True)[0]
155
+ if 'detector' not in dataset.metadata:
156
+ if 'energy_resolution' not in dataset.metadata['detector']:
157
+ dataset.metadata['detector']['energy_resolution'] = 138
158
+ print('Using energy resolution of 138 eV')
159
+ if 'start_channel' not in dataset.metadata['detector']:
160
+ dataset.metadata['detector']['start_channel'] = start = np.searchsorted(energy_scale, 100)
161
+ resolution = dataset.metadata['detector']['energy_resolution']
162
+
163
+ start = dataset.metadata['detector']['start_channel']
164
+ ## we use half the width of the resolution for smearing
165
+ width = int(np.ceil(resolution/(energy_scale[1]-energy_scale[0])/2)+1)
166
+ new_spectrum = scipy.signal.savgol_filter(np.array(dataset)[start:], width, 2) ## we use half the width of the resolution for smearing
167
+ prominence = 10
168
+ minor_peaks, _ = scipy.signal.find_peaks(new_spectrum, prominence=prominence)
169
+
170
+ while len(minor_peaks) > minimum_number_of_peaks:
171
+ prominence+=10
172
+ minor_peaks, _ = scipy.signal.find_peaks(new_spectrum, prominence=prominence)
173
+ return np.array(minor_peaks)+start
174
+
175
+ def find_elements(spectrum, minor_peaks):
176
+ if not isinstance(spectrum, sidpy.Dataset):
177
+ raise TypeError(' Need a sidpy dataset')
178
+ energy_scale = spectrum.get_spectral_dims(return_axis=True)[0]
179
+ elements = []
180
+ for peak in minor_peaks:
181
+ found = False
182
+ for element in range(3,82):
183
+ if 'lines' in x_sections[str(element)]:
184
+ if 'K-L2' in x_sections[str(element)]['lines']:
185
+ if abs(x_sections[str(element)]['lines']['K-L2']['position']- energy_scale[peak]) <10:
186
+ found = True
187
+ if x_sections[str(element)]['name'] not in elements:
188
+ elements.append( x_sections[str(element)]['name'])
189
+ if not found:
190
+ if 'L3-M3' in x_sections[str(element)]['lines']:
191
+ if abs(x_sections[str(element)]['lines']['L3-M5']['position']- energy_scale[peak]) <30:
192
+ if x_sections[str(element)]['name'] not in elements:
193
+ elements.append( x_sections[str(element)]['name'])
194
+ return elements
195
+
196
+ def get_x_ray_lines(spectrum, elements):
197
+ out_tags = {}
198
+ alpha_K = 1e6
199
+ alpha_L = 6.5e7
200
+ alpha_M = 8*1e8 # 2.2e10
201
+ # My Fit
202
+ alpha_K = .9e6
203
+ alpha_L = 6.e7
204
+ alpha_M = 6*1e8 # 2.2e10
205
+ # omega_K = Z**4/(alpha_K+Z**4)
206
+ # omega_L = Z**4/(alpha_L+Z**4)
207
+ # omega_M = Z**4/(alpha_M+Z**4)
208
+ energy_scale = np.array(spectrum.get_spectral_dims(return_axis=True)[0].values)
209
+ for element in elements:
210
+ atomic_number = pyTEMlib.eds_tools.elements_list.index(element)
211
+ out_tags[element] ={'Z': atomic_number}
212
+ lines = x_sections[str(atomic_number)]['lines']
213
+ K_weight = 0
214
+ K_main = 'None'
215
+ K_lines = []
216
+ L_weight = 0
217
+ L_main = 'None'
218
+ L_lines = []
219
+ M_weight = 0
220
+ M_main = 'None'
221
+ M_lines = []
222
+
223
+ for key, line in lines.items():
224
+ if 'K' == key[0]:
225
+ if line['position'] < energy_scale[-1]:
226
+ K_lines.append(key)
227
+ if line['weight'] > K_weight:
228
+ K_weight = line['weight']
229
+ K_main = key
230
+ if 'L' == key[0]:
231
+ if line['position'] < energy_scale[-1]:
232
+ L_lines.append(key)
233
+ if line['weight'] > L_weight:
234
+ L_weight = line['weight']
235
+ L_main = key
236
+ if 'M' == key[0]:
237
+ if line['position'] < energy_scale[-1]:
238
+ M_lines.append(key)
239
+ if line['weight'] > M_weight:
240
+ M_weight = line['weight']
241
+ M_main = key
242
+
243
+ if K_weight > 0:
244
+ out_tags[element]['K-family'] = {'main': K_main, 'weight': K_weight, 'lines': K_lines}
245
+ height = spectrum[np.searchsorted(energy_scale, x_sections[str(atomic_number)]['lines'][K_main]['position'] )].compute()
246
+ out_tags[element]['K-family']['height'] = height/K_weight
247
+ for key in K_lines:
248
+ out_tags[element]['K-family'][key] = x_sections[str(atomic_number)]['lines'][key]
249
+ if L_weight > 0:
250
+ out_tags[element]['L-family'] = {'main': L_main, 'weight': L_weight, 'lines': L_lines}
251
+ height = spectrum[np.searchsorted(energy_scale, x_sections[str(atomic_number)]['lines'][L_main]['position'] )].compute()
252
+ out_tags[element]['L-family']['height'] = height/L_weight
253
+ for key in L_lines:
254
+ out_tags[element]['L-family'][key] = x_sections[str(atomic_number)]['lines'][key]
255
+ if M_weight > 0:
256
+ out_tags[element]['M-family'] = {'main': M_main, 'weight': M_weight, 'lines': M_lines}
257
+ height = spectrum[np.searchsorted(energy_scale, x_sections[str(atomic_number)]['lines'][M_main]['position'] )].compute()
258
+ out_tags[element]['M-family']['height'] = height/M_weight
259
+ for key in M_lines:
260
+ out_tags[element]['M-family'][key] = x_sections[str(atomic_number)]['lines'][key]
261
+
262
+ xs = get_eds_cross_sections(atomic_number)
263
+ if 'K' in xs and 'K-family' in out_tags[element]:
264
+ out_tags[element]['K-family']['probability'] = xs['K']
265
+ if 'L' in xs and 'L-family' in out_tags[element]:
266
+ out_tags[element]['L-family']['probability'] = xs['L']
267
+ if 'M' in xs and 'M-family' in out_tags[element]:
268
+ out_tags[element]['M-family']['probability'] = xs['M']
269
+
270
+ if 'EDS' not in spectrum.metadata:
271
+ spectrum.metadata['EDS'] = {}
272
+ spectrum.metadata['EDS'].update(out_tags)
273
+ return out_tags
274
+
275
+
276
+ def getFWHM(E, E_ref, FWHM_ref):
277
+ return np.sqrt(2.5*(E-E_ref)+FWHM_ref**2)
278
+
279
+ def gaussian(enrgy_scale, mu, FWHM):
280
+ sig = FWHM/2/np.sqrt(2*np.log(2))
281
+ return np.exp(-np.power(np.array(enrgy_scale) - mu, 2.) / (2 * np.power(sig, 2.)))
282
+
283
+ def get_peak(E, energy_scale):
284
+ E_ref = 5895.0
285
+ FWHM_ref = 136 #eV
286
+ FWHM = getFWHM(E, E_ref, FWHM_ref)
287
+ gaus = gaussian(energy_scale, E, FWHM)
288
+
289
+ return gaus /(gaus.sum()+1e-12)
290
+
291
+
292
+ def initial_model_parameter(spectrum):
293
+ tags = spectrum.metadata['EDS']
294
+ energy_scale = spectrum.get_spectral_dims(return_axis=True)[0]
295
+ p = []
296
+ peaks = []
297
+ keys = []
298
+ for element, lines in tags.items():
299
+ if 'K-family' in lines:
300
+ model = np.zeros(len(energy_scale))
301
+ for line, info in lines['K-family'].items():
302
+ if line[0] == 'K':
303
+ model += get_peak(info['position'], energy_scale)*info['weight']
304
+ lines['K-family']['peaks'] = model /model.sum() # *lines['K-family']['probability']
305
+
306
+ p.append(lines['K-family']['height'] / lines['K-family']['peaks'].max())
307
+ peaks.append(lines['K-family']['peaks'])
308
+ keys.append(element+':K-family')
309
+ if 'L-family' in lines:
310
+ model = np.zeros(len(energy_scale))
311
+ for line, info in lines['L-family'].items():
312
+ if line[0] == 'L':
313
+ model += get_peak(info['position'], energy_scale)*info['weight']
314
+ lines['L-family']['peaks'] = model /model.sum() # *lines['L-family']['probability']
315
+ p.append(lines['L-family']['height'] / lines['L-family']['peaks'].max())
316
+ peaks.append(lines['L-family']['peaks'])
317
+ keys.append(element+':L-family')
318
+ if 'M-family' in lines:
319
+ model = np.zeros(len(energy_scale))
320
+ for line, info in lines['M-family'].items():
321
+ if line[0] == 'M':
322
+ model += get_peak(info['position'], energy_scale)*info['weight']
323
+ lines['M-family']['peaks'] = model /model.sum()*lines['M-family']['probability']
324
+ p.append(lines['M-family']['height'] / lines['M-family']['peaks'].max())
325
+ peaks.append(lines['M-family']['peaks'])
326
+ keys.append(element+':M-family')
327
+
328
+
329
+ #p.extend([300, 10, 1.e-04])
330
+ # p.extend([1, 300, -.02])
331
+ p.extend([1e7, 1e-3, 1500, 20])
332
+ return np.array(peaks), np.array(p), keys
333
+
334
+ def get_model(spectrum, start=100):
335
+ model = np.zeros(len(spectrum))
336
+ for key in spectrum.metadata['EDS']:
337
+ for family in spectrum.metadata['EDS'][key]:
338
+ if isinstance(spectrum.metadata['EDS'][key][family], dict):
339
+ intensity = spectrum.metadata['EDS'][key][family]['areal_density']
340
+ peaks = spectrum.metadata['EDS'][key][family]['peaks']
341
+ model += peaks * intensity
342
+
343
+ if 'detector_efficiency' in spectrum.metadata['EDS']['detector'].keys():
344
+ detector_efficiency = spectrum.metadata['EDS']['detector']['detector_efficiency']
345
+ else:
346
+ detector_efficiency = None
347
+ E_0 = spectrum.metadata['experiment']['acceleration_voltage']
348
+
349
+ # if detector_efficiency is not None:
350
+ # model[start:] += detector_efficiency[start:] * (pp[-3] + pp[-2] * (E_0 - energy_scale) / energy_scale +
351
+ # pp[-1] * (E_0 - energy_scale) ** 2 / energy_scale)
352
+
353
+ return model
354
+
355
+ def fit_model(spectrum, elements, use_detector_efficiency=False):
356
+ out_tags = get_x_ray_lines(spectrum, elements)
357
+ peaks, pin, keys = initial_model_parameter(spectrum)
358
+ energy_scale = spectrum.get_spectral_dims(return_axis=True)[0].values
359
+
360
+ if 'detector' in spectrum.metadata['EDS'].keys():
361
+ if 'start_channel' not in spectrum.metadata['EDS']['detector']:
362
+ spectrum.metadata['EDS']['detector']['start_channel'] = np.searchsorted(energy_scale, 100)
363
+ if 'detector_efficiency' in spectrum.metadata['EDS']['detector'].keys():
364
+ if use_detector_efficiency:
365
+ detector_efficiency = spectrum.metadata['EDS']['detector']['detector_efficiency']
366
+ else:
367
+ use_detector_efficiency = False
368
+ else:
369
+ print('need detector information to fit spectrum')
370
+ return
371
+ start = spectrum.metadata['EDS']['detector']['start_channel']
372
+ energy_scale = energy_scale[start:]
373
+
374
+ E_0= spectrum.metadata['experiment']['acceleration_voltage']
375
+
376
+ def residuals(pp, yy):
377
+ #get_model(peaks, pp, detector_efficiency=None)
378
+ model = np.zeros(len(yy))
379
+ for i in range(len(pp)-4):
380
+ model += peaks[i]*pp[i]
381
+ # pp[-3:] = np.abs(pp[-3:])
382
+
383
+ if use_detector_efficiency:
384
+ bremsstrahlung = pp[-4] / (energy_scale + pp[-3] * energy_scale**2 + pp[-2] * energy_scale**.5) - pp[-1]
385
+
386
+ model[start:] += detector_efficiency[start:] * bremsstrahlung
387
+ #(pp[-3] + pp[-2] * (E_0 - energy_scale) / energy_scale +
388
+ # pp[-1] * (E_0-energy_scale) ** 2 / energy_scale))
389
+
390
+ err = np.abs((yy - model)[start:]) # /np.sqrt(np.abs(yy[start:])+1e-12)
391
+
392
+ return err
393
+
394
+ y = np.array(spectrum) # .compute()
395
+ [p, _] = leastsq(residuals, pin, args=(y))
396
+
397
+ # print(pin[-6:], p[-6:])
398
+
399
+ update_fit_values(out_tags, peaks, p)
400
+
401
+
402
+ if 'EDS' not in spectrum.metadata:
403
+ spectrum.metadata['EDS'] = {}
404
+ spectrum.metadata['EDS'].update(out_tags)
405
+
406
+ return np.array(peaks), np.array(p)
407
+
408
+
409
+ def update_fit_values(out_tags, peaks, p):
410
+ index = 0
411
+ for element, lines in out_tags.items():
412
+ if 'K-family' in lines:
413
+ lines['K-family']['areal_density'] = p[index]
414
+ lines['K-family']['peaks'] = peaks[index]
415
+ index += 1
416
+ if 'L-family' in lines:
417
+ lines['L-family']['areal_density'] = p[index]
418
+ lines['L-family']['peaks'] = peaks[index]
419
+ index += 1
420
+ if 'M-family' in lines:
421
+ lines['M-family']['areal_density'] =p[index]
422
+ lines['M-family']['peaks'] = peaks[index]
423
+ index += 1
424
+
425
+ def get_eds_xsection(Xsection, energy_scale, start_bgd, end_bgd):
426
+ background = pyTEMlib.eels_tools.power_law_background(Xsection, energy_scale, [start_bgd, end_bgd], verbose=False)
427
+ cross_section_core = Xsection- background[0]
428
+ cross_section_core[cross_section_core < 0] = 0.0
429
+ cross_section_core[energy_scale < end_bgd] = 0.0
430
+ return cross_section_core
431
+
432
+
433
+ def get_eds_cross_sections(z, acceleration_voltage=200000):
434
+ energy_scale = np.arange(1,20000)
435
+ Xsection = pyTEMlib.eels_tools.xsec_xrpa(energy_scale, acceleration_voltage/1000., z, 400.)
436
+ edge_info = pyTEMlib.eels_tools.get_x_sections(z)
437
+
438
+
439
+ eds_cross_sections = {}
440
+ Xyield = edge_info['total_fluorescent_yield']
441
+ if 'K' in Xyield:
442
+ start_bgd = edge_info['K1']['onset'] * 0.8
443
+ end_bgd = edge_info['K1']['onset'] - 5
444
+ if start_bgd > end_bgd:
445
+ start_bgd = end_bgd-100
446
+ if start_bgd > energy_scale[0] and end_bgd< energy_scale[-1]-100:
447
+ eds_xsection = get_eds_xsection(Xsection, energy_scale, start_bgd, end_bgd)
448
+ eds_xsection[eds_xsection<0] = 0.
449
+ start_sum = np.searchsorted(energy_scale, edge_info['K1']['onset'])
450
+ end_sum = start_sum+600
451
+ if end_sum> len(Xsection):
452
+ end_sum = len(Xsection)-1
453
+ eds_cross_sections['K1'] = eds_xsection[start_sum:end_sum].sum()
454
+ eds_cross_sections['K'] = eds_xsection[start_sum:end_sum].sum() * Xyield['K']
455
+
456
+ if 'L3' in Xyield:
457
+ start_bgd = edge_info['L3']['onset'] * 0.8
458
+ end_bgd = edge_info['L3']['onset'] - 5
459
+ if start_bgd > end_bgd:
460
+ start_bgd = end_bgd-100
461
+ if start_bgd > energy_scale[0] and end_bgd< energy_scale[-1]-100:
462
+ eds_xsection = get_eds_xsection(Xsection, energy_scale, start_bgd, end_bgd)
463
+ eds_xsection[eds_xsection<0] = 0.
464
+ start_sum = np.searchsorted(energy_scale, edge_info['L3']['onset'])
465
+ end_sum = start_sum+600
466
+ if end_sum> len(Xsection):
467
+ end_sum = len(Xsection)-1
468
+ if end_sum >np.searchsorted(energy_scale, edge_info['K1']['onset'])-10:
469
+ end_sum = np.searchsorted(energy_scale, edge_info['K1']['onset'])-10
470
+ eds_cross_sections['L'] = eds_xsection[start_sum:end_sum].sum()
471
+ L1_channel = np.searchsorted(energy_scale, edge_info['L1']['onset'])
472
+ m_start = start_sum-100
473
+ if m_start < 2:
474
+ m_start = start_sum-20
475
+ l3_rise = np.max(Xsection[m_start: L1_channel-10])-np.min(Xsection[m_start: L1_channel-10])
476
+ l1_rise = np.max(Xsection[L1_channel-10: L1_channel+100])-np.min(Xsection[L1_channel-10: L1_channel+100])
477
+ l1_ratio = l1_rise/l3_rise
478
+
479
+ eds_cross_sections['L1'] = l1_ratio * eds_cross_sections['L']
480
+ eds_cross_sections['L2'] = eds_cross_sections['L']*(1-l1_ratio)*1/3
481
+ eds_cross_sections['L3'] = eds_cross_sections['L']*(1-l1_ratio)*2/3
482
+ eds_cross_sections['yield_L1'] = Xyield['L1']
483
+ eds_cross_sections['yield_L2'] = Xyield['L2']
484
+ eds_cross_sections['yield_L3'] = Xyield['L3']
485
+
486
+ eds_cross_sections['L'] = eds_cross_sections['L1']*Xyield['L1']+eds_cross_sections['L2']*Xyield['L2']+eds_cross_sections['L3']*Xyield['L3']
487
+ # eds_cross_sections['L'] /= 8
488
+ if 'M5' in Xyield:
489
+ start_bgd = edge_info['M5']['onset'] * 0.8
490
+ end_bgd = edge_info['M5']['onset'] - 5
491
+ if start_bgd > end_bgd:
492
+ start_bgd = end_bgd-100
493
+ if start_bgd > energy_scale[0] and end_bgd< energy_scale[-1]-100:
494
+ eds_xsection = get_eds_xsection(Xsection, energy_scale, start_bgd, end_bgd)
495
+ eds_xsection[eds_xsection<0] = 0.
496
+ start_sum = np.searchsorted(energy_scale, edge_info['M5']['onset'])
497
+ end_sum = start_sum+600
498
+ if end_sum > np.searchsorted(energy_scale, edge_info['L3']['onset'])-10:
499
+ end_sum = np.searchsorted(energy_scale, edge_info['L3']['onset'])-10
500
+ eds_cross_sections['M'] = eds_xsection[start_sum:end_sum].sum()
501
+ #print(edge_info['M5']['onset'] - edge_info['M1']['onset'])
502
+ M3_channel = np.searchsorted(energy_scale, edge_info['M3']['onset'])
503
+ M1_channel = np.searchsorted(energy_scale, edge_info['M1']['onset'])
504
+ m5_rise = np.max(Xsection[start_sum-100: M3_channel-10])-np.min(Xsection[start_sum-100: M3_channel-10])
505
+ m3_rise = np.max(Xsection[M3_channel-10: M1_channel-10])-np.min(Xsection[M3_channel-10: M1_channel-10])
506
+ m1_rise = np.max(Xsection[M1_channel-10: M1_channel+100])-np.min(Xsection[M1_channel-10: M1_channel+100])
507
+ m1_ratio = m1_rise/m5_rise
508
+ m3_ratio = m3_rise/m5_rise
509
+ m5_ratio = 1-(m1_ratio+m3_ratio)
510
+ #print(m1_ratio, m3_ratio, 1-(m1_ratio+m3_ratio))
511
+ eds_cross_sections['M1'] = m1_ratio * eds_cross_sections['M']
512
+ eds_cross_sections['M2'] = m3_ratio * eds_cross_sections['M']*1/3
513
+ eds_cross_sections['M3'] = m3_ratio * eds_cross_sections['M']*2/3
514
+ eds_cross_sections['M4'] = m5_ratio * eds_cross_sections['M']*2/5
515
+ eds_cross_sections['M5'] = m5_ratio * eds_cross_sections['M']*3/5
516
+ eds_cross_sections['yield_M1'] = Xyield['M1']
517
+ eds_cross_sections['yield_M2'] = Xyield['M2']
518
+ eds_cross_sections['yield_M3'] = Xyield['M3']
519
+ eds_cross_sections['yield_M4'] = Xyield['M4']
520
+ eds_cross_sections['yield_M5'] = Xyield['M5']
521
+ eds_cross_sections['M'] = eds_cross_sections['M1']*Xyield['M1']+eds_cross_sections['M2']*Xyield['M2']+eds_cross_sections['M3']*Xyield['M3'] \
522
+ +eds_cross_sections['M4']*Xyield['M4']+eds_cross_sections['M5']*Xyield['M5']
523
+ #eds_cross_sections['M'] /= 18
524
+ return eds_cross_sections
525
+
526
+
527
+ def get_phases(dataset, mode='kmeans', number_of_phases=4):
528
+ X_vec = np.array(dataset).reshape(dataset.shape[0]*dataset.shape[1], dataset.shape[2])
529
+ X_vec = np.divide(X_vec.T, X_vec.sum(axis=1)).T
530
+ if mode != 'kmeans':
531
+ gmm = GaussianMixture(n_components=number_of_phases, covariance_type="full") #choose number of components
532
+
533
+ gmm_results = gmm.fit(np.array(X_vec)) #we can intelligently fold the data and perform GM
534
+ gmm_labels = gmm_results.fit_predict(X_vec)
535
+
536
+ dataset.metadata['gaussian_mixing_model'] = {'map': gmm_labels.reshape(dataset.shape[0], dataset.shape[1]),
537
+ 'covariances': gmm.covariances_,
538
+ 'weights': gmm.weights_,
539
+ 'means': gmm_results.means_}
540
+ else:
541
+ km = KMeans(number_of_phases, n_init =10) #choose number of clusters
542
+ km_results = km.fit(np.array(X_vec)) #we can intelligently fold the data and perform Kmeans
543
+ dataset.metadata['kmeans'] = {'map': km_results.labels_.reshape(dataset.shape[0], dataset.shape[1]),
544
+ 'means': km_results.cluster_centers_}
545
+
546
+ def plot_phases(dataset, image=None, survey_image=None):
547
+ if survey_image is not None:
548
+ ncols = 3
549
+ else:
550
+ ncols = 2
551
+ axis_index = 0
552
+ fig, axes = plt.subplots(nrows=1, ncols=ncols, figsize = (10,3))
553
+ if survey_image is not None:
554
+ im = axes[0].imshow(survey_image.T)
555
+ axis_index += 1
556
+ #if 'gaussian_mixing_model' in dataset.metadata:
557
+ # phase_spectra = dataset.metadata['gaussian_mixing_model']['means']
558
+ # map = dataset.metadata['gaussian_mixing_model']['map']
559
+ #el
560
+ if 'kmeans' in dataset.metadata:
561
+ phase_spectra = dataset.metadata['kmeans']['means']
562
+ map = dataset.metadata['kmeans']['map']
563
+
564
+ cmap = plt.get_cmap('jet', len(phase_spectra))
565
+ im = axes[axis_index].imshow(image.T,cmap='gray')
566
+ im = axes[axis_index].imshow(map.T, cmap=cmap,vmin=np.min(map) - 0.5,
567
+ vmax=np.max(map) + 0.5,alpha=0.2)
568
+
569
+ cbar = fig.colorbar(im, ax=axes[axis_index])
570
+ cbar.ax.set_yticks(np.arange(0, len(phase_spectra) ))
571
+ cbar.ax.set_ylabel("GMM Phase", fontsize = 14)
572
+ axis_index += 1
573
+ for index, spectrum in enumerate(phase_spectra):
574
+ axes[axis_index].plot(dataset.energy/1000, spectrum, color = cmap(index), label=str(index))
575
+ axes[axis_index].set_xlabel('energy (keV)')
576
+ plt.legend()
577
+ plt.tight_layout()
578
+ plt.show()
579
+ return fig
580
+
581
+
582
+ def plot_lines(eds_quantification: dict, axis: plt.Axes):
583
+ for key, lines in eds_quantification.items():
584
+ if 'K-family' in lines:
585
+ intensity = lines['K-family']['height']
586
+ for line in lines['K-family']:
587
+ if line[0] == 'K':
588
+ pos = lines['K-family'][line]['position']
589
+ axis.plot([pos,pos], [0, intensity*lines['K-family'][line]['weight']], color='blue')
590
+ if line == lines['K-family']['main']:
591
+ axis.text(pos,0, key+'\n'+line, verticalalignment='top')
592
+
593
+ if 'L-family' in lines:
594
+ intensity = lines['L-family']['height']
595
+ for line in lines['L-family']:
596
+ if line[0] == 'L':
597
+ pos = lines['L-family'][line]['position']
598
+ axis.plot([pos,pos], [0, intensity*lines['L-family'][line]['weight']], color='black')
599
+ if line in [lines['L-family']['main'], 'L3-M5', 'L3-N5', 'L1-M3']:
600
+ axis.text(pos,0, key+'\n'+line, verticalalignment='top')
601
+
602
+ if 'M-family' in lines:
603
+ intensity = lines['M-family']['height']
604
+ for line in lines['M-family']:
605
+ if line[0] == 'M':
606
+ pos = lines['M-family'][line]['position']
607
+ axis.plot([pos,pos], [0, intensity*lines['M-family'][line]['weight']], color='green')
608
+ if line in [lines['M-family']['main'], 'M5-N7', 'M4-N6']:
609
+ axis.text(pos,0, key+'\n'+line, verticalalignment='top')