pyTEMlib 0.2023.4.0__py2.py3-none-any.whl → 0.2023.8.0__py2.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.

pyTEMlib/eels_dialog.py CHANGED
@@ -8,23 +8,24 @@ try:
8
8
  from PyQt5 import QtCore, QtWidgets
9
9
  except:
10
10
  Qt_available = False
11
- print('Qt dialogs are not available')
12
- from pyTEMlib import eels_dlg
11
+ # print('Qt dialogs are not available')
13
12
 
14
- import numpy as np
15
13
 
16
- from pyTEMlib import eels_tools as eels
14
+ import numpy as np
17
15
 
16
+ import ipywidgets
17
+ import IPython.display
18
+ import matplotlib
18
19
  import matplotlib.pylab as plt
19
20
  import matplotlib.patches as patches
20
21
 
21
22
  from pyTEMlib import file_tools as ft
23
+ from pyTEMlib import eels_tools as eels
22
24
 
23
25
  import sidpy
24
26
 
25
- _version = 000
26
-
27
27
  if Qt_available:
28
+ from pyTEMlib import eels_dlg
28
29
  from pyTEMlib import eels_dialog_utilities
29
30
 
30
31
  class EELSDialog(QtWidgets.QDialog):
@@ -32,38 +33,48 @@ if Qt_available:
32
33
  EELS Input Dialog for Chemical Analysis
33
34
  """
34
35
 
35
- def __init__(self, dataset=None):
36
+ def __init__(self, datasets=None):
36
37
  super().__init__(None, QtCore.Qt.WindowStaysOnTopHint)
37
38
  # Create an instance of the GUI
38
- if dataset is None:
39
+ if datasets is None:
39
40
  # make a dummy dataset
40
- dataset = ft.make_dummy_dataset(sidpy.DataType.SPECTRUM)
41
- if not isinstance(dataset, sidpy.Dataset):
42
- raise TypeError('dataset has to be a sidpy dataset')
43
- self.spec_dim = ft.get_dimensions_by_type('spectral', dataset)
41
+ datasets = {'Channel_000':ft.make_dummy_dataset(sidpy.DataType.SPECTRUM)}
42
+ elif isinstance(datasets, sidpy.Dataset):
43
+ datasets = {'Channel_000': datasets}
44
+ elif isinstance(datasets, dict):
45
+ pass
46
+ else:
47
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
48
+ self.datasets = datasets
49
+ self.dataset = datasets[list(datasets)[0]]
50
+
51
+ if not isinstance(self.dataset, sidpy.Dataset):
52
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
53
+
54
+ self.spec_dim = ft.get_dimensions_by_type('spectral', self.dataset)
44
55
  if len(self.spec_dim) != 1:
45
56
  raise TypeError('We need exactly one SPECTRAL dimension')
46
57
  self.spec_dim = self.spec_dim[0]
47
-
58
+
48
59
  self.ui = eels_dlg.UiDialog(self)
49
60
  # Run the .setup_ui() method to show the GUI
50
61
  # self.ui.setup_ui(self)
51
62
 
52
63
  self.set_action()
53
64
 
54
- self.dataset = dataset
55
65
  self.energy_scale = np.array([])
56
66
  self.model = np.array([])
57
67
  self.y_scale = 1.0
58
68
  self.change_y_scale = 1.0
69
+ self.spectrum_ll = None
70
+ self.low_loss_key = None
59
71
 
60
72
  self.edges = {}
61
73
 
62
-
63
74
  self.show_regions = False
64
75
  self.show()
65
76
 
66
- self.set_dataset(dataset)
77
+ self.set_dataset(self.dataset)
67
78
  initial_elements = []
68
79
 
69
80
  for key in self.edges:
@@ -86,8 +97,9 @@ if Qt_available:
86
97
  self.updY = 0
87
98
  self.figure.canvas.mpl_connect('button_press_event', self.plot)
88
99
 
100
+ self.ui.do_fit_button.setFocus()
89
101
  self.plot()
90
- self.ui.edit4.setFocus()
102
+ self.ui.do_fit_button.setFocus()
91
103
 
92
104
  def set_dataset(self, dataset):
93
105
 
@@ -145,7 +157,7 @@ if Qt_available:
145
157
  else:
146
158
  dispersion = self.energy_scale[1]-self.energy_scale[0]
147
159
  self.ui.edit9.setText(f"{edge['areal_density']*self.y_scale*1e-6/dispersion:.2f}")
148
- self.ui.unit9.setText(r'atoms/nm$^2$')
160
+ self.ui.unit9.setText('atoms/nm²')
149
161
  else:
150
162
  self.ui.list3.setCurrentIndex(0)
151
163
  self.ui.edit4.setText(str(0))
@@ -192,6 +204,7 @@ if Qt_available:
192
204
  return False
193
205
 
194
206
  index = self.ui.list3.currentIndex()
207
+ # self.ui.dialog.setWindowTitle(f'{index}, {zz}')
195
208
 
196
209
  if str(index) not in self.edges:
197
210
  self.edges[str(index)] = {}
@@ -211,7 +224,9 @@ if Qt_available:
211
224
  def on_enter(self):
212
225
  sender = self.sender()
213
226
  edge_list = self.ui.list3
227
+ # self.ui.dialog.setWindowTitle(f"{sender.objectName()}")
214
228
 
229
+
215
230
  if sender.objectName() == 'fit_start_edit':
216
231
  value = float(str(sender.displayText()).strip())
217
232
  if value < self.energy_scale[0]:
@@ -230,9 +245,10 @@ if Qt_available:
230
245
  sender.setText(str(self.edges['fit_area']['fit_end']))
231
246
  elif sender.objectName() == 'element_edit':
232
247
  if str(sender.displayText()).strip() == '0':
233
- sender.setText('PT')
234
- #self.pt_dialog.energy_scale = self.energy_scale
235
- #self.pt_dialog.show()
248
+ # sender.setText('PT')
249
+ self.pt_dialog.energy_scale = self.energy_scale
250
+ self.pt_dialog.show()
251
+ pass
236
252
  else:
237
253
  self.update_element(str(sender.displayText()).strip())
238
254
  self.update()
@@ -242,6 +258,9 @@ if Qt_available:
242
258
  elif sender.objectName() == 'multiplier_edit':
243
259
  index = edge_list.currentIndex()
244
260
  self.edges[str(index)]['areal_density'] = float(self.ui.edit9.displayText())
261
+ if self.y_scale != 1.0:
262
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
263
+ self.edges[str(index)]['areal_density'] /= self.y_scale * 1e-6 *dispersion
245
264
  if 'background' not in self.edges['model']:
246
265
  print(' no background')
247
266
  return
@@ -255,6 +274,8 @@ if Qt_available:
255
274
  if self.show_regions:
256
275
  self.plot()
257
276
 
277
+
278
+
258
279
  def sort_elements(self):
259
280
  onsets = []
260
281
  for index, edge in self.edges.items():
@@ -281,16 +302,13 @@ if Qt_available:
281
302
  edge['end_exclude'] = self.energy_scale[-3]
282
303
 
283
304
  def set_elements(self, selected_elements):
284
-
285
305
  edge_list = self.ui.list3
286
- index = 0 # edge_list.currentIndex()
287
-
288
- for elem in selected_elements:
289
- if self.update_element(elem):
290
- index = edge_list.currentIndex()
291
- edge_list.setCurrentIndex(index + 1)
306
+
307
+ for index, elem in enumerate(selected_elements):
308
+ edge_list.setCurrentIndex(index)
309
+ self.update_element(elem)
310
+
292
311
  self.sort_elements()
293
- edge_list.setCurrentIndex(index)
294
312
  self.update()
295
313
 
296
314
  def plot(self, event=None):
@@ -450,6 +468,7 @@ if Qt_available:
450
468
 
451
469
  def on_list_enter(self):
452
470
  sender = self.sender()
471
+ # self.ui.dialog.setWindowTitle(f"on list eneter {sender.objectName()}")
453
472
 
454
473
  if sender.objectName() == 'edge_list':
455
474
  index = self.ui.list3.currentIndex()
@@ -488,6 +507,8 @@ if Qt_available:
488
507
 
489
508
  def on_check(self):
490
509
  sender = self.sender()
510
+ # self.ui.dialog.setWindowTitle(f"on_check {sender.objectName()}")
511
+
491
512
 
492
513
  if sender.objectName() == 'edge_check':
493
514
  self.show_regions = sender.isChecked()
@@ -497,25 +518,62 @@ if Qt_available:
497
518
  self.low_loss()
498
519
  elif sender.objectName() == 'probability':
499
520
  dispersion = self.energy_scale[1]-self.energy_scale[0]
521
+ old_y_scale = self.y_scale *1.
500
522
  if sender.isChecked():
501
- self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm']*dispersion
502
- self.change_y_scale = 1/self.dataset.metadata['experiment']['flux_ppm']*dispersion
523
+ flux_key = None
524
+ spectrum_key = None
525
+
526
+ for key in self.datasets.keys():
527
+ if 'Reference' in key:
528
+ if self.datasets[key].data_type.name == 'IMAGE': # Prefer Ronchigrams
529
+ flux_key = key
530
+ self.dataset.metadata['experiment']['flux_reference_key'] = flux_key
531
+ elif self.datasets[key].data_type.name == 'SPECTRUM':
532
+ spectrum_key = key
533
+ self.dataset.metadata['experiment']['low_loss_key'] = spectrum_key
534
+ if flux_key is None:
535
+ flux_key = spectrum_key
536
+
537
+ # self.ui.dialog.setWindowTitle(f"2nd {self.dataset.metadata['experiment']['flux_ppm']:.2f}")
538
+ if self.dataset.metadata['experiment']['flux_ppm'] > 0:
539
+ # self.ui.dialog.setWindowTitle(f"3rD {self.dataset.metadata['experiment']['flux_ppm']:.2f}")
540
+ self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm']*dispersion
541
+ elif flux_key is not None:
542
+ self.dataset.metadata['experiment']['flux_ppm'] = (np.array(self.datasets[flux_key])/1e6).sum()
543
+ self.dataset.metadata['experiment']['flux_ppm'] /= self.datasets[flux_key].metadata['experiment']['exposure_time']
544
+ self.dataset.metadata['experiment']['flux_ppm'] *= self.dataset.metadata['experiment']['exposure_time']
545
+ self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm']*dispersion
546
+ else:
547
+ self.y_scale = 1.0
503
548
  else:
504
549
  self.y_scale = 1.0
505
- self.change_y_scale = self.dataset.metadata['experiment']['flux_ppm']/dispersion
550
+
551
+ self.change_y_scale = self.y_scale/old_y_scale
506
552
  self.update()
507
553
  self.plot()
508
554
 
509
555
  def low_loss(self):
510
556
  self.edges['use_low_loss'] = self.ui.check10.isChecked()
511
-
512
- if 'low_loss' not in self.edges:
513
- self.edges['low_loss'] = {}
514
- if 'spectrum' not in self.edges['low_loss']:
515
- spectrum_ll = ft.open_file(write_hdf_file=False)
516
-
517
- self.edges['low_loss']['spectrum'] = np.array(spectrum_ll)
518
- self.spectrum_ll = self.edges['low_loss']['spectrum']
557
+ if self.low_loss_key is None:
558
+ for key in self.datasets.keys():
559
+ if 'Reference' in key:
560
+ if self.datasets[key].data_type.name == 'SPECTRUM':
561
+ self.low_loss_key = key
562
+ self.dataset.metadata['experiment']['low_loss_key'] = self.low_loss_key
563
+
564
+ if self.low_loss_key is None:
565
+ self.low_loss_key = ft.add_dataset_from_file(self.datasets, key_name='Reference')
566
+ self.spectrum_ll = self.datasets[self.low_loss_key]
567
+ if self.spectrum_ll.data_type.name != 'SPECTRUM':
568
+ self.spectrum_ll = None
569
+ self.low_loss_key = None
570
+
571
+ if self.low_loss_key is not None:
572
+ self.spectrum_ll = self.datasets[self.low_loss_key]
573
+ if 'number_of_frames' in self.spectrum_ll.metadata['experiment']:
574
+ self.spectrum_ll.metadata['experiment']['exposure_time'] = \
575
+ self.spectrum_ll.metadata['experiment']['single_exposure_time'] * \
576
+ self.spectrum_ll.metadata['experiment']['number_of_frames']
519
577
 
520
578
  def do_all_button_click(self):
521
579
 
@@ -536,7 +594,7 @@ if Qt_available:
536
594
  self.energy_scale = self.dataset._axes[self.spec_dim].values
537
595
  eff_beta = eels.effective_collection_angle(self.energy_scale, alpha, beta, beam_kv)
538
596
  if self.edges['use_low_loss']:
539
- low_loss = self.spectrum_ll/self.spectrum_ll.sum()
597
+ low_loss = np.array(self.spectrum_ll)/self.spectrum_ll.sum()
540
598
  else:
541
599
  low_loss = None
542
600
 
@@ -620,6 +678,9 @@ if Qt_available:
620
678
  self.plot()
621
679
 
622
680
  def do_auto_id_button_click(self):
681
+ # self.ui.dialog.setWindowTitle(f"auto id ")
682
+ self.ui.do_fit_button.setFocus()
683
+
623
684
  if '0' not in self.edges:
624
685
  self.edges['0'] ={}
625
686
  found_edges = eels.auto_id_edges(self.dataset)
@@ -746,3 +807,557 @@ if Qt_available:
746
807
  else:
747
808
  legline.set_alpha(0.2)
748
809
  self.fig.canvas.draw()
810
+
811
+ def get_sidebar():
812
+ side_bar = ipywidgets.GridspecLayout(13, 3,width='auto', grid_gap="0px")
813
+
814
+
815
+ row = 0
816
+ side_bar[row, :3] = ipywidgets.ToggleButton(description='Fit Area',
817
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
818
+ tooltip='Shows fit regions and regions excluded from fit',
819
+ button_style='info') #ipywidgets.ButtonStyle(button_color='lightblue'))
820
+ row += 1
821
+ side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Fit Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
822
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
823
+ row += 1
824
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Fit End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
825
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
826
+
827
+ row += 1
828
+
829
+ side_bar[row, :3] = ipywidgets.Button(description='Elements',
830
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
831
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
832
+ row += 1
833
+ side_bar[row, :2] = ipywidgets.Dropdown(
834
+ options=[('Edge 1', 0), ('Edge 2', 1), ('Edge 3', 2), ('Edge 4', 3),('Add Edge', -1)],
835
+ value=0,
836
+ description='Edges:',
837
+ disabled=False,
838
+ layout=ipywidgets.Layout(width='200px'))
839
+ """side_bar[row,2] = ipywidgets.ToggleButton(
840
+ description='Regions',
841
+ disabled=False,
842
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
843
+ tooltip='Shows fit regions and regions excluded from fit',
844
+ layout=ipywidgets.Layout(width='100px')
845
+ )
846
+ """
847
+ row += 1
848
+ side_bar[row, :2] = ipywidgets.IntText(value=7.5,description='Z:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
849
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="", layout=ipywidgets.Layout(width='100px'))
850
+ row += 1
851
+ side_bar[row, :2] = ipywidgets.Dropdown(
852
+ options=['K1','L3', 'M5', 'M3', 'M1', 'N7', 'N5', 'N3', 'N1'],
853
+ value='K1',
854
+ description='Symmetry:',
855
+ disabled=False,
856
+ layout=ipywidgets.Layout(width='200px'))
857
+ row += 1
858
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Onset:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
859
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
860
+ row += 1
861
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Excl.Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
862
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
863
+ row += 1
864
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Excl.End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
865
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
866
+ row += 1
867
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Mutliplier:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
868
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="a.u.", layout=ipywidgets.Layout(width='100px'))
869
+ row += 1
870
+
871
+ side_bar[row, :3] = ipywidgets.Button(description='Quantification',
872
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
873
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
874
+
875
+ row += 1
876
+ side_bar[row,0] = ipywidgets.ToggleButton(
877
+ description='Probabiity',
878
+ disabled=False,
879
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
880
+ tooltip='Changes y-axis to probability of flux is given',
881
+ layout=ipywidgets.Layout(width='100px')
882
+ )
883
+ side_bar[row,1] = ipywidgets.ToggleButton(
884
+ description='Conv.LL',
885
+ disabled=False,
886
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
887
+ tooltip='Changes y-axis to probability of flux is given',
888
+ layout=ipywidgets.Layout(width='100px')
889
+ )
890
+ side_bar[row,2] = ipywidgets.ToggleButton(
891
+ description='Show Edges',
892
+ disabled=False,
893
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
894
+ tooltip='Changes y-axis to probability of flux is given',
895
+ layout=ipywidgets.Layout(width='100px')
896
+ )
897
+ return side_bar
898
+
899
+
900
+ class CompositionWidget(object):
901
+ def __init__(self, datasets=None):
902
+ self.datasets = datasets
903
+ if not isinstance(datasets, dict):
904
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
905
+
906
+ self.sidebar = get_sidebar()
907
+ self.dataset = datasets[list(datasets)[0]]
908
+ if not isinstance(self.dataset, sidpy.Dataset):
909
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
910
+ self.spec_dim = ft.get_dimensions_by_type('spectral', self.dataset)
911
+ if len(self.spec_dim) != 1:
912
+ raise TypeError('We need exactly one SPECTRAL dimension')
913
+ self.spec_dim = self.spec_dim[0]
914
+ #self.energy_scale = self.dataset._axes[self.spec_dim]
915
+
916
+ self.energy_scale = self.spec_dim[1]
917
+ self.model = np.array([])
918
+ self.y_scale = 1.0
919
+ self.change_y_scale = 1.0
920
+ self.spectrum_ll = None
921
+ self.low_loss_key = None
922
+
923
+ self.edges = {}
924
+
925
+ self.show_regions = False
926
+
927
+ with plt.ioff():
928
+ self.fig = plt.figure()
929
+ self.fig.canvas.toolbar_position = 'right'
930
+ self.fig.canvas.toolbar_visible = True
931
+ self.key = list(self.datasets.keys())[0]
932
+ self.set_dataset()
933
+ self.set_action()
934
+ self.y_scale = 1.0
935
+ self.change_y_scale = 1.0
936
+ self.plot(scale=False)
937
+ self.selector = matplotlib.widgets.SpanSelector(self.fig.gca(), self.line_select_callback,
938
+ direction="horizontal",
939
+ interactive=True,
940
+ props=dict(facecolor='blue', alpha=0.2))
941
+ self.start_cursor = ipywidgets.FloatText(value=0, description='Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
942
+ self.end_cursor = ipywidgets.FloatText(value=0, description='End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
943
+ self.panel = ipywidgets.VBox([ipywidgets.HBox([ipywidgets.Label('',layout=ipywidgets.Layout(width='100px')), ipywidgets.Label('Cursor:'),
944
+ self.start_cursor,ipywidgets.Label('eV'),
945
+ self.end_cursor, ipywidgets.Label('eV')]),
946
+ self.fig.canvas])
947
+
948
+ self.app_layout = ipywidgets.AppLayout(
949
+ left_sidebar=self.sidebar,
950
+ center=self.panel,
951
+ footer=None,#message_bar,
952
+ pane_heights=[0, 10, 0],
953
+ pane_widths=[4, 10, 0],
954
+ )
955
+ IPython.display.display(self.app_layout)
956
+
957
+ def line_select_callback(self, x_min, x_max):
958
+ self.start_cursor.value = np.round(x_min,3)
959
+ self.end_cursor.value = np.round(x_max, 3)
960
+ self.start_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.start_cursor.value)
961
+ self.end_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.end_cursor.value)
962
+
963
+ def plot(self, scale=True):
964
+
965
+ ylim = self.fig.gca().get_ylim()
966
+
967
+ ax = self.fig.gca()
968
+ ax.clear()
969
+ ax.plot(self.energy_scale, self.datasets[self.key]*self.y_scale, label=self.datasets[self.key].title)
970
+
971
+ ax.set_xlabel(self.datasets[self.key].labels[0])
972
+ ax.set_ylabel(self.datasets[self.key].data_descriptor)
973
+ ax.ticklabel_format(style='sci', scilimits=(-2, 3))
974
+ if scale:
975
+ ax.set_ylim(np.array(ylim)*self.change_y_scale)
976
+ self.change_y_scale = 1.0
977
+ if self.y_scale != 1.:
978
+ ax.set_ylabel('scattering probability (ppm/eV)')
979
+ self.selector = matplotlib.widgets.SpanSelector(self.fig.gca(), self.line_select_callback,
980
+ direction="horizontal",
981
+ interactive=True,
982
+ props=dict(facecolor='blue', alpha=0.2))
983
+
984
+ if len(self.model) > 1:
985
+ ax.plot(self.energy_scale, self.model*self.y_scale, label='model')
986
+ difference_spec = self.datasets[self.key] - self.model
987
+ ax.plot(self.energy_scale, difference_spec*self.y_scale, label='difference')
988
+ # axis.plot(self.energy_scale, (self.datasets[key] - self.model) / np.sqrt(self.datasets[key])*self.y_scale, label='Poisson')
989
+
990
+
991
+ ax.legend()
992
+
993
+ if self.sidebar[12, 2].value:
994
+ self.show_edges()
995
+ if self.sidebar[0, 0].value:
996
+ self.plot_regions()
997
+ self.fig.canvas.draw_idle()
998
+
999
+
1000
+ def plot_regions(self):
1001
+ axis = self.fig.gca()
1002
+ y_min, y_max = axis.get_ylim()
1003
+ height = y_max - y_min
1004
+
1005
+ rect = []
1006
+ if 'fit_area' in self.edges:
1007
+ color = 'blue'
1008
+ alpha = 0.2
1009
+ x_min = self.edges['fit_area']['fit_start']
1010
+ width = self.edges['fit_area']['fit_end'] - x_min
1011
+ rect.append(patches.Rectangle((x_min, y_min), width, height,
1012
+ edgecolor=color, alpha=alpha, facecolor=color))
1013
+ axis.add_patch(rect[0])
1014
+ axis.text(x_min, y_max, 'fit region', verticalalignment='top')
1015
+ color = 'red'
1016
+ alpha = 0.5
1017
+
1018
+ for key in self.edges:
1019
+ if key.isdigit():
1020
+ x_min = self.edges[key]['start_exclude']
1021
+ width = self.edges[key]['end_exclude']-x_min
1022
+ rect.append(patches.Rectangle((x_min, y_min), width, height,
1023
+ edgecolor=color, alpha=alpha, facecolor=color))
1024
+ axis.add_patch(rect[-1])
1025
+ axis.text(x_min, y_max, f"exclude\n edge {int(key)+1}", verticalalignment='top')
1026
+
1027
+ def show_edges(self):
1028
+ axis = self.fig.gca()
1029
+ x_min, x_max = axis.get_xlim()
1030
+ y_min, y_max = axis.get_ylim()
1031
+
1032
+ for key, edge in self.edges.items():
1033
+ i = 0
1034
+ if key.isdigit():
1035
+ element = edge['element']
1036
+ for sym in edge['all_edges']:
1037
+ x = edge['all_edges'][sym]['onset'] + edge['chemical_shift']
1038
+ if x_min < x < x_max:
1039
+ axis.text(x, y_max, '\n' * i + f"{element}-{sym}",
1040
+ verticalalignment='top', color='black')
1041
+ axis.axvline(x, ymin=0, ymax=1, color='gray')
1042
+ i += 1
1043
+
1044
+
1045
+
1046
+
1047
+ def set_dataset(self, index=0):
1048
+ if 'edges' not in self.dataset.metadata or self.dataset.metadata['edges'] == {}:
1049
+ self.dataset.metadata['edges'] = {'0': {}, 'model': {}, 'use_low_loss': False}
1050
+
1051
+ self.edges = self.dataset.metadata['edges']
1052
+ if '0' not in self.edges:
1053
+ self.edges['0'] = {}
1054
+
1055
+ if 'fit_area' not in self.edges:
1056
+ self.edges['fit_area'] = {}
1057
+ if 'fit_start' not in self.edges['fit_area']:
1058
+ self.sidebar[1,0].value = np.round(self.energy_scale[50], 3)
1059
+ self.edges['fit_area']['fit_start'] = self.sidebar[1,0].value
1060
+ else:
1061
+ self.sidebar[1,0].value = np.round(self.edges['fit_area']['fit_start'],3)
1062
+ if 'fit_end' not in self.edges['fit_area']:
1063
+ self.sidebar[2,0].value = np.round(self.energy_scale[-2], 3)
1064
+ self.edges['fit_area']['fit_end'] = self.sidebar[2,0].value
1065
+ else:
1066
+ self.sidebar[2,0].value = np.round(self.edges['fit_area']['fit_end'],3)
1067
+
1068
+ if self.dataset.data_type.name == 'SPECTRAL_IMAGE':
1069
+ if 'SI_bin_x' not in self.dataset.metadata['experiment']:
1070
+ self.dataset.metadata['experiment']['SI_bin_x'] = 1
1071
+ self.dataset.metadata['experiment']['SI_bin_y'] = 1
1072
+
1073
+ bin_x = self.dataset.metadata['experiment']['SI_bin_x']
1074
+ bin_y = self.dataset.metadata['experiment']['SI_bin_y']
1075
+ # self.dataset.view.set_bin([bin_x, bin_y])
1076
+ self.update()
1077
+
1078
+ def update_element(self, z=0, index=-1):
1079
+ # We check whether this element is already in the
1080
+ if z == 0:
1081
+ z = self.sidebar[5,0].value
1082
+
1083
+ zz = eels.get_z(z)
1084
+ for key, edge in self.edges.items():
1085
+ if key.isdigit():
1086
+ if 'z' in edge:
1087
+ if zz == edge['z']:
1088
+ return False
1089
+
1090
+ major_edge = ''
1091
+ minor_edge = ''
1092
+ all_edges = {}
1093
+ x_section = eels.get_x_sections(zz)
1094
+ edge_start = 10 # int(15./ft.get_slope(self.energy_scale)+0.5)
1095
+ for key in x_section:
1096
+ if len(key) == 2 and key[0] in ['K', 'L', 'M', 'N', 'O'] and key[1].isdigit():
1097
+ if self.energy_scale[edge_start] < x_section[key]['onset'] < self.energy_scale[-edge_start]:
1098
+ if key in ['K1', 'L3', 'M5']:
1099
+ major_edge = key
1100
+ elif key in self.sidebar[6,0].options:
1101
+ if minor_edge == '':
1102
+ minor_edge = key
1103
+ if int(key[-1]) % 2 > 0:
1104
+ if int(minor_edge[-1]) % 2 == 0 or key[-1] > minor_edge[-1]:
1105
+ minor_edge = key
1106
+
1107
+ all_edges[key] = {'onset': x_section[key]['onset']}
1108
+
1109
+ if major_edge != '':
1110
+ key = major_edge
1111
+ elif minor_edge != '':
1112
+ key = minor_edge
1113
+ else:
1114
+ print(f'Could not find no edge of {zz} in spectrum')
1115
+ return False
1116
+ if index == -1:
1117
+ index = self.sidebar[4, 0].value
1118
+ # self.ui.dialog.setWindowTitle(f'{index}, {zz}')
1119
+
1120
+ if str(index) not in self.edges:
1121
+ self.edges[str(index)] = {}
1122
+
1123
+ start_exclude = x_section[key]['onset'] - x_section[key]['excl before']
1124
+ end_exclude = x_section[key]['onset'] + x_section[key]['excl after']
1125
+
1126
+ self.edges[str(index)] = {'z': zz, 'symmetry': key, 'element': eels.elements[zz],
1127
+ 'onset': x_section[key]['onset'], 'end_exclude': end_exclude,
1128
+ 'start_exclude': start_exclude}
1129
+ self.edges[str(index)]['all_edges'] = all_edges
1130
+ self.edges[str(index)]['chemical_shift'] = 0.0
1131
+ self.edges[str(index)]['areal_density'] = 0.0
1132
+ self.edges[str(index)]['original_onset'] = self.edges[str(index)]['onset']
1133
+ return True
1134
+
1135
+
1136
+
1137
+ def sort_elements(self):
1138
+ onsets = []
1139
+ for index, edge in self.edges.items():
1140
+ if index.isdigit():
1141
+ onsets.append(float(edge['onset']))
1142
+
1143
+ arg_sorted = np.argsort(onsets)
1144
+ edges = self.edges.copy()
1145
+ for index, i_sorted in enumerate(arg_sorted):
1146
+ self.edges[str(index)] = edges[str(i_sorted)].copy()
1147
+
1148
+ index = 0
1149
+ edge = self.edges['0']
1150
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
1151
+
1152
+ while str(index + 1) in self.edges:
1153
+ next_edge = self.edges[str(index + 1)]
1154
+ if edge['end_exclude'] > next_edge['start_exclude'] - 5 * dispersion:
1155
+ edge['end_exclude'] = next_edge['start_exclude'] - 5 * dispersion
1156
+ edge = next_edge
1157
+ index += 1
1158
+
1159
+ if edge['end_exclude'] > self.energy_scale[-3]:
1160
+ edge['end_exclude'] = self.energy_scale[-3]
1161
+
1162
+ def set_elements(self, selected_elements):
1163
+
1164
+ for index, elem in enumerate(selected_elements):
1165
+ self.sidebar[4, 0].value =index
1166
+ self.update_element(elem)
1167
+
1168
+ # self.sort_elements()
1169
+ self.update()
1170
+
1171
+
1172
+
1173
+ def set_element(self, elem):
1174
+ self.update_element(self.sidebar[5, 0].value)
1175
+ # self.sort_elements()
1176
+ self.update()
1177
+
1178
+ def cursor2energy_scale(self, value):
1179
+
1180
+ dispersion = (self.end_cursor.value - self.start_cursor.value) / (self.end_channel - self.start_channel)
1181
+ self.datasets[self.key].energy_loss *= (self.sidebar[3, 0].value/dispersion)
1182
+ self.sidebar[3, 0].value = dispersion
1183
+ offset = self.start_cursor.value - self.start_channel * dispersion
1184
+ self.datasets[self.key].energy_loss += (self.sidebar[2, 0].value-self.datasets[self.key].energy_loss[0])
1185
+ self.sidebar[2, 0].value = offset
1186
+ self.plot()
1187
+
1188
+ def set_fit_area(self, value):
1189
+ if self.sidebar[1,0].value > self.sidebar[2,0].value:
1190
+ self.sidebar[1,0].value = self.sidebar[2,0].value -1
1191
+ if self.sidebar[1,0].value < self.energy_scale[0]:
1192
+ self.sidebar[1,0].value = self.energy_scale[0]
1193
+ if self.sidebar[2,0].value > self.energy_scale[-1]:
1194
+ self.sidebar[2,0].value = self.energy_scale[-1]
1195
+ self.edges['fit_area']['fit_start'] = self.sidebar[1,0].value
1196
+ self.edges['fit_area']['fit_end'] = self.sidebar[2,0].value
1197
+
1198
+ self.plot()
1199
+
1200
+ def set_y_scale(self, value):
1201
+ self.change_y_scale = 1/self.y_scale
1202
+ if self.sidebar[12, 0].value:
1203
+ dispersion = self.energy_scale[1] - self.energy_scale[0]
1204
+ self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm'] * dispersion
1205
+ else:
1206
+ self.y_scale = 1.0
1207
+
1208
+ self.change_y_scale *= self.y_scale
1209
+ self.update()
1210
+ self.plot()
1211
+
1212
+ def find_elements(self, value=0):
1213
+
1214
+ if '0' not in self.edges:
1215
+ self.edges['0'] ={}
1216
+ found_edges = eels.auto_id_edges(self.dataset)
1217
+
1218
+ to_delete = []
1219
+ if len(found_edges) >0:
1220
+ for key in self.edges:
1221
+ if key.isdigit():
1222
+ to_delete.append(key)
1223
+ for key in to_delete:
1224
+ del self.edges[key]
1225
+ if len(to_delete) > 0:
1226
+ self.edges['0'] = {}
1227
+
1228
+ selected_elements = []
1229
+ for key in found_edges:
1230
+ selected_elements.append(key)
1231
+ self.set_elements(selected_elements)
1232
+
1233
+ self.update()
1234
+
1235
+ def update(self, index=0):
1236
+
1237
+ index = self.sidebar[4,0].value # which edge
1238
+ if index < 0:
1239
+ options = list(self.sidebar[4,0].options)
1240
+ options.insert(-1, (f'Edge {len(self.sidebar[4,0].options)}', len(self.sidebar[4,0].options)-1))
1241
+ self.sidebar[4,0].options= options
1242
+ self.sidebar[4,0].value = len(self.sidebar[4,0].options)-2
1243
+ if str(index) not in self.edges:
1244
+ self.edges[str(index)] = {'z': 0, 'element': 'x', 'symmetry': 'K1', 'onset': 0, 'start_exclude': 0, 'end_exclude':0,
1245
+ 'areal_density': 0}
1246
+ if 'z' not in self.edges[str(index)]:
1247
+ self.edges[str(index)] = {'z': 0, 'element': 'x', 'symmetry': 'K1', 'onset': 0, 'start_exclude': 0, 'end_exclude':0,
1248
+ 'areal_density': 0}
1249
+ edge = self.edges[str(index)]
1250
+
1251
+ self.sidebar[5,0].value = edge['z']
1252
+ self.sidebar[5,2].value = edge['element']
1253
+ self.sidebar[6,0].value = edge['symmetry']
1254
+ self.sidebar[7,0].value = edge['onset']
1255
+ self.sidebar[8,0].value = edge['start_exclude']
1256
+ self.sidebar[9,0].value = edge['end_exclude']
1257
+ if self.y_scale == 1.0:
1258
+ self.sidebar[10, 0].value = edge['areal_density']
1259
+ self.sidebar[10, 2].value = 'a.u.'
1260
+ else:
1261
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
1262
+ self.sidebar[10, 0].value = np.round(edge['areal_density']/self.dataset.metadata['experiment']['flux_ppm']*1e-6, 2)
1263
+ self.sidebar[10, 2].value = 'atoms/nm²'
1264
+
1265
+
1266
+ def do_fit(self, value=0):
1267
+ if 'experiment' in self.dataset.metadata:
1268
+ exp = self.dataset.metadata['experiment']
1269
+ if 'convergence_angle' not in exp:
1270
+ raise ValueError('need a convergence_angle in experiment of metadata dictionary ')
1271
+ alpha = exp['convergence_angle']
1272
+ beta = exp['collection_angle']
1273
+ beam_kv = exp['acceleration_voltage']
1274
+
1275
+ else:
1276
+ raise ValueError('need a experiment parameter in metadata dictionary')
1277
+
1278
+ eff_beta = eels.effective_collection_angle(self.energy_scale, alpha, beta, beam_kv)
1279
+
1280
+ self.low_loss = None
1281
+ if self.sidebar[12, 1].value:
1282
+ for key in self.datasets.keys():
1283
+ if key != self.key:
1284
+ if isinstance(self.datasets[key], sidpy.Dataset):
1285
+ if self.datasets[key].data_type.name == 'SPECTRUM':
1286
+ if self.datasets[key].energy_loss[0] < 0:
1287
+ self.low_loss = self.datasets[key]/self.datasets[key].sum()
1288
+
1289
+ edges = eels.make_cross_sections(self.edges, np.array(self.energy_scale), beam_kv, eff_beta, self.low_loss)
1290
+
1291
+ if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
1292
+ spectrum = self.dataset.view.get_spectrum()
1293
+ else:
1294
+ spectrum = self.dataset
1295
+ self.edges = eels.fit_edges2(spectrum, self.energy_scale, edges)
1296
+ areal_density = []
1297
+ elements = []
1298
+ for key in edges:
1299
+ if key.isdigit(): # only edges have numbers in that dictionary
1300
+ elements.append(edges[key]['element'])
1301
+ areal_density.append(edges[key]['areal_density'])
1302
+ areal_density = np.array(areal_density)
1303
+ out_string = '\nRelative composition: \n'
1304
+ for i, element in enumerate(elements):
1305
+ out_string += f'{element}: {areal_density[i] / areal_density.sum() * 100:.1f}% '
1306
+
1307
+ self.model = self.edges['model']['spectrum']
1308
+ self.update()
1309
+ self.plot()
1310
+
1311
+ def modify_onset(self, value=-1):
1312
+ edge_index = self.sidebar[4, 0].value
1313
+ edge = self.edges[str(edge_index)]
1314
+ edge['onset'] = self.sidebar[7,0].value
1315
+ edge['chemical_shift'] = edge['onset'] - edge['original_onset']
1316
+ self.update()
1317
+
1318
+
1319
+ def modify_start_exclude(self, value=-1):
1320
+ edge_index = self.sidebar[4, 0].value
1321
+ edge = self.edges[str(edge_index)]
1322
+ edge['start_exclude'] = self.sidebar[8,0].value
1323
+ self.plot()
1324
+
1325
+ def modify_end_exclude(self, value=-1):
1326
+ edge_index = self.sidebar[4, 0].value
1327
+ edge = self.edges[str(edge_index)]
1328
+ edge['end_exclude'] = self.sidebar[9,0].value
1329
+ self.plot()
1330
+
1331
+ def modify_areal_density(self, value=-1):
1332
+ edge_index = self.sidebar[4, 0].value
1333
+ edge = self.edges[str(edge_index)]
1334
+
1335
+ edge['areal_density'] = self.sidebar[10, 0].value
1336
+ if self.y_scale != 1.0:
1337
+ dispersion = self.energy_scale[1]-self.energy_scale[0]
1338
+ edge['areal_density'] = self.sidebar[10, 0].value *self.dataset.metadata['experiment']['flux_ppm']/1e-6
1339
+
1340
+ self.model = self.edges['model']['background']
1341
+ for key in self.edges:
1342
+ if key.isdigit():
1343
+ self.model = self.model + self.edges[key]['areal_density'] * self.edges[key]['data']
1344
+ self.plot()
1345
+
1346
+ def set_action(self):
1347
+ self.sidebar[1, 0].observe(self.set_fit_area, names='value')
1348
+ self.sidebar[2, 0].observe(self.set_fit_area, names='value')
1349
+
1350
+ self.sidebar[3, 0].on_click(self.find_elements)
1351
+ self.sidebar[4, 0].observe(self.update)
1352
+ self.sidebar[5, 0].observe(self.set_element, names='value')
1353
+
1354
+ self.sidebar[7, 0].observe(self.modify_onset, names='value')
1355
+ self.sidebar[8, 0].observe(self.modify_start_exclude, names='value')
1356
+ self.sidebar[9, 0].observe(self.modify_end_exclude, names='value')
1357
+ self.sidebar[10, 0].observe(self.modify_areal_density, names='value')
1358
+
1359
+ self.sidebar[11, 0].on_click(self.do_fit)
1360
+ self.sidebar[12, 2].observe(self.plot)
1361
+ self.sidebar[0, 0].observe(self.plot)
1362
+
1363
+ self.sidebar[12,0].observe(self.set_y_scale)