pyTEMlib 0.2020.11.0__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 (59) hide show
  1. pyTEMlib/__init__.py +11 -11
  2. pyTEMlib/animation.py +631 -0
  3. pyTEMlib/atom_tools.py +240 -222
  4. pyTEMlib/config_dir.py +57 -29
  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 -486
  11. pyTEMlib/{interactive_eels.py → eels_dialog_utilities.py} +1199 -1524
  12. pyTEMlib/eels_tools.py +2031 -1731
  13. pyTEMlib/file_tools.py +1276 -491
  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 -0
  19. pyTEMlib/image_tools.py +1399 -956
  20. pyTEMlib/info_widget.py +933 -0
  21. pyTEMlib/interactive_image.py +1 -0
  22. pyTEMlib/kinematic_scattering.py +1196 -0
  23. pyTEMlib/low_loss_widget.py +176 -0
  24. pyTEMlib/microscope.py +61 -78
  25. pyTEMlib/peak_dialog.py +1047 -350
  26. pyTEMlib/peak_dlg.py +286 -248
  27. pyTEMlib/probe_tools.py +653 -202
  28. pyTEMlib/sidpy_tools.py +153 -129
  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.0.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.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/WHEEL +6 -5
  36. {pyTEMlib-0.2020.11.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/entry_points.txt +0 -1
  37. pyTEMlib/KinsCat.py +0 -2685
  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 -86
  47. pyTEMlib/dm3_reader.py +0 -609
  48. pyTEMlib/edges_db.py +0 -76
  49. pyTEMlib/eels_dlg.py +0 -240
  50. pyTEMlib/hdf_utils.py +0 -481
  51. pyTEMlib/image_tools1.py +0 -2194
  52. pyTEMlib/info_dialog.py +0 -227
  53. pyTEMlib/info_dlg.py +0 -205
  54. pyTEMlib/nion_reader.py +0 -293
  55. pyTEMlib/nsi_reader.py +0 -165
  56. pyTEMlib/structure_tools.py +0 -316
  57. pyTEMlib-0.2020.11.0.dist-info/METADATA +0 -20
  58. pyTEMlib-0.2020.11.0.dist-info/RECORD +0 -42
  59. {pyTEMlib-0.2020.11.0.dist-info → pyTEMlib-0.2024.8.4.dist-info}/top_level.txt +0 -0
pyTEMlib/eels_dialog.py CHANGED
@@ -1,486 +1,749 @@
1
- from PyQt5 import QtCore, QtWidgets
2
- from pyTEMlib import eels_dlg
3
-
4
- import numpy as np
5
-
6
- from pyTEMlib import eels_tools as eels
7
- from pyTEMlib import interactive_eels
8
- import matplotlib.patches as patches
9
- from pyTEMlib import file_tools as ft
10
- import sidpy
11
-
12
- _version = 000
13
-
14
-
15
- class EELSDialog(QtWidgets.QDialog):
16
- """
17
- EELS Input Dialog for Chemical Analysis
18
- """
19
-
20
- def __init__(self, dataset=None):
21
- super().__init__(None, QtCore.Qt.WindowStaysOnTopHint)
22
- # Create an instance of the GUI
23
- self.ui = eels_dlg.UiDialog(self)
24
- # Run the .setup_ui() method to show the GUI
25
- # self.ui.setup_ui(self)
26
-
27
- self.set_action()
28
-
29
- self.dataset = dataset
30
- self.energy_scale = np.array([])
31
- self.model = np.array([])
32
- self.edges = {}
33
- self.axis = None
34
- self.show_regions = False
35
- self.show()
36
-
37
- if dataset is None:
38
- # make a dummy dataset
39
- dataset = ft.make_dummy_dataset(sidpy.DataTypes.SPECTRUM)
40
-
41
- if not isinstance(dataset, sidpy.Dataset):
42
- raise TypeError('dataset has to be a sidpy dataset')
43
-
44
- self.set_dataset(dataset)
45
- # TODO: set elements does not work correctly for periodic table
46
- # selected_edges = eels.find_edges(dataset, sensitivity=3)
47
- selected_edges = []
48
- initial_elements = []
49
-
50
- for edge in selected_edges:
51
- initial_elements.append(edge.split('-')[0])
52
- if len(initial_elements) > 0:
53
- self.set_elements(initial_elements)
54
-
55
- self.pt_dialog = interactive_eels.PeriodicTableDialog(energy_scale=self.energy_scale,
56
- initial_elements=initial_elements)
57
- self.pt_dialog.signal_selected[list].connect(self.set_elements)
58
-
59
- self.dataset.plot()
60
-
61
- if hasattr(self.dataset.view, 'axes'):
62
- self.axis = self.dataset.view.axes[-1]
63
- elif hasattr(self.dataset.view, 'axis'):
64
- self.axis = self.dataset.view.axis
65
- self.figure = self.axis.figure
66
-
67
- self.cid = self.figure.canvas.mpl_connect('draw_event', self.plot)
68
-
69
- self.plot()
70
- self.ui.edit4.setFocus()
71
-
72
- def set_dataset(self, dataset):
73
-
74
- self.dataset = dataset
75
- if 'edges' not in self.dataset.metadata or self.dataset.metadata['edges'] == {}:
76
- self.dataset.metadata['edges'] = {'0': {}, 'model': {}}
77
- self.edges = self.dataset.metadata['edges']
78
-
79
- spec_dim = -1
80
- for dim, axis in dataset._axes.items():
81
- if axis.dimension_type == sidpy.DimensionTypes.SPECTRAL:
82
- spec_dim = dim
83
- if spec_dim < 0:
84
- raise TypeError('We need at least one SPECTRAL dimension')
85
-
86
- self.spec_dim = spec_dim
87
- self.energy_scale = dataset._axes[spec_dim].values
88
-
89
- self.ui.edit2.setText(f"{self.energy_scale[-2]:.3f}")
90
- if 'fit_area' not in self.edges:
91
- self.edges['fit_area'] = {}
92
- if 'fit_start' not in self.edges['fit_area']:
93
- self.ui.edit1.setText(f"{self.energy_scale[50]:.3f}")
94
- self.edges['fit_area']['fit_start'] = float(self.ui.edit1.displayText())
95
- else:
96
- self.ui.edit1.setText(f"{self.edges['fit_area']['fit_start']:.3f}")
97
- if 'fit_end' not in self.edges['fit_area']:
98
- self.ui.edit2.setText(f"{self.energy_scale[-2]:.3f}")
99
- self.edges['fit_area']['fit_end'] = float(self.ui.edit2.displayText())
100
- else:
101
- self.ui.edit2.setText(f"{self.edges['fit_area']['fit_end']:.3f}")
102
-
103
- self.update()
104
-
105
- def update(self):
106
- index = self.ui.list3.currentIndex() # which edge
107
- edge = self.edges[str(index)]
108
-
109
- if 'Z' in edge:
110
- self.ui.list5.setCurrentIndex(self.ui.edge_sym.index(edge['symmetry']))
111
- self.ui.edit4.setText(str(edge['Z']))
112
- self.ui.unit4.setText(edge['element'])
113
- self.ui.edit6.setText(f"{edge['onset']:.2f}")
114
- self.ui.edit7.setText(f"{edge['start_exclude']:.2f}")
115
- self.ui.edit8.setText(f"{edge['end_exclude']:.2f}")
116
- self.ui.edit9.setText(f"{edge['areal_density']:.2e}")
117
- else:
118
- self.ui.list3.setCurrentIndex(0)
119
- self.ui.edit4.setText(str(0))
120
- self.ui.unit4.setText(' ')
121
- self.ui.edit6.setText(f"{0:.2f}")
122
- self.ui.edit7.setText(f"{0:.2f}")
123
- self.ui.edit8.setText(f"{0:.2f}")
124
- self.ui.edit9.setText(f"{0:.2e}")
125
-
126
- def update_element(self, z):
127
- # We check whether this element is already in the
128
- zz = eels.get_z(z)
129
- for key, edge in self.edges.items():
130
- if 'Z' in edge:
131
- if zz == edge['Z']:
132
- return False
133
-
134
- major_edge = ''
135
- minor_edge = ''
136
- all_edges = {}
137
- x_section = eels.get_x_sections(zz)
138
- edge_start = 10 # int(15./ft.get_slope(self.energy_scale)+0.5)
139
- for key in x_section:
140
- if len(key) == 2 and key[0] in ['K', 'L', 'M', 'N', 'O'] and key[1].isdigit():
141
- if self.energy_scale[edge_start] < x_section[key]['onset'] < self.energy_scale[-edge_start]:
142
- if key in ['K1', 'L3', 'M5']:
143
- major_edge = key
144
- elif key in self.ui.edge_sym:
145
- if minor_edge == '':
146
- minor_edge = key
147
- if int(key[-1]) % 2 > 0:
148
- if int(minor_edge[-1]) % 2 == 0 or key[-1] > minor_edge[-1]:
149
- minor_edge = key
150
-
151
- all_edges[key] = {'onset': x_section[key]['onset']}
152
-
153
- if major_edge != '':
154
- key = major_edge
155
- elif minor_edge != '':
156
- key = minor_edge
157
- else:
158
- print(f'Could not find no edge of {zz} in spectrum')
159
- return False
160
-
161
- index = self.ui.list3.currentIndex()
162
-
163
- if str(index) not in self.edges:
164
- self.edges[str(index)] = {}
165
-
166
- start_exclude = x_section[key]['onset'] - x_section[key]['excl before']
167
- end_exclude = x_section[key]['onset'] + x_section[key]['excl after']
168
-
169
- self.edges[str(index)] = {'Z': zz, 'symmetry': key, 'element': eels.elements[zz],
170
- 'onset': x_section[key]['onset'], 'end_exclude': end_exclude,
171
- 'start_exclude': start_exclude}
172
- self.edges[str(index)]['all_edges'] = all_edges
173
- self.edges[str(index)]['chemical_shift'] = 0.0
174
- self.edges[str(index)]['areal_density'] = 0.0
175
- self.edges[str(index)]['original_onset'] = self.edges[str(index)]['onset']
176
- return True
177
-
178
- def on_enter(self):
179
- sender = self.sender()
180
- edge_list = self.ui.list3
181
-
182
- if sender.objectName() == 'fit_start_edit':
183
- value = float(str(sender.displayText()).strip())
184
- if value < self.energy_scale[0]:
185
- value = self.energy_scale[0]
186
- if value > self.energy_scale[-5]:
187
- value = self.energy_scale[-5]
188
- self.edges['fit_area']['fit_start'] = value
189
- sender.setText(str(self.edges['fit_area']['fit_start']))
190
- elif sender.objectName() == 'fit_end_edit':
191
- value = float(str(sender.displayText()).strip())
192
- if value < self.energy_scale[5]:
193
- value = self.energy_scale[5]
194
- if value > self.energy_scale[-1]:
195
- value = self.energy_scale[-1]
196
- self.edges['fit_area']['fit_end'] = value
197
- sender.setText(str(self.edges['fit_area']['fit_end']))
198
- elif sender.objectName() == 'element_edit':
199
- if str(sender.displayText()).strip() == '0':
200
- sender.setText('PT')
201
- self.pt_dialog.energy_scale = self.energy_scale
202
- self.pt_dialog.show()
203
- else:
204
- self.update_element(str(sender.displayText()).strip())
205
- self.update()
206
- elif sender.objectName() in ['onset_edit', 'excl_start_edit', 'excl_end_edit']:
207
- self.check_area_consistency()
208
-
209
- elif sender.objectName() == 'multiplier_edit':
210
- index = edge_list.currentIndex()
211
- self.edges[str(index)]['areal_density'] = float(self.ui.edit9.displayText())
212
- if 'background' not in self.edges['model']:
213
- print(' no background')
214
- return
215
- self.model = self.edges['model']['background']
216
- for key in self.edges:
217
- if key.isdigit():
218
- self.model = self.model + self.edges[key]['areal_density'] * self.edges[key]['data']
219
- self.plot()
220
- else:
221
- return
222
- if self.show_regions:
223
- self.plot()
224
-
225
- def sort_elements(self):
226
- onsets = []
227
- for index, edge in self.edges.items():
228
- if index.isdigit():
229
- onsets.append(float(edge['onset']))
230
-
231
- arg_sorted = np.argsort(onsets)
232
- edges = self.edges.copy()
233
- for index, isorted in enumerate(arg_sorted):
234
- self.edges[str(index)] = edges[str(isorted)].copy()
235
-
236
- index = 0
237
- edge = self.edges['0']
238
- dispersion = self.energy_scale[1]-self.energy_scale[0]
239
-
240
- while str(index + 1) in self.edges:
241
- next_edge = self.edges[str(index + 1)]
242
- if edge['end_exclude'] > next_edge['start_exclude'] - 5 * dispersion:
243
- edge['end_exclude'] = next_edge['start_exclude'] - 5 * dispersion
244
- edge = next_edge
245
- index += 1
246
-
247
- if edge['end_exclude'] > self.energy_scale[-3]:
248
- edge['end_exclude'] = self.energy_scale[-3]
249
-
250
- def set_elements(self, selected_elements):
251
-
252
- edge_list = self.ui.list3
253
- index = edge_list.currentIndex()
254
-
255
- for elem in selected_elements:
256
- if self.update_element(elem):
257
- index = edge_list.currentIndex()
258
- edge_list.setCurrentIndex(index + 1)
259
- self.sort_elements()
260
- edge_list.setCurrentIndex(index)
261
- self.update()
262
-
263
- def plot(self):
264
- self.energy_scale = self.dataset._axes[self.spec_dim].values
265
- if self.dataset.data_type == sidpy.DataTypes.SPECTRAL_IMAGE:
266
- spectrum = self.dataset.view.get_spectrum()
267
- self.axis = self.dataset.view.axes[1]
268
- else:
269
- spectrum = np.array(self.dataset)
270
- self.axis = self.dataset.view.axis
271
-
272
- if self.ui.select10.isChecked():
273
- if 'experiment' in self.dataset.metadata:
274
- exp = self.dataset.metadata['experiment']
275
- if 'convergence_angle' not in exp:
276
- raise ValueError('need a convergence_angle in experiment of metadata dictionary ')
277
- alpha = exp['convergence_angle']
278
- beta = exp['collection_angle']
279
- beam_kv = exp['acceleration_voltage']
280
-
281
- eff_beta = eels.effective_collection_angle(self.energy_scale, alpha, beta, beam_kv)
282
- edges = eels.make_cross_sections(self.edges, np.array(self.energy_scale), beam_kv, eff_beta)
283
- self.edges = eels.fit_edges2(spectrum, self.energy_scale, edges)
284
- areal_density = []
285
- elements = []
286
- for key in edges:
287
- if key.isdigit(): # only edges have numbers in that dictionary
288
- elements.append(edges[key]['element'])
289
- areal_density.append(edges[key]['areal_density'])
290
- areal_density = np.array(areal_density)
291
- out_string = '\nRelative composition: \n'
292
- for i, element in enumerate(elements):
293
- out_string += f'{element}: {areal_density[i] / areal_density.sum() * 100:.1f}% '
294
-
295
- self.model = self.edges['model']['spectrum']
296
- self.update()
297
-
298
- x_limit = self.axis.get_xlim()
299
- y_limit = self.axis.get_ylim()
300
- self.axis.clear()
301
-
302
- self.axis.plot(self.energy_scale, spectrum, label='spectrum')
303
- if len(self.model) > 1:
304
- self.axis.plot(self.energy_scale, self.model, label='model')
305
- self.axis.plot(self.energy_scale, spectrum - self.model, label='difference')
306
- self.axis.plot(self.energy_scale, (spectrum - self.model) / np.sqrt(spectrum), label='Poisson')
307
- self.axis.legend()
308
- self.axis.set_xlim(x_limit)
309
- self.axis.set_ylim(y_limit)
310
-
311
- if self.ui.show_edges.isChecked():
312
- self.show_edges()
313
- if self.show_regions:
314
- self.plot_regions()
315
- self.figure.canvas.draw_idle()
316
-
317
- def plot_regions(self):
318
- y_min, y_max = self.axis.get_ylim()
319
- height = y_max - y_min
320
-
321
- rect = []
322
- if 'fit_area' in self.edges:
323
- color = 'blue'
324
- alpha = 0.2
325
- x_min = self.edges['fit_area']['fit_start']
326
- width = self.edges['fit_area']['fit_end'] - x_min
327
- rect.append(patches.Rectangle((x_min, y_min), width, height,
328
- edgecolor=color, alpha=alpha, facecolor=color))
329
- self.axis.add_patch(rect[0])
330
- self.axis.text(x_min, y_max, 'fit region', verticalalignment='top')
331
- color = 'red'
332
- alpha = 0.5
333
- for key in self.edges:
334
- if key.isdigit():
335
- x_min = self.edges[key]['start_exclude']
336
- width = self.edges[key]['end_exclude']-x_min
337
- rect.append(patches.Rectangle((x_min, y_min), width, height,
338
- edgecolor=color, alpha=alpha, facecolor=color))
339
- self.axis.add_patch(rect[-1])
340
- self.axis.text(x_min, y_max, f"exclude\n edge {int(key)+1}", verticalalignment='top')
341
-
342
- def show_edges(self):
343
- x_min, x_max = self.axis.get_xlim()
344
- y_min, y_max = self.axis.get_ylim()
345
-
346
- for key, edge in self.edges.items():
347
- i = 0
348
- if key.isdigit():
349
- element = edge['element']
350
- for sym in edge['all_edges']:
351
- x = edge['all_edges'][sym]['onset'] + edge['chemical_shift']
352
- if x_min < x < x_max:
353
- self.axis.text(x, y_max, '\n' * i + f"{element}-{sym}",
354
- verticalalignment='top', color='black')
355
- self.axis.axvline(x, ymin=0, ymax=1, color='gray')
356
- i += 1
357
-
358
- def check_area_consistency(self):
359
- if self.dataset is None:
360
- return
361
- onset = float(self.ui.edit6.displayText())
362
- excl_start = float(self.ui.edit7.displayText())
363
- excl_end = float(self.ui.edit8.displayText())
364
- if onset < self.energy_scale[2]:
365
- onset = self.energy_scale[2]
366
- excl_start = self.energy_scale[2]
367
- if onset > self.energy_scale[-2]:
368
- onset = self.energy_scale[-2]
369
- excl_end = self.energy_scale[-2]
370
- if excl_start > onset:
371
- excl_start = onset
372
- if excl_end < onset:
373
- excl_end = onset
374
-
375
- index = self.ui.list3.currentIndex()
376
- self.edges[str(index)]['chemical_shift'] = onset - self.edges[str(index)]['original_onset']
377
- self.edges[str(index)]['onset'] = onset
378
- self.edges[str(index)]['end_exclude'] = excl_end
379
- self.edges[str(index)]['start_exclude'] = excl_start
380
-
381
- self.update()
382
-
383
- def on_list_enter(self):
384
- sender = self.sender()
385
-
386
- if sender.objectName() == 'edge_list':
387
- index = self.ui.list3.currentIndex()
388
-
389
- number_of_edges = 0
390
- for key in self.edges:
391
- if key.isdigit():
392
- if int(key) > number_of_edges:
393
- number_of_edges = int(key)
394
- number_of_edges += 1
395
- if index > number_of_edges:
396
- index = number_of_edges
397
- self.ui.list3.setCurrentIndex(index)
398
- if str(index) not in self.edges:
399
- self.edges[str(index)] = {'Z': 0, 'symmetry': 'K1', 'element': 'H', 'onset': 0, 'end_exclude': 0,
400
- 'start_exclude': 0, 'areal_density': 0}
401
-
402
- self.update()
403
- elif sender.objectName() == 'symmetry_list':
404
- sym = self.ui.list5.currentText()
405
- index = self.ui.list3.currentIndex()
406
- zz = self.edges[str(index)]['Z']
407
- if zz > 1:
408
- x_section = eels.get_x_sections(zz)
409
- if sym in x_section:
410
- start_exclude = x_section[sym]['onset'] - x_section[sym]['excl before']
411
- end_exclude = x_section[sym]['onset'] + x_section[sym]['excl after']
412
- self.edges[str(index)].update({'symmetry': sym, 'onset': x_section[sym]['onset'],
413
- 'end_exclude': end_exclude, 'start_exclude': start_exclude})
414
- self.edges[str(index)]['chemical_shift'] = 0.0
415
- self.edges[str(index)]['areal_density'] = 0.0
416
- self.edges[str(index)]['original_onset'] = self.edges[index]['onset']
417
- self.update()
418
- elif sender.objectName() == 'symmetry_method':
419
- self.ui.select5.setCurrentIndex(0)
420
-
421
- def on_check(self):
422
- sender = self.sender()
423
-
424
- if sender.objectName() == 'edge_check':
425
- self.show_regions = sender.isChecked()
426
- self.plot()
427
-
428
- def do_all_button_click(self):
429
- pass
430
-
431
- def do_fit_button_click(self):
432
- if 'experiment' in self.dataset.metadata:
433
- exp = self.dataset.metadata['experiment']
434
- if 'convergence_angle' not in exp:
435
- raise ValueError('need a convergence_angle in experiment of metadata dictionary ')
436
- alpha = exp['convergence_angle']
437
- beta = exp['collection_angle']
438
- beam_kv = exp['acceleration_voltage']
439
-
440
- else:
441
- raise ValueError('need a experiment parameter in metadata dictionary')
442
- self.energy_scale = self.dataset._axes[self.spec_dim].values
443
- eff_beta = eels.effective_collection_angle(self.energy_scale, alpha, beta, beam_kv)
444
-
445
- edges = eels.make_cross_sections(self.edges, np.array(self.energy_scale), beam_kv, eff_beta)
446
-
447
- if self.dataset.data_type == sidpy.DataTypes.SPECTRAL_IMAGE:
448
- spectrum = self.dataset.view.get_spectrum()
449
- else:
450
- spectrum = self.dataset
451
- self.edges = eels.fit_edges2(spectrum, self.energy_scale, edges)
452
- areal_density = []
453
- elements = []
454
- for key in edges:
455
- if key.isdigit(): # only edges have numbers in that dictionary
456
- elements.append(edges[key]['element'])
457
- areal_density.append(edges[key]['areal_density'])
458
- areal_density = np.array(areal_density)
459
- out_string = '\nRelative composition: \n'
460
- for i, element in enumerate(elements):
461
- out_string += f'{element}: {areal_density[i] / areal_density.sum() * 100:.1f}% '
462
-
463
- self.model = self.edges['model']['spectrum']
464
- self.update()
465
- self.plot()
466
-
467
- def set_action(self):
468
- self.ui.edit1.editingFinished.connect(self.on_enter)
469
- self.ui.edit2.editingFinished.connect(self.on_enter)
470
- self.ui.list3.activated[str].connect(self.on_list_enter)
471
- self.ui.check3.clicked.connect(self.on_check)
472
- self.ui.edit4.editingFinished.connect(self.on_enter)
473
- self.ui.list5.activated[str].connect(self.on_list_enter)
474
- self.ui.select5.activated[str].connect(self.on_list_enter)
475
-
476
- self.ui.edit6.editingFinished.connect(self.on_enter)
477
- self.ui.edit7.editingFinished.connect(self.on_enter)
478
- self.ui.edit8.editingFinished.connect(self.on_enter)
479
- self.ui.edit9.editingFinished.connect(self.on_enter)
480
-
481
- self.ui.check10.clicked.connect(self.on_check)
482
- self.ui.select10.clicked.connect(self.on_check)
483
- self.ui.show_edges.clicked.connect(self.on_check)
484
-
485
- self.ui.do_all_button.clicked.connect(self.do_all_button_click)
486
- self.ui.do_fit_button.clicked.connect(self.do_fit_button_click)
1
+ """
2
+ Author: Gerd Duscher
3
+ """
4
+
5
+
6
+ import numpy as np
7
+ import warnings
8
+
9
+ import ipywidgets
10
+ import IPython.display
11
+ # from IPython.display import display
12
+ import matplotlib
13
+ import matplotlib.pylab as plt
14
+ import matplotlib.patches as patches
15
+
16
+ from pyTEMlib import file_tools as ft
17
+ from pyTEMlib import eels_tools as eels
18
+ from pyTEMlib import eels_dialog_utilities
19
+
20
+ import sidpy
21
+
22
+
23
+ class CurveVisualizer(object):
24
+ """Plots a sidpy.Dataset with spectral dimension-type
25
+
26
+ """
27
+ def __init__(self, dset, spectrum_number=None, axis=None, leg=None, **kwargs):
28
+ if not isinstance(dset, sidpy.Dataset):
29
+ raise TypeError('dset should be a sidpy.Dataset object')
30
+ if axis is None:
31
+ self.fig = plt.figure()
32
+ self.axis = self.fig.add_subplot(1, 1, 1)
33
+ else:
34
+ self.axis = axis
35
+ self.fig = axis.figure
36
+
37
+ self.dset = dset
38
+ self.selection = []
39
+ [self.spec_dim, self.energy_scale] = ft.get_dimensions_by_type('spectral', self.dset)[0]
40
+
41
+ self.lined = dict()
42
+ self.plot(**kwargs)
43
+
44
+ def plot(self, **kwargs):
45
+ if self.dset.data_type.name == 'IMAGE_STACK':
46
+ line1, = self.axis.plot(self.energy_scale.values, self.dset[0, 0], label='spectrum', **kwargs)
47
+ else:
48
+ line1, = self.axis.plot(self.energy_scale.values, self.dset, label='spectrum', **kwargs)
49
+ lines = [line1]
50
+ if 'add2plot' in self.dset.metadata:
51
+ data = self.dset.metadata['add2plot']
52
+ for key, line in data.items():
53
+ line_add, = self.axis.plot(self.energy_scale.values, line['data'], label=line['legend'])
54
+ lines.append(line_add)
55
+
56
+ legend = self.axis.legend(loc='upper right', fancybox=True, shadow=True)
57
+ legend.get_frame().set_alpha(0.4)
58
+
59
+ for legline, origline in zip(legend.get_lines(), lines):
60
+ legline.set_picker(True)
61
+ legline.set_pickradius(5) # 5 pts tolerance
62
+ self.lined[legline] = origline
63
+ self.fig.canvas.mpl_connect('pick_event', self.onpick)
64
+
65
+ self.axis.axhline(0, color='gray', alpha=0.6)
66
+ self.axis.set_xlabel(self.dset.labels[0])
67
+ self.axis.set_ylabel(self.dset.data_descriptor)
68
+ self.axis.ticklabel_format(style='sci', scilimits=(-2, 3))
69
+ self.fig.canvas.draw_idle()
70
+
71
+ def update(self, **kwargs):
72
+ x_limit = self.axis.get_xlim()
73
+ y_limit = self.axis.get_ylim()
74
+ self.axis.clear()
75
+ self.plot(**kwargs)
76
+ self.axis.set_xlim(x_limit)
77
+ self.axis.set_ylim(y_limit)
78
+
79
+ def onpick(self, event):
80
+ # on the pick event, find the orig line corresponding to the
81
+ # legend proxy line, and toggle the visibility
82
+ legline = event.artist
83
+ origline = self.lined[legline]
84
+ vis = not origline.get_visible()
85
+ origline.set_visible(vis)
86
+ # Change the alpha on the line in the legend, so we can see what lines
87
+ # have been toggled
88
+ if vis:
89
+ legline.set_alpha(1.0)
90
+ else:
91
+ legline.set_alpha(0.2)
92
+ self.fig.canvas.draw()
93
+
94
+ def get_core_loss_sidebar():
95
+ side_bar = ipywidgets.GridspecLayout(14, 3,width='auto', grid_gap="0px")
96
+
97
+
98
+ row = 0
99
+ side_bar[row, :3] = ipywidgets.ToggleButton(description='Fit Area',
100
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
101
+ tooltip='Shows fit regions and regions excluded from fit',
102
+ button_style='info') #ipywidgets.ButtonStyle(button_color='lightblue'))
103
+ row += 1
104
+ side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Fit Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
105
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
106
+ row += 1
107
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Fit End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
108
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
109
+
110
+ row += 1
111
+
112
+ side_bar[row, :3] = ipywidgets.Button(description='Elements',
113
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
114
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
115
+ row += 1
116
+ side_bar[row, :2] = ipywidgets.Dropdown(
117
+ options=[('Edge 1', 0), ('Edge 2', 1), ('Edge 3', 2), ('Edge 4', 3),('Add Edge', -1)],
118
+ value=0,
119
+ description='Edges:',
120
+ disabled=False,
121
+ layout=ipywidgets.Layout(width='200px'))
122
+ """side_bar[row,2] = ipywidgets.ToggleButton(
123
+ description='Regions',
124
+ disabled=False,
125
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
126
+ tooltip='Shows fit regions and regions excluded from fit',
127
+ layout=ipywidgets.Layout(width='100px')
128
+ )
129
+ """
130
+ row += 1
131
+ side_bar[row, :2] = ipywidgets.IntText(value=7.5,description='Z:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
132
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="", layout=ipywidgets.Layout(width='100px'))
133
+ row += 1
134
+ side_bar[row, :2] = ipywidgets.Dropdown(
135
+ options=['K1','L3', 'M5', 'M3', 'M1', 'N7', 'N5', 'N3', 'N1'],
136
+ value='K1',
137
+ description='Symmetry:',
138
+ disabled=False,
139
+ layout=ipywidgets.Layout(width='200px'))
140
+ row += 1
141
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Onset:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
142
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
143
+ row += 1
144
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Excl.Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
145
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
146
+ row += 1
147
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Excl.End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
148
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
149
+ row += 1
150
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Mutliplier:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
151
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="a.u.", layout=ipywidgets.Layout(width='100px'))
152
+ row += 1
153
+
154
+ side_bar[row, :3] = ipywidgets.Button(description='Quantification',
155
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
156
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
157
+
158
+ row += 1
159
+ side_bar[row,0] = ipywidgets.ToggleButton(
160
+ description='Probabiity',
161
+ disabled=False,
162
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
163
+ tooltip='Changes y-axis to probability of flux is given',
164
+ layout=ipywidgets.Layout(width='100px')
165
+ )
166
+ side_bar[row,1] = ipywidgets.ToggleButton(
167
+ description='Conv.LL',
168
+ disabled=False,
169
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
170
+ tooltip='Changes y-axis to probability of flux is given',
171
+ layout=ipywidgets.Layout(width='100px')
172
+ )
173
+ side_bar[row,2] = ipywidgets.ToggleButton(
174
+ description='Show Edges',
175
+ disabled=False,
176
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
177
+ tooltip='Changes y-axis to probability of flux is given',
178
+ layout=ipywidgets.Layout(width='100px')
179
+ )
180
+
181
+ row += 1
182
+ side_bar[row,0] = ipywidgets.ToggleButton(
183
+ description='Do All',
184
+ disabled=False,
185
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
186
+ tooltip='Fits all spectra of spectrum image',
187
+ layout=ipywidgets.Layout(width='100px')
188
+ )
189
+
190
+ side_bar[row,1] = ipywidgets.IntProgress(value=0, min=0, max=10, description=' ', bar_style='', # 'success', 'info', 'warning', 'danger' or ''
191
+ style={'bar_color': 'maroon'}, orientation='horizontal')
192
+ return side_bar
193
+
194
+
195
+
196
+ class CompositionWidget(object):
197
+ def __init__(self, datasets=None, key=None):
198
+
199
+ if not isinstance(datasets, dict):
200
+ raise TypeError('dataset or first item has to be a sidpy dataset')
201
+ self.datasets = datasets
202
+
203
+
204
+ self.model = []
205
+ self.sidebar = get_core_loss_sidebar()
206
+
207
+ self.set_dataset(key)
208
+
209
+ self.periodic_table = eels_dialog_utilities.PeriodicTableWidget(self.energy_scale)
210
+ self.elements_cancel_button = ipywidgets.Button(description='Cancel')
211
+ self.elements_select_button = ipywidgets.Button(description='Select')
212
+ self.elements_auto_button = ipywidgets.Button(description='Auto ID')
213
+
214
+ self.periodic_table_panel = ipywidgets.VBox([self.periodic_table.periodic_table,
215
+ ipywidgets.HBox([self.elements_cancel_button, self.elements_auto_button, self.elements_select_button])])
216
+
217
+
218
+ self.app_layout = ipywidgets.AppLayout(
219
+ left_sidebar=self.sidebar,
220
+ center=self.view.panel,
221
+ footer=None,#message_bar,
222
+ pane_heights=[0, 10, 0],
223
+ pane_widths=[4, 10, 0],
224
+ )
225
+ self.set_action()
226
+ IPython.display.display(self.app_layout)
227
+
228
+
229
+ def line_select_callback(self, x_min, x_max):
230
+ self.start_cursor.value = np.round(x_min,3)
231
+ self.end_cursor.value = np.round(x_max, 3)
232
+
233
+ self.start_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.start_cursor.value)
234
+ self.end_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.end_cursor.value)
235
+
236
+
237
+ def plot(self, scale=True):
238
+ self.view.change_y_scale = self.change_y_scale
239
+ self.view.y_scale = self.y_scale
240
+ self.energy_scale = self.dataset.energy_loss.values
241
+
242
+ if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
243
+ spectrum = self.view.get_spectrum()
244
+ else:
245
+ spectrum = self.dataset
246
+ if len(self.model) > 1:
247
+ additional_spectra = {'model': self.model,
248
+ 'difference': spectrum-self.model}
249
+ else:
250
+ additional_spectra = None
251
+ self.view.plot(scale=True, additional_spectra=additional_spectra )
252
+ self.change_y_scale = 1.
253
+
254
+ if self.sidebar[12, 2].value:
255
+ self.show_edges()
256
+ if self.sidebar[0, 0].value:
257
+ self.plot_regions()
258
+ self.view.figure.canvas.draw_idle()
259
+
260
+
261
+ def plot_regions(self):
262
+ axis = self.view.figure.gca()
263
+ y_min, y_max = axis.get_ylim()
264
+ height = y_max - y_min
265
+
266
+ rect = []
267
+ if 'fit_area' in self.edges:
268
+ color = 'blue'
269
+ alpha = 0.2
270
+ x_min = self.edges['fit_area']['fit_start']
271
+ width = self.edges['fit_area']['fit_end'] - x_min
272
+ rect.append(patches.Rectangle((x_min, y_min), width, height,
273
+ edgecolor=color, alpha=alpha, facecolor=color))
274
+ axis.add_patch(rect[0])
275
+ axis.text(x_min, y_max, 'fit region', verticalalignment='top')
276
+ color = 'red'
277
+ alpha = 0.5
278
+
279
+ for key in self.edges:
280
+ if key.isdigit():
281
+ x_min = self.edges[key]['start_exclude']
282
+ width = self.edges[key]['end_exclude']-x_min
283
+ rect.append(patches.Rectangle((x_min, y_min), width, height,
284
+ edgecolor=color, alpha=alpha, facecolor=color))
285
+ axis.add_patch(rect[-1])
286
+ axis.text(x_min, y_max, f"exclude\n edge {int(key)+1}", verticalalignment='top')
287
+
288
+ def show_edges(self):
289
+ axis = self.view.figure.gca()
290
+ x_min, x_max = axis.get_xlim()
291
+ y_min, y_max = axis.get_ylim()
292
+
293
+ for key, edge in self.edges.items():
294
+ i = 0
295
+ if key.isdigit():
296
+ element = edge['element']
297
+ for sym in edge['all_edges']:
298
+ x = edge['all_edges'][sym]['onset'] + edge['chemical_shift']
299
+ if x_min < x < x_max:
300
+ axis.text(x, y_max, '\n' * i + f"{element}-{sym}",
301
+ verticalalignment='top', color='black')
302
+ axis.axvline(x, ymin=0, ymax=1, color='gray')
303
+ i += 1
304
+
305
+
306
+
307
+
308
+ def set_dataset(self, set_key):
309
+ spectrum_list = []
310
+ self.spectrum_keys_list = []
311
+ reference_list =[('None', -1)]
312
+
313
+ for index, key in enumerate(self.datasets.keys()):
314
+ if '_rel' not in key:
315
+ if 'Reference' not in key :
316
+ if 'SPECTR' in self.datasets[key].data_type.name:
317
+ spectrum_list.append((f'{key}: {self.datasets[key].title}', index))
318
+ self.spectrum_keys_list.append(key)
319
+ reference_list.append((f'{key}: {self.datasets[key].title}', index))
320
+
321
+ if set_key in self.spectrum_keys_list:
322
+ self.key = set_key
323
+ else:
324
+ self.key = self.spectrum_keys_list[-1]
325
+ self.dataset = self.datasets[self.key]
326
+
327
+ self.spec_dim = self.dataset.get_spectral_dims(return_axis=True)[0]
328
+
329
+ self.energy_scale = self.spec_dim.values
330
+ self.dd = (self.energy_scale[0], self.energy_scale[1])
331
+
332
+ self.dataset.metadata['experiment']['offset'] = self.energy_scale[0]
333
+ self.dataset.metadata['experiment']['dispersion'] = self.energy_scale[1] - self.energy_scale[0]
334
+ if 'edges' not in self.dataset.metadata or self.dataset.metadata['edges'] == {}:
335
+ self.dataset.metadata['edges'] = {'0': {}, 'model': {}, 'use_low_loss': False}
336
+
337
+ self.edges = self.dataset.metadata['edges']
338
+ if '0' not in self.edges:
339
+ self.edges['0'] = {}
340
+
341
+ if 'fit_area' not in self.edges:
342
+ self.edges['fit_area'] = {}
343
+ if 'fit_start' not in self.edges['fit_area']:
344
+ self.sidebar[1,0].value = np.round(self.energy_scale[50], 3)
345
+ self.edges['fit_area']['fit_start'] = self.sidebar[1,0].value
346
+ else:
347
+ self.sidebar[1,0].value = np.round(self.edges['fit_area']['fit_start'],3)
348
+ if 'fit_end' not in self.edges['fit_area']:
349
+ self.sidebar[2,0].value = np.round(self.energy_scale[-2], 3)
350
+ self.edges['fit_area']['fit_end'] = self.sidebar[2,0].value
351
+ else:
352
+ self.sidebar[2,0].value = np.round(self.edges['fit_area']['fit_end'],3)
353
+
354
+ if self.dataset.data_type.name == 'SPECTRAL_IMAGE':
355
+ if 'SI_bin_x' not in self.dataset.metadata['experiment']:
356
+ self.dataset.metadata['experiment']['SI_bin_x'] = 1
357
+ self.dataset.metadata['experiment']['SI_bin_y'] = 1
358
+
359
+ bin_x = self.dataset.metadata['experiment']['SI_bin_x']
360
+ bin_y = self.dataset.metadata['experiment']['SI_bin_y']
361
+ # self.dataset.view.set_bin([bin_x, bin_y])
362
+ if self.dataset.data_type.name =='SPECTRAL_IMAGE':
363
+ self.view = eels_dialog_utilities.SIPlot(self.dataset)
364
+ else:
365
+ self.view = eels_dialog_utilities.SpectrumPlot(self.dataset)
366
+ self.y_scale = 1.0
367
+ self.change_y_scale = 1.0
368
+
369
+ self.update()
370
+
371
+ def update_element(self, z=0, index=-1):
372
+ # We check whether this element is already in the
373
+ if z == 0:
374
+ z = self.sidebar[5,0].value
375
+
376
+ zz = eels.get_z(z)
377
+ for key, edge in self.edges.items():
378
+ if key.isdigit():
379
+ if 'z' in edge:
380
+ if zz == edge['z']:
381
+ return False
382
+
383
+ major_edge = ''
384
+ minor_edge = ''
385
+ all_edges = {}
386
+ x_section = eels.get_x_sections(zz)
387
+ edge_start = 10 # int(15./ft.get_slope(self.energy_scale)+0.5)
388
+ for key in x_section:
389
+ if len(key) == 2 and key[0] in ['K', 'L', 'M', 'N', 'O'] and key[1].isdigit():
390
+ if self.energy_scale[edge_start] < x_section[key]['onset'] < self.energy_scale[-edge_start]:
391
+ if key in ['K1', 'L3', 'M5']:
392
+ major_edge = key
393
+ elif key in self.sidebar[6,0].options:
394
+ if minor_edge == '':
395
+ minor_edge = key
396
+ if int(key[-1]) % 2 > 0:
397
+ if int(minor_edge[-1]) % 2 == 0 or key[-1] > minor_edge[-1]:
398
+ minor_edge = key
399
+
400
+ all_edges[key] = {'onset': x_section[key]['onset']}
401
+
402
+ if major_edge != '':
403
+ key = major_edge
404
+ elif minor_edge != '':
405
+ key = minor_edge
406
+ else:
407
+ print(f'Could not find no edge of {zz} in spectrum')
408
+ return False
409
+ if index == -1:
410
+ index = self.sidebar[4, 0].value
411
+ # self.ui.dialog.setWindowTitle(f'{index}, {zz}')
412
+
413
+ if str(index) not in self.edges:
414
+ self.edges[str(index)] = {}
415
+
416
+ start_exclude = x_section[key]['onset'] - x_section[key]['excl before']
417
+ end_exclude = x_section[key]['onset'] + x_section[key]['excl after']
418
+
419
+ self.edges[str(index)] = {'z': zz, 'symmetry': key, 'element': eels.elements[zz],
420
+ 'onset': x_section[key]['onset'], 'end_exclude': end_exclude,
421
+ 'start_exclude': start_exclude}
422
+ self.edges[str(index)]['all_edges'] = all_edges
423
+ self.edges[str(index)]['chemical_shift'] = 0.0
424
+ self.edges[str(index)]['areal_density'] = 0.0
425
+ self.edges[str(index)]['original_onset'] = self.edges[str(index)]['onset']
426
+ return True
427
+
428
+ def sort_elements(self):
429
+ onsets = []
430
+ for index, edge in self.edges.items():
431
+ if index.isdigit():
432
+ onsets.append(float(edge['onset']))
433
+
434
+ arg_sorted = np.argsort(onsets)
435
+ edges = self.edges.copy()
436
+ for index, i_sorted in enumerate(arg_sorted):
437
+ self.edges[str(index)] = edges[str(i_sorted)].copy()
438
+
439
+ index = 0
440
+ edge = self.edges['0']
441
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
442
+
443
+ while str(index + 1) in self.edges:
444
+ next_edge = self.edges[str(index + 1)]
445
+ if edge['end_exclude'] > next_edge['start_exclude'] - 5 * dispersion:
446
+ edge['end_exclude'] = next_edge['start_exclude'] - 5 * dispersion
447
+ edge = next_edge
448
+ index += 1
449
+
450
+ if edge['end_exclude'] > self.energy_scale[-3]:
451
+ edge['end_exclude'] = self.energy_scale[-3]
452
+
453
+ def set_elements(self, value=0):
454
+ selected_elements = self.periodic_table.get_output()
455
+ edges = self.edges.copy()
456
+ to_delete = []
457
+ old_elements = []
458
+ if len(selected_elements) > 0:
459
+ for key in self.edges:
460
+ if key.isdigit():
461
+ to_delete.append(key)
462
+ old_elements.append(self.edges[key]['element'])
463
+
464
+ for key in to_delete:
465
+ edges[key] = self.edges[key]
466
+ del self.edges[key]
467
+
468
+ for index, elem in enumerate(selected_elements):
469
+ if elem in old_elements:
470
+ self.edges[str(index)] = edges[str(old_elements.index(elem))]
471
+ else:
472
+ self.update_element(elem, index=index)
473
+ self.sort_elements()
474
+ self.update()
475
+ self.set_figure_pane()
476
+
477
+ def set_element(self, elem):
478
+ self.update_element(self.sidebar[5, 0].value)
479
+ # self.sort_elements()
480
+ self.update()
481
+
482
+ def cursor2energy_scale(self, value):
483
+ dispersion = (self.end_cursor.value - self.start_cursor.value) / (self.end_channel - self.start_channel)
484
+ self.datasets[self.key].energy_loss *= (self.sidebar[3, 0].value/dispersion)
485
+ self.sidebar[3, 0].value = dispersion
486
+ offset = self.start_cursor.value - self.start_channel * dispersion
487
+ self.datasets[self.key].energy_loss += (self.sidebar[2, 0].value-self.datasets[self.key].energy_loss[0])
488
+ self.sidebar[2, 0].value = offset
489
+ self.plot()
490
+
491
+ def set_fit_area(self, value):
492
+ if self.sidebar[1,0].value > self.sidebar[2,0].value:
493
+ self.sidebar[1,0].value = self.sidebar[2,0].value -1
494
+ if self.sidebar[1,0].value < self.energy_scale[0]:
495
+ self.sidebar[1,0].value = self.energy_scale[0]
496
+ if self.sidebar[2,0].value > self.energy_scale[-1]:
497
+ self.sidebar[2,0].value = self.energy_scale[-1]
498
+ self.edges['fit_area']['fit_start'] = self.sidebar[1,0].value
499
+ self.edges['fit_area']['fit_end'] = self.sidebar[2,0].value
500
+
501
+ self.plot()
502
+
503
+ def set_y_scale(self, value):
504
+ self.change_y_scale = 1/self.y_scale
505
+ self.y_scale = 1.0
506
+ if self.dataset.metadata['experiment']['flux_ppm'] > 0:
507
+ if self.sidebar[12, 0].value:
508
+ dispersion = self.energy_scale[1] - self.energy_scale[0]
509
+ self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm'] * dispersion
510
+
511
+ self.change_y_scale *= self.y_scale
512
+ self.update()
513
+ self.plot()
514
+
515
+ def auto_id(self, value=0):
516
+ found_edges = eels.auto_id_edges(self.dataset)
517
+ if len(found_edges) > 0:
518
+ self.periodic_table.elements_selected = found_edges
519
+ self.periodic_table.update()
520
+
521
+ def find_elements(self, value=0):
522
+
523
+ if '0' not in self.edges:
524
+ self.edges['0'] = {}
525
+ # found_edges = eels.auto_id_edges(self.dataset)
526
+ found_edges = {}
527
+
528
+ selected_elements = []
529
+ elements = self.edges.copy()
530
+
531
+ for key in self.edges:
532
+ if key.isdigit():
533
+ if 'element' in self.edges[key]:
534
+ selected_elements.append(self.edges[key]['element'])
535
+ self.periodic_table.elements_selected = selected_elements
536
+ self.periodic_table.update()
537
+ self.app_layout.center = self.periodic_table_panel # self.periodic_table.periodic_table
538
+
539
+ def set_figure_pane(self, value=0):
540
+
541
+ self.app_layout.center = self.view.panel
542
+
543
+ def update(self, index=0):
544
+
545
+ index = self.sidebar[4,0].value # which edge
546
+ if index < 0:
547
+ options = list(self.sidebar[4,0].options)
548
+ options.insert(-1, (f'Edge {len(self.sidebar[4,0].options)}', len(self.sidebar[4,0].options)-1))
549
+ self.sidebar[4,0].options= options
550
+ self.sidebar[4,0].value = len(self.sidebar[4,0].options)-2
551
+ if str(index) not in self.edges:
552
+ self.edges[str(index)] = {'z': 0, 'element': 'x', 'symmetry': 'K1', 'onset': 0, 'start_exclude': 0, 'end_exclude':0,
553
+ 'areal_density': 0, 'chemical_shift':0}
554
+ if 'z' not in self.edges[str(index)]:
555
+ self.edges[str(index)] = {'z': 0, 'element': 'x', 'symmetry': 'K1', 'onset': 0, 'start_exclude': 0, 'end_exclude':0,
556
+ 'areal_density': 0, 'chemical_shift':0}
557
+ edge = self.edges[str(index)]
558
+
559
+ self.sidebar[5,0].value = edge['z']
560
+ self.sidebar[5,2].value = edge['element']
561
+ self.sidebar[6,0].value = edge['symmetry']
562
+ self.sidebar[7,0].value = edge['onset']
563
+ self.sidebar[8,0].value = edge['start_exclude']
564
+ self.sidebar[9,0].value = edge['end_exclude']
565
+ if self.y_scale == 1.0:
566
+ self.sidebar[10, 0].value = edge['areal_density']
567
+ self.sidebar[10, 2].value = 'a.u.'
568
+ else:
569
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
570
+ self.sidebar[10, 0].value = np.round(edge['areal_density']/self.dataset.metadata['experiment']['flux_ppm']*1e-6, 2)
571
+ self.sidebar[10, 2].value = 'atoms/nm²'
572
+
573
+
574
+ def do_fit(self, value=0):
575
+ if 'experiment' in self.dataset.metadata:
576
+ exp = self.dataset.metadata['experiment']
577
+ if 'convergence_angle' not in exp:
578
+ raise ValueError('need a convergence_angle in experiment of metadata dictionary ')
579
+ alpha = exp['convergence_angle']
580
+ beta = exp['collection_angle']
581
+ beam_kv = exp['acceleration_voltage']
582
+
583
+ else:
584
+ raise ValueError('need a experiment parameter in metadata dictionary')
585
+
586
+ eff_beta = eels.effective_collection_angle(self.energy_scale, alpha, beta, beam_kv)
587
+
588
+ self.low_loss = None
589
+ if self.sidebar[12, 1].value:
590
+ for key in self.datasets.keys():
591
+ if key != self.key:
592
+ if isinstance(self.datasets[key], sidpy.Dataset):
593
+ if self.datasets[key].data_type.name == 'SPECTRUM':
594
+ if self.datasets[key].energy_loss[0] < 0:
595
+ self.low_loss = self.datasets[key]/self.datasets[key].sum()
596
+
597
+ edges = eels.make_cross_sections(self.edges, np.array(self.energy_scale), beam_kv, eff_beta, self.low_loss)
598
+
599
+ if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
600
+ spectrum = self.view.get_spectrum()
601
+ else:
602
+ spectrum = self.dataset
603
+ self.edges = eels.fit_edges2(spectrum, self.energy_scale, edges)
604
+ areal_density = []
605
+ elements = []
606
+ for key in edges:
607
+ if key.isdigit(): # only edges have numbers in that dictionary
608
+ elements.append(edges[key]['element'])
609
+ areal_density.append(edges[key]['areal_density'])
610
+ areal_density = np.array(areal_density)
611
+ out_string = '\nRelative composition: \n'
612
+ for i, element in enumerate(elements):
613
+ out_string += f'{element}: {areal_density[i] / areal_density.sum() * 100:.1f}% '
614
+
615
+ self.model = self.edges['model']['spectrum']
616
+ self.update()
617
+ self.plot()
618
+
619
+ def do_all_button_click(self, value=0):
620
+ if self.sidebar[13,0].value==False:
621
+ return
622
+
623
+ if self.dataset.data_type.name != 'SPECTRAL_IMAGE':
624
+ self.do_fit()
625
+ return
626
+
627
+ if 'experiment' in self.dataset.metadata:
628
+ exp = self.dataset.metadata['experiment']
629
+ if 'convergence_angle' not in exp:
630
+ raise ValueError('need a convergence_angle in experiment of metadata dictionary ')
631
+ alpha = exp['convergence_angle']
632
+ beta = exp['collection_angle']
633
+ beam_kv = exp['acceleration_voltage']
634
+ else:
635
+ raise ValueError('need a experiment parameter in metadata dictionary')
636
+
637
+ eff_beta = eels.effective_collection_angle(self.energy_scale, alpha, beta, beam_kv)
638
+ eff_beta = beta
639
+ self.low_loss = None
640
+ if self.sidebar[12, 1].value:
641
+ for key in self.datasets.keys():
642
+ if key != self.key:
643
+ if isinstance(self.datasets[key], sidpy.Dataset):
644
+ if 'SPECTR' in self.datasets[key].data_type.name:
645
+ if self.datasets[key].energy_loss[0] < 0:
646
+ self.low_loss = self.datasets[key]/self.datasets[key].sum()
647
+
648
+ edges = eels.make_cross_sections(self.edges, np.array(self.energy_scale), beam_kv, eff_beta, self.low_loss)
649
+
650
+ view = self.view
651
+ bin_x = view.bin_x
652
+ bin_y = view.bin_y
653
+
654
+ start_x = view.x
655
+ start_y = view.y
656
+
657
+ number_of_edges = 0
658
+ for key in self.edges:
659
+ if key.isdigit():
660
+ number_of_edges += 1
661
+
662
+ results = np.zeros([int(self.dataset.shape[0]/bin_x), int(self.dataset.shape[1]/bin_y), number_of_edges])
663
+ total_spec = int(self.dataset.shape[0]/bin_x)*int(self.dataset.shape[1]/bin_y)
664
+ self.sidebar[13,1].max = total_spec
665
+ #self.ui.progress.setMaximum(total_spec)
666
+ #self.ui.progress.setValue(0)
667
+ ind = 0
668
+ for x in range(int(self.dataset.shape[0]/bin_x)):
669
+ for y in range(int(self.dataset.shape[1]/bin_y)):
670
+ ind += 1
671
+ self.sidebar[13,1].value = ind
672
+ view.x = x*bin_x
673
+ view.y = y*bin_y
674
+ spectrum = view.get_spectrum()
675
+ with warnings.catch_warnings():
676
+ warnings.simplefilter("ignore")
677
+ edges = eels.fit_edges2(spectrum, self.energy_scale, edges)
678
+ for key, edge in edges.items():
679
+ if key.isdigit():
680
+ # element.append(edge['element'])
681
+ results[x, y, int(key)] = edge['areal_density']
682
+ edges['spectrum_image_quantification'] = results
683
+ self.sidebar[13,1].value = total_spec
684
+ view.x = start_x
685
+ view.y = start_y
686
+ self.sidebar[13,0].value = False
687
+
688
+
689
+ def modify_onset(self, value=-1):
690
+ edge_index = self.sidebar[4, 0].value
691
+ edge = self.edges[str(edge_index)]
692
+ edge['onset'] = self.sidebar[7,0].value
693
+ if 'original_onset' not in edge:
694
+ edge['original_onset'] = edge['onset']
695
+ edge['chemical_shift'] = edge['onset'] - edge['original_onset']
696
+ self.update()
697
+
698
+
699
+ def modify_start_exclude(self, value=-1):
700
+ edge_index = self.sidebar[4, 0].value
701
+ edge = self.edges[str(edge_index)]
702
+ edge['start_exclude'] = self.sidebar[8,0].value
703
+ self.plot()
704
+
705
+ def modify_end_exclude(self, value=-1):
706
+ edge_index = self.sidebar[4, 0].value
707
+ edge = self.edges[str(edge_index)]
708
+ edge['end_exclude'] = self.sidebar[9,0].value
709
+ self.plot()
710
+
711
+ def modify_areal_density(self, value=-1):
712
+ edge_index = self.sidebar[4, 0].value
713
+ edge = self.edges[str(edge_index)]
714
+
715
+ edge['areal_density'] = self.sidebar[10, 0].value
716
+ if self.y_scale != 1.0:
717
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
718
+ edge['areal_density'] = self.sidebar[10, 0].value *self.dataset.metadata['experiment']['flux_ppm']/1e-6
719
+
720
+ self.model = self.edges['model']['background']
721
+ for key in self.edges:
722
+ if key.isdigit():
723
+ if 'data' in self.edges[key]:
724
+
725
+ self.model = self.model + self.edges[key]['areal_density'] * self.edges[key]['data']
726
+ self.plot()
727
+
728
+ def set_action(self):
729
+ self.sidebar[1, 0].observe(self.set_fit_area, names='value')
730
+ self.sidebar[2, 0].observe(self.set_fit_area, names='value')
731
+
732
+ self.sidebar[3, 0].on_click(self.find_elements)
733
+ self.sidebar[4, 0].observe(self.update, names='value')
734
+ self.sidebar[5, 0].observe(self.set_element, names='value')
735
+
736
+ self.sidebar[7, 0].observe(self.modify_onset, names='value')
737
+ self.sidebar[8, 0].observe(self.modify_start_exclude, names='value')
738
+ self.sidebar[9, 0].observe(self.modify_end_exclude, names='value')
739
+ self.sidebar[10, 0].observe(self.modify_areal_density, names='value')
740
+
741
+ self.sidebar[11, 0].on_click(self.do_fit)
742
+ self.sidebar[12, 2].observe(self.plot, names='value')
743
+ self.sidebar[0, 0].observe(self.plot, names='value')
744
+ self.sidebar[12,0].observe(self.set_y_scale, names='value')
745
+ self.sidebar[13,0].observe(self.do_all_button_click, names='value')
746
+
747
+ self.elements_cancel_button.on_click(self.set_figure_pane)
748
+ self.elements_auto_button.on_click(self.auto_id)
749
+ self.elements_select_button.on_click(self.set_elements)