pyTEMlib 0.2023.3.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/peak_dialog.py CHANGED
@@ -7,17 +7,23 @@ try:
7
7
  from PyQt5 import QtCore, QtWidgets
8
8
  except:
9
9
  Qt_available = False
10
- print('Qt dialogs are not available')
10
+ # print('Qt dialogs are not available')
11
11
 
12
12
  import numpy as np
13
13
  import scipy
14
14
  import scipy.optimize
15
15
  import scipy.signal
16
16
 
17
+ import ipywidgets
18
+ from IPython.display import display
19
+ import matplotlib
20
+ import matplotlib.pylab as plt
21
+ import matplotlib.patches as patches
22
+
17
23
  import sidpy
18
24
  import pyTEMlib.file_tools as ft
19
- import pyTEMlib.eels_tools as eels
20
- import pyTEMlib.peak_dlg as peak_dlg
25
+ from pyTEMlib import eels_tools
26
+ from pyTEMlib import peak_dlg
21
27
 
22
28
  advanced_present = True
23
29
  try:
@@ -28,20 +34,626 @@ except ModuleNotFoundError:
28
34
 
29
35
  _version = .001
30
36
 
37
+ def get_sidebar():
38
+ side_bar = ipywidgets.GridspecLayout(16, 3, width='auto', grid_gap="0px")
39
+ row = 0
40
+ side_bar[row, :3] = ipywidgets.Button(description='Fit Area',
41
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
42
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
43
+ row += 1
44
+ side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Fit Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
45
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
46
+ row += 1
47
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Fit End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
48
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
49
+
50
+ row += 1
51
+ side_bar[row, :3] = ipywidgets.Button(description='Peak Finding',
52
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
53
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
54
+
55
+ row += 1
56
+
57
+
58
+ side_bar[row, :2] = ipywidgets.Dropdown(
59
+ options=[('0', 0), ('1', 1), ('2', 2), ('3', 3), ('4', 4)],
60
+ value=0,
61
+ description='Peaks:',
62
+ disabled=False,
63
+ layout=ipywidgets.Layout(width='200px'))
64
+
65
+ side_bar[row, 2] = ipywidgets.Button(
66
+ description='Smooth',
67
+ disabled=False,
68
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
69
+ tooltip='Do Gaussian Mixing',
70
+ layout=ipywidgets.Layout(width='100px'))
71
+
72
+ row += 1
73
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Number:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
74
+ side_bar[row, 2] = ipywidgets.Button(
75
+ description='Find',
76
+ disabled=False,
77
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
78
+ tooltip='Find first peaks from Gaussian mixture',
79
+ layout=ipywidgets.Layout(width='100px'))
80
+
81
+ row += 1
82
+
83
+ side_bar[row, :3] = ipywidgets.Button(description='Peaks',
84
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
85
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
86
+ row += 1
87
+ side_bar[row, :2] = ipywidgets.Dropdown(
88
+ options=[('Peak 1', 0), ('Add Peak', -1)],
89
+ value=0,
90
+ description='Peaks:',
91
+ disabled=False,
92
+ layout=ipywidgets.Layout(width='200px'))
93
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="", layout=ipywidgets.Layout(width='100px'))
94
+ row += 1
95
+ side_bar[row, :2] = ipywidgets.Dropdown(
96
+ options=[ 'Gauss', 'Lorentzian', 'Drude', 'Zero-Loss'],
97
+ value='Gauss',
98
+ description='Symmetry:',
99
+ disabled=False,
100
+ layout=ipywidgets.Layout(width='200px'))
101
+ row += 1
102
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Position:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
103
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
104
+ row += 1
105
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Amplitude:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
106
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
107
+ row += 1
108
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Width FWHM:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
109
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
110
+ row += 1
111
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Asymmetry:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
112
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="a.u.", layout=ipywidgets.Layout(width='100px'))
113
+ row += 1
114
+
115
+ side_bar[row, :3] = ipywidgets.Button(description='White-Line',
116
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
117
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
118
+
119
+ row += 1
120
+ side_bar[row, :2] = ipywidgets.Dropdown(
121
+ options=[('None', 0)],
122
+ value=0,
123
+ description='Ratio:',
124
+ disabled=False,
125
+ layout=ipywidgets.Layout(width='200px'))
126
+ side_bar[row, 2] = ipywidgets.widgets.Label(value=" ", layout=ipywidgets.Layout(width='100px'))
127
+ row += 1
128
+ side_bar[row, :2] = ipywidgets.Dropdown(
129
+ options=[('None', 0)],
130
+ value=0,
131
+ description= 'Sum:',
132
+ disabled=False,
133
+ layout=ipywidgets.Layout(width='200px'))
134
+ side_bar[row, 2] = ipywidgets.widgets.Label(value=" ", layout=ipywidgets.Layout(width='100px'))
135
+ return side_bar
136
+
137
+ class PeakFitWidget(object):
138
+ def __init__(self, datasets=None):
139
+ self.datasets = datasets
140
+ if not isinstance(datasets, dict):
141
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
142
+
143
+ self.sidebar = get_sidebar()
144
+ self.key = list(self.datasets)[0]
145
+ self.dataset = datasets[self.key]
146
+ if not isinstance(self.dataset, sidpy.Dataset):
147
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
148
+ self.spec_dim = ft.get_dimensions_by_type('spectral', self.dataset)
149
+ if len(self.spec_dim) != 1:
150
+ raise TypeError('We need exactly one SPECTRAL dimension')
151
+ self.spec_dim = self.spec_dim[0]
152
+ #self.energy_scale = self.dataset._axes[self.spec_dim]
153
+
154
+ self.energy_scale = self.spec_dim[1]
155
+
156
+
157
+ self.model = np.array([])
158
+ self.y_scale = 1.0
159
+ self.change_y_scale = 1.0
160
+ self.spectrum_ll = None
161
+ self.low_loss_key = None
162
+
163
+ self.peaks = {}
164
+
165
+ self.show_regions = False
166
+
167
+ with plt.ioff():
168
+ self.fig = plt.figure()
169
+ self.fig.canvas.toolbar_position = 'right'
170
+ self.fig.canvas.toolbar_visible = True
171
+ #self.set_dataset()
172
+ self.set_action()
173
+ self.y_scale = 1.0
174
+ self.change_y_scale = 1.0
175
+ self.plot(scale=False)
176
+
177
+ if 'peak_fit' not in self.dataset.metadata:
178
+ self.dataset.metadata['peak_fit'] = {}
179
+ if 'edges' in self.dataset.metadata:
180
+ if 'fit_area' in self.dataset.metadata['edges']:
181
+ self.dataset.metadata['peak_fit']['fit_start'] = \
182
+ self.dataset.metadata['edges']['fit_area']['fit_start']
183
+ self.dataset.metadata['peak_fit']['fit_end'] = self.dataset.metadata['edges']['fit_area']['fit_end']
184
+ self.dataset.metadata['peak_fit']['peaks'] = {'0': {'position': self.energy_scale[1],
185
+ 'amplitude': 1000.0, 'width': 1.0,
186
+ 'type': 'Gauss', 'asymmetry': 0}}
187
+
188
+
189
+ self.peaks = self.dataset.metadata['peak_fit']
190
+ if 'fit_start' not in self.peaks:
191
+ self.peaks['fit_start'].value = self.energy_scale[1]
192
+ self.peaks['fit_end'].value = self.energy_scale[-2]
193
+
194
+ if 'peak_model' in self.peaks:
195
+ self.peak_model = self.peaks['peak_model']
196
+ self.model = self.peak_model
197
+ if 'edge_model' in self.peaks:
198
+ self.model = self.model + self.peaks['edge_model']
199
+ else:
200
+ self.model = np.array([])
201
+ self.peak_model = np.array([])
202
+ if 'peak_out_list' in self.peaks:
203
+ self.peak_out_list = self.peaks['peak_out_list']
204
+ self.set_peak_list()
205
+
206
+ # check whether a core loss analysis has been done previously
207
+ if not hasattr(self, 'core_loss') and 'edges' in self.dataset.metadata:
208
+ self.core_loss = True
209
+ else:
210
+ self.core_loss = False
211
+
212
+ self.update()
213
+
214
+ self.selector = matplotlib.widgets.SpanSelector(self.fig.gca(), self.line_select_callback,
215
+ direction="horizontal",
216
+ interactive=True,
217
+ props=dict(facecolor='blue', alpha=0.2))
218
+ self.start_cursor = ipywidgets.FloatText(value=0, description='Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
219
+ self.end_cursor = ipywidgets.FloatText(value=0, description='End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
220
+ self.panel = ipywidgets.VBox([ipywidgets.HBox([ipywidgets.Label('',layout=ipywidgets.Layout(width='100px')), ipywidgets.Label('Cursor:'),
221
+ self.start_cursor,ipywidgets.Label('eV'),
222
+ self.end_cursor, ipywidgets.Label('eV')]),
223
+ self.fig.canvas])
224
+
225
+ self.app_layout = ipywidgets.AppLayout(
226
+ left_sidebar=self.sidebar,
227
+ center=self.panel,
228
+ footer=None,#message_bar,
229
+ pane_heights=[0, 10, 0],
230
+ pane_widths=[4, 10, 0],
231
+ )
232
+ display(self.app_layout)
233
+
234
+ def line_select_callback(self, x_min, x_max):
235
+ self.start_cursor.value = np.round(x_min,3)
236
+ self.end_cursor.value = np.round(x_max, 3)
237
+ self.start_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.start_cursor.value)
238
+ self.end_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.end_cursor.value)
239
+
240
+
241
+ def set_peak_list(self):
242
+ self.peak_list = []
243
+ if 'peaks' not in self.peaks:
244
+ self.peaks['peaks'] = {}
245
+ key = 0
246
+ for key in self.peaks['peaks']:
247
+ if key.isdigit():
248
+ self.peak_list.append((f'Peak {int(key) + 1}', int(key)))
249
+ self.peak_list.append(('add peak', -1))
250
+ self.sidebar[7, 0].options = self.peak_list
251
+ self.sidebar[7, 0].value = 0
252
+
253
+
254
+ def plot(self, scale=True):
255
+
256
+ ylim = self.fig.gca().get_ylim()
257
+
258
+ ax = self.fig.gca()
259
+ ax.clear()
260
+ ax.plot(self.energy_scale, self.datasets[self.key]*self.y_scale, label=self.datasets[self.key].title)
261
+ ax.set_xlabel(self.datasets[self.key].labels[0])
262
+ ax.set_ylabel(self.datasets[self.key].data_descriptor)
263
+ ax.ticklabel_format(style='sci', scilimits=(-2, 3))
264
+ if scale:
265
+ ax.set_ylim(np.array(ylim)*self.change_y_scale)
266
+ self.change_y_scale = 1.0
267
+ if self.y_scale != 1.:
268
+ ax.set_ylabel('scattering probability (ppm/eV)')
269
+ self.selector = matplotlib.widgets.SpanSelector(self.fig.gca(), self.line_select_callback,
270
+ direction="horizontal",
271
+ interactive=True,
272
+ props=dict(facecolor='blue', alpha=0.2))
273
+
274
+ if len(self.model) > 1:
275
+ ax.plot(self.energy_scale, self.model*self.y_scale, label='model')
276
+ difference_spec = self.datasets[self.key] - self.model
277
+ ax.plot(self.energy_scale, difference_spec*self.y_scale, label='difference')
278
+ # axis.plot(self.energy_scale, (self.datasets[key] - self.model) / np.sqrt(self.datasets[key])*self.y_scale, label='Poisson')
279
+
280
+ if 'peaks' in self.peaks:
281
+ for index, peak in self.peaks['peaks'].items():
282
+ p = [peak['position'], peak['amplitude'], peak['width']]
283
+ ax.plot(self.energy_scale, eels_tools.gauss(self.energy_scale, p))
284
+
285
+ ax.legend()
286
+
287
+
288
+
289
+ def set_dataset(self, index=0):
290
+ if 'edges' not in self.dataset.metadata or self.dataset.metadata['edges'] == {}:
291
+ self.dataset.metadata['edges'] = {'0': {}, 'model': {}, 'use_low_loss': False}
292
+
293
+ self.edges = self.dataset.metadata['edges']
294
+ if '0' not in self.edges:
295
+ self.edges['0'] = {}
296
+
297
+ if 'fit_area' not in self.edges:
298
+ self.edges['fit_area'] = {}
299
+ if 'fit_start' not in self.edges['fit_area']:
300
+ self.sidebar[1,0].value = np.round(self.energy_scale[50], 3)
301
+ self.edges['fit_area']['fit_start'] = self.sidebar[1,0].value
302
+ else:
303
+ self.sidebar[1,0].value = np.round(self.edges['fit_area']['fit_start'],3)
304
+ if 'fit_end' not in self.edges['fit_area']:
305
+ self.sidebar[2,0].value = np.round(self.energy_scale[-2], 3)
306
+ self.edges['fit_area']['fit_end'] = self.sidebar[2,0].value
307
+ else:
308
+ self.sidebar[2,0].value = np.round(self.edges['fit_area']['fit_end'],3)
309
+
310
+ if self.dataset.data_type.name == 'SPECTRAL_IMAGE':
311
+ if 'SI_bin_x' not in self.dataset.metadata['experiment']:
312
+ self.dataset.metadata['experiment']['SI_bin_x'] = 1
313
+ self.dataset.metadata['experiment']['SI_bin_y'] = 1
314
+
315
+ bin_x = self.dataset.metadata['experiment']['SI_bin_x']
316
+ bin_y = self.dataset.metadata['experiment']['SI_bin_y']
317
+ # self.dataset.view.set_bin([bin_x, bin_y])
318
+ self.update()
319
+
320
+ def set_fit_area(self, value):
321
+ """
322
+ if self.sidebar[1,0].value > self.sidebar[2,0].value:
323
+ self.sidebar[1,0].value = self.sidebar[2,0].value - 1.0
324
+ if float(self.sidebar[1,0].value) < self.energy_scale[0]:
325
+ self.sidebar[1,0].value = self.energy_scale[0]
326
+ if self.sidebar[2,0].value > self.energy_scale[-1]:
327
+ self.sidebar[2,0].value = self.energy_scale[-1]
328
+ """
329
+ self.peaks['fit_start'] = self.sidebar[1, 0].value
330
+ self.peaks['fit_end'] = self.sidebar[2, 0].value
331
+
332
+ self.plot()
333
+
334
+ def set_y_scale(self, value):
335
+ self.change_y_scale = 1/self.y_scale
336
+ if self.sidebar[12, 0].value:
337
+ dispersion = self.energy_scale[1] - self.energy_scale[0]
338
+ self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm'] * dispersion
339
+ else:
340
+ self.y_scale = 1.0
341
+
342
+ self.change_y_scale *= self.y_scale
343
+ self.update()
344
+ self.plot()
345
+
346
+ def update(self, index=0):
347
+
348
+ # self.setWindowTitle('update')
349
+ self.sidebar[1, 0].value = self.peaks['fit_start']
350
+ self.sidebar[2, 0].value = self.peaks['fit_end']
351
+
352
+ peak_index = self.sidebar[7, 0].value
353
+ self.peak_index = self.sidebar[7, 0].value
354
+ if str(peak_index) not in self.peaks['peaks']:
355
+ self.peaks['peaks'][str(peak_index)] = {'position': self.energy_scale[1], 'amplitude': 1000.0,
356
+ 'width': 1.0, 'type': 'Gauss', 'asymmetry': 0}
357
+ self.sidebar[8, 0].value = self.peaks['peaks'][str(peak_index)]['type']
358
+ if 'associated_edge' in self.peaks['peaks'][str(peak_index)]:
359
+ self.sidebar[7, 2].value = (self.peaks['peaks'][str(peak_index)]['associated_edge'])
360
+ else:
361
+ self.sidebar[7, 2].value = ''
362
+ self.sidebar[9, 0].value = self.peaks['peaks'][str(peak_index)]['position']
363
+ self.sidebar[10, 0].value = self.peaks['peaks'][str(peak_index)]['amplitude']
364
+ self.sidebar[11, 0].value = self.peaks['peaks'][str(peak_index)]['width']
365
+ if 'asymmetry' not in self.peaks['peaks'][str(peak_index)]:
366
+ self.peaks['peaks'][str(peak_index)]['asymmetry'] = 0.
367
+ self.sidebar[12, 0].value = self.peaks['peaks'][str(peak_index)]['asymmetry']
368
+
369
+
370
+ def fit_peaks(self, value = 0):
371
+ """Fit spectrum with peaks given in peaks dictionary"""
372
+ print('Fitting peaks...')
373
+ p_in = []
374
+ for key, peak in self.peaks['peaks'].items():
375
+ if key.isdigit():
376
+ p_in.append(peak['position'])
377
+ p_in.append(peak['amplitude'])
378
+ p_in.append(peak['width'])
379
+
380
+ spectrum = np.array(self.dataset)
381
+
382
+ # set the energy scale and fit start and end points
383
+ energy_scale = np.array(self.energy_scale)
384
+ start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
385
+ end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
386
+
387
+ energy_scale = self.energy_scale[start_channel:end_channel]
388
+ # select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
389
+ if 'model' in self.dataset.metadata:
390
+ model = self.dataset.metadata['model'][start_channel:end_channel]
391
+ elif self.core_loss:
392
+ print('Core loss model found. Fitting on top of the model.')
393
+ model = self.dataset.metadata['edges']['model']['spectrum'][start_channel:end_channel]
394
+ else:
395
+ print('No core loss model found. Fitting to the full spectrum.')
396
+ model = np.zeros(end_channel - start_channel)
397
+
398
+ # if we have a core loss model we will only fit the difference between the model and the data.
399
+ difference = np.array(spectrum[start_channel:end_channel] - model)
400
+
401
+ # find the optimum fitting parameters
402
+ [self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals_smooth, np.array(p_in), ftol=1e-3,
403
+ args=(energy_scale, difference, False))
404
+
405
+ # construct the fit data from the optimized parameters
406
+ self.peak_model = np.zeros(len(self.energy_scale))
407
+ self.model = np.zeros(len(self.energy_scale))
408
+ self.model[start_channel:end_channel] = model
409
+ fit = eels_tools.model_smooth(energy_scale, self.p_out, False)
410
+ self.peak_model[start_channel:end_channel] = fit
411
+ self.dataset.metadata['peak_fit']['edge_model'] = self.model
412
+ self.model = self.model + self.peak_model
413
+ self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
414
+
415
+ for key, peak in self.peaks['peaks'].items():
416
+ if key.isdigit():
417
+ p_index = int(key)*3
418
+ self.peaks['peaks'][key] = {'position': self.p_out[p_index],
419
+ 'amplitude': self.p_out[p_index+1],
420
+ 'width': self.p_out[p_index+2],
421
+ 'type': 'Gauss',
422
+ 'associated_edge': ''}
423
+
424
+ self.find_associated_edges()
425
+ self.find_white_lines()
426
+ self.update()
427
+ self.plot()
428
+
429
+ def find_associated_edges(self):
430
+ onsets = []
431
+ edges = []
432
+ if 'edges' in self.dataset.metadata:
433
+ for key, edge in self.dataset.metadata['edges'].items():
434
+ if key.isdigit():
435
+ element = edge['element']
436
+ for sym in edge['all_edges']: # TODO: Could be replaced with exclude
437
+ onsets.append(edge['all_edges'][sym]['onset'] + edge['chemical_shift'])
438
+ # if 'sym' == edge['symmetry']:
439
+ edges.append([key, f"{element}-{sym}", onsets[-1]])
440
+ for key, peak in self.peaks['peaks'].items():
441
+ if key.isdigit():
442
+ distance = self.energy_scale[-1]
443
+ index = -1
444
+ for ii, onset in enumerate(onsets):
445
+ if onset < peak['position'] < onset+50:
446
+ if distance > np.abs(peak['position'] - onset):
447
+ distance = np.abs(peak['position'] - onset) # TODO: check whether absolute is good
448
+ distance_onset = peak['position'] - onset
449
+ index = ii
450
+ if index >= 0:
451
+ peak['associated_edge'] = edges[index][1] # check if more info is necessary
452
+ peak['distance_to_onset'] = distance_onset
453
+
454
+ def find_white_lines(self):
455
+ if 'edges' in self.dataset.metadata:
456
+ white_lines = {}
457
+ for index, peak in self.peaks['peaks'].items():
458
+ if index.isdigit():
459
+ if 'associated_edge' in peak:
460
+ if peak['associated_edge'][-2:] in ['L3', 'L2', 'M5', 'M4']:
461
+ if peak['distance_to_onset'] < 10:
462
+ area = np.sqrt(2 * np.pi) * peak['amplitude'] * np.abs(peak['width']/np.sqrt(2 * np.log(2)))
463
+ if peak['associated_edge'] not in white_lines:
464
+ white_lines[peak['associated_edge']] = 0.
465
+ if area > 0:
466
+ white_lines[peak['associated_edge']] += area # TODO: only positive ones?
467
+ white_line_ratios = {}
468
+ white_line_sum = {}
469
+ for sym, area in white_lines.items():
470
+ if sym[-2:] in ['L2', 'M4', 'M2']:
471
+ if area > 0 and f"{sym[:-1]}{int(sym[-1]) + 1}" in white_lines:
472
+ if white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"] > 0:
473
+ white_line_ratios[f"{sym}/{sym[-2]}{int(sym[-1]) + 1}"] = area / white_lines[
474
+ f"{sym[:-1]}{int(sym[-1]) + 1}"]
475
+ white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] = (
476
+ area + white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"])
477
+
478
+ areal_density = 1.
479
+ if 'edges' in self.dataset.metadata:
480
+ for key, edge in self.dataset.metadata['edges'].items():
481
+ if key.isdigit():
482
+ if edge['element'] == sym.split('-')[0]:
483
+ areal_density = edge['areal_density']
484
+ break
485
+ white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] /= areal_density
486
+
487
+ self.peaks['white_lines'] = white_lines
488
+ self.peaks['white_line_ratios'] = white_line_ratios
489
+ self.peaks['white_line_sums'] = white_line_sum
490
+ self.wl_list = []
491
+ self.wls_list = []
492
+ if len(self.peaks['white_line_ratios']) > 0:
493
+ for key in self.peaks['white_line_ratios']:
494
+ self.wl_list.append(key)
495
+ for key in self.peaks['white_line_sums']:
496
+ self.wls_list.append(key)
497
+
498
+ self.sidebar[14, 0].options = self.wl_list
499
+ self.sidebar[14, 0].value = self.wl_list[0]
500
+ self.sidebar[14, 2].value = f"{self.peaks['white_line_ratios'][self.wl_list[0]]:.2f}"
501
+
502
+ self.sidebar[15, 0].options = self.wls_list
503
+ self.sidebar[15, 0].value = self.wls_list[0]
504
+ self.sidebar[15, 2].value = f"{self.peaks['white_line_sums'][self.wls_list[0]]*1e6:.4f} ppm"
505
+
506
+ else:
507
+ self.wl_list.append('Ratio')
508
+ self.wls_list.append('Sum')
509
+
510
+ self.sidebar[14, 0].options = ['None']
511
+ self.sidebar[14, 0].value = 'None'
512
+ self.sidebar[14, 2].value = ' '
513
+
514
+ self.sidebar[15, 0].options = ['None']
515
+ self.sidebar[15, 0].value = 'None'
516
+ self.sidebar[15, 2].value = ' '
517
+ def find_peaks(self, value=0):
518
+ number_of_peaks = int(self.sidebar[5, 0].value)
519
+
520
+ # is now sorted in smooth function
521
+ # flat_list = [item for sublist in self.peak_out_list for item in sublist]
522
+ # new_list = np.reshape(flat_list, [len(flat_list) // 3, 3])
523
+ # arg_list = np.argsort(np.abs(new_list[:, 1]))
524
+
525
+ self.peak_list = []
526
+ self.peaks['peaks'] = {}
527
+ for i in range(number_of_peaks):
528
+ self.peak_list.append((f'Peak {i+1}', i))
529
+ p = self.peak_out_list[i]
530
+ self.peaks['peaks'][str(i)] = {'position': p[0], 'amplitude': p[1], 'width': p[2], 'type': 'Gauss',
531
+ 'asymmetry': 0}
532
+
533
+ self.peak_list.append((f'add peak', -1))
534
+
535
+ self.sidebar[7, 0].options = self.peak_list
536
+ self.sidebar[7, 0].value = 0
537
+ self.find_associated_edges()
538
+ self.find_white_lines()
539
+
540
+ self.update()
541
+ self.plot()
542
+
543
+ def smooth(self, value=0):
544
+ """Fit lots of Gaussian to spectrum and let the program sort it out
545
+
546
+ We sort the peaks by area under the Gaussians, assuming that small areas mean noise.
547
+
548
+ """
549
+ iterations = self.sidebar[4, 0].value
550
+ self.sidebar[5, 0].value = 0
551
+ advanced_present=False
552
+
553
+ self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset, iterations, advanced_present)
554
+
555
+ spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
556
+ if spec_dim[1][0] > 0:
557
+ self.model = self.dataset.metadata['edges']['model']['spectrum']
558
+ elif 'model' in self.dataset.metadata:
559
+ self.model = self.dataset.metadata['model']
560
+ else:
561
+ self.model = np.zeros(len(spec_dim[1]))
562
+
563
+ self.sidebar[5, 0].value = number_of_peaks
564
+
565
+ self.dataset.metadata['peak_fit']['edge_model'] = self.model
566
+ self.model = self.model + self.peak_model
567
+ self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
568
+ self.dataset.metadata['peak_fit']['peak_out_list'] = self.peak_out_list
569
+
570
+ self.update()
571
+ self.plot()
572
+
573
+ def make_model(self):
574
+ p_peaks = []
575
+ for key, peak in self.peaks['peaks'].items():
576
+ if key.isdigit():
577
+ p_peaks.append(peak['position'])
578
+ p_peaks.append(peak['amplitude'])
579
+ p_peaks.append(peak['width'])
580
+
581
+
582
+ # set the energy scale and fit start and end points
583
+ energy_scale = np.array(self.energy_scale)
584
+ start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
585
+ end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
586
+ energy_scale = self.energy_scale[start_channel:end_channel]
587
+ # select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
588
+
589
+ fit = eels_tools.model_smooth(energy_scale, p_peaks, False)
590
+ self.peak_model[start_channel:end_channel] = fit
591
+ if 'edge_model' in self.dataset.metadata['peak_fit']:
592
+ self.model = self.dataset.metadata['peak_fit']['edge_model'] + self.peak_model
593
+ else:
594
+ self.model = np.zeros(self.dataset.shape)
595
+ def modify_peak_position(self, value=-1):
596
+ peak_index = self.sidebar[7, 0].value
597
+ self.peaks['peaks'][str(peak_index)]['position'] = self.sidebar[9,0].value
598
+ self.make_model()
599
+ self.plot()
600
+
601
+ def modify_peak_amplitude(self, value=-1):
602
+ peak_index = self.sidebar[7, 0].value
603
+ self.peaks['peaks'][str(peak_index)]['amplitude'] = self.sidebar[10,0].value
604
+ self.make_model()
605
+ self.plot()
606
+
607
+ def modify_peak_width(self, value=-1):
608
+ peak_index = self.sidebar[7, 0].value
609
+ self.peaks['peaks'][str(peak_index)]['width'] = self.sidebar[11,0].value
610
+ self.make_model()
611
+ self.plot()
612
+
613
+
614
+ def set_action(self):
615
+ self.sidebar[1, 0].observe(self.set_fit_area, names='value')
616
+ self.sidebar[2, 0].observe(self.set_fit_area, names='value')
617
+
618
+ self.sidebar[4, 2].on_click(self.smooth)
619
+ self.sidebar[7,0].observe(self.update)
620
+ self.sidebar[5,2].on_click(self.find_peaks)
621
+
622
+ self.sidebar[6, 0].on_click(self.fit_peaks)
623
+ self.sidebar[9, 0].observe(self.modify_peak_position, names='value')
624
+ self.sidebar[10, 0].observe(self.modify_peak_amplitude, names='value')
625
+ self.sidebar[11, 0].observe(self.modify_peak_width, names='value')
626
+
627
+
628
+
629
+
31
630
  if Qt_available:
32
631
  class PeakFitDialog(QtWidgets.QDialog):
33
632
  """
34
633
  EELS Input Dialog for ELNES Analysis
35
634
  """
36
635
 
37
- def __init__(self, dataset=None):
636
+ def __init__(self, datasets=None):
38
637
  super().__init__(None, QtCore.Qt.WindowStaysOnTopHint)
638
+
639
+ if datasets is None:
640
+ # make a dummy dataset
641
+ datasets = ft.make_dummy_dataset('spectrum')
642
+ if not isinstance(datasets, dict):
643
+ datasets= {'Channel_000': datasets}
644
+
645
+ self.dataset = datasets[list(datasets.keys())[0]]
646
+ self.datasets = datasets
39
647
  # Create an instance of the GUI
40
- self.ui = peak_dlg.UiDialog(self)
648
+ if 'low_loss' in self.dataset.metadata:
649
+ mode = 'low_loss'
650
+ else:
651
+ mode = 'core_loss'
652
+
653
+ self.ui = peak_dlg.UiDialog(self, mode=mode)
41
654
 
42
655
  self.set_action()
43
656
 
44
- self.dataset = dataset
45
657
  self.energy_scale = np.array([])
46
658
  self.peak_out_list = []
47
659
  self.p_out = []
@@ -49,14 +661,11 @@ if Qt_available:
49
661
  self.show_regions = False
50
662
  self.show()
51
663
 
52
- if dataset is None:
53
- # make a dummy dataset
54
- dataset = ft.make_dummy_dataset('spectrum')
664
+
55
665
 
56
- if not isinstance(dataset, sidpy.Dataset):
666
+ if not isinstance(self.dataset, sidpy.Dataset):
57
667
  raise TypeError('dataset has to be a sidpy dataset')
58
- self.dataset = dataset
59
- self.spec_dim = ft.get_dimensions_by_type('spectral', dataset)
668
+ self.spec_dim = ft.get_dimensions_by_type('spectral', self.dataset)
60
669
  if len(self.spec_dim) != 1:
61
670
  raise TypeError('We need exactly one SPECTRAL dimension')
62
671
  self.spec_dim = self.spec_dim[0]
@@ -72,6 +681,7 @@ if Qt_available:
72
681
  self.dataset.metadata['peak_fit']['peaks'] = {'0': {'position': self.energy_scale[1],
73
682
  'amplitude': 1000.0, 'width': 1.0,
74
683
  'type': 'Gauss', 'asymmetry': 0}}
684
+
75
685
 
76
686
  self.peaks = self.dataset.metadata['peak_fit']
77
687
  if 'fit_start' not in self.peaks:
@@ -120,7 +730,13 @@ if Qt_available:
120
730
  self.ui.smooth_list.addItems(self.ui.iteration_list)
121
731
  self.ui.smooth_list.setCurrentIndex(0)
122
732
 
733
+ if 'low_loss' in self.dataset.metadata:
734
+ self.ui.iteration_list = ['0']
735
+
736
+
123
737
  self.figure.canvas.mpl_connect('button_press_event', self.plot)
738
+
739
+
124
740
  self.plot()
125
741
 
126
742
  def update(self):
@@ -145,12 +761,20 @@ if Qt_available:
145
761
  self.ui.edit8.setText(f"{self.peaks['peaks'][str(peak_index)]['asymmetry']:.2f}")
146
762
 
147
763
  def plot(self):
764
+
148
765
  spec_dim = ft.get_dimensions_by_type(sidpy.DimensionType.SPECTRAL, self.dataset)
149
766
  spec_dim = spec_dim[0]
150
767
  self.energy_scale = spec_dim[1].values
151
768
  if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
152
769
  spectrum = self.dataset.view.get_spectrum()
153
770
  self.axis = self.dataset.view.axes[1]
771
+ name = 's'
772
+ if 'zero_loss' in self.dataset.metadata:
773
+ x = self.dataset.view.x
774
+ y = self.dataset.view.y
775
+ self.energy_scale -= self.dataset.metadata['zero_loss']['shifts'][x, y]
776
+ name = f"shift { self.dataset.metadata['zero_loss']['shifts'][x, y]:.3f}"
777
+ self.setWindowTitle(f'plot {x}')
154
778
  else:
155
779
  spectrum = np.array(self.dataset)
156
780
  self.axis = self.dataset.view.axis
@@ -160,10 +784,13 @@ if Qt_available:
160
784
  self.axis.clear()
161
785
 
162
786
  self.axis.plot(self.energy_scale, spectrum, label='spectrum')
787
+ if 'zero_loss' in self.dataset.metadata:
788
+ self.axis.plot(self.energy_scale, spectrum, label=name)
789
+
163
790
  if len(self.model) > 1:
164
791
  self.axis.plot(self.energy_scale, self.model, label='model')
165
792
  self.axis.plot(self.energy_scale, spectrum - self.model, label='difference')
166
- self.axis.plot(self.energy_scale, (spectrum - self.model) / np.sqrt(spectrum), label='Poisson')
793
+ #self.axis.plot(self.energy_scale, (spectrum - self.model) / np.sqrt(spectrum), label='Poisson')
167
794
  self.axis.legend()
168
795
  self.axis.set_xlim(x_limit)
169
796
  self.axis.set_ylim(y_limit)
@@ -171,7 +798,7 @@ if Qt_available:
171
798
 
172
799
  for index, peak in self.peaks['peaks'].items():
173
800
  p = [peak['position'], peak['amplitude'], peak['width']]
174
- self.axis.plot(self.energy_scale, eels.gauss(self.energy_scale, p))
801
+ self.axis.plot(self.energy_scale, eels_tools.gauss(self.energy_scale, p))
175
802
 
176
803
  def fit_peaks(self):
177
804
  """Fit spectrum with peaks given in peaks dictionary"""
@@ -196,7 +823,9 @@ if Qt_available:
196
823
 
197
824
  energy_scale = self.energy_scale[start_channel:end_channel]
198
825
  # select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
199
- if self.core_loss:
826
+ if 'model' in self.dataset.metadata:
827
+ model = self.dataset.metadata['model'][start_channel:end_channel]
828
+ elif self.core_loss:
200
829
  print('Core loss model found. Fitting on top of the model.')
201
830
  model = self.dataset.metadata['edges']['model']['spectrum'][start_channel:end_channel]
202
831
  else:
@@ -207,14 +836,14 @@ if Qt_available:
207
836
  difference = np.array(spectrum[start_channel:end_channel] - model)
208
837
 
209
838
  # find the optimum fitting parameters
210
- [self.p_out, _] = scipy.optimize.leastsq(eels.residuals_smooth, np.array(p_in), ftol=1e-3,
839
+ [self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals_smooth, np.array(p_in), ftol=1e-3,
211
840
  args=(energy_scale, difference, False))
212
841
 
213
842
  # construct the fit data from the optimized parameters
214
843
  self.peak_model = np.zeros(len(self.energy_scale))
215
844
  self.model = np.zeros(len(self.energy_scale))
216
845
  self.model[start_channel:end_channel] = model
217
- fit = eels.model_smooth(energy_scale, self.p_out, False)
846
+ fit = eels_tools.model_smooth(energy_scale, self.p_out, False)
218
847
  self.peak_model[start_channel:end_channel] = fit
219
848
  self.dataset.metadata['peak_fit']['edge_model'] = self.model
220
849
  self.model = self.model + self.peak_model
@@ -246,6 +875,8 @@ if Qt_available:
246
875
  spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
247
876
  if spec_dim[1][0] > 0:
248
877
  self.model = self.dataset.metadata['edges']['model']['spectrum']
878
+ elif 'model' in self.dataset.metadata:
879
+ self.model = self.dataset.metadata['model']
249
880
  else:
250
881
  self.model = np.zeros(len(spec_dim[1]))
251
882
 
@@ -270,85 +901,86 @@ if Qt_available:
270
901
  onsets.append(edge['all_edges'][sym]['onset'] + edge['chemical_shift'])
271
902
  # if 'sym' == edge['symmetry']:
272
903
  edges.append([key, f"{element}-{sym}", onsets[-1]])
273
- for key, peak in self.peaks['peaks'].items():
274
- if key.isdigit():
275
- distance = self.energy_scale[-1]
276
- index = -1
277
- for ii, onset in enumerate(onsets):
278
- if onset < peak['position'] < onset+50:
279
- if distance > np.abs(peak['position'] - onset):
280
- distance = np.abs(peak['position'] - onset) # TODO: check whether absolute is good
281
- distance_onset = peak['position'] - onset
282
- index = ii
283
- if index >= 0:
284
- peak['associated_edge'] = edges[index][1] # check if more info is necessary
285
- peak['distance_to_onset'] = distance_onset
904
+ for key, peak in self.peaks['peaks'].items():
905
+ if key.isdigit():
906
+ distance = self.energy_scale[-1]
907
+ index = -1
908
+ for ii, onset in enumerate(onsets):
909
+ if onset < peak['position'] < onset+50:
910
+ if distance > np.abs(peak['position'] - onset):
911
+ distance = np.abs(peak['position'] - onset) # TODO: check whether absolute is good
912
+ distance_onset = peak['position'] - onset
913
+ index = ii
914
+ if index >= 0:
915
+ peak['associated_edge'] = edges[index][1] # check if more info is necessary
916
+ peak['distance_to_onset'] = distance_onset
286
917
 
287
918
  def find_white_lines(self):
288
- white_lines = {}
289
- for index, peak in self.peaks['peaks'].items():
290
- if index.isdigit():
291
- if 'associated_edge' in peak:
292
- if peak['associated_edge'][-2:] in ['L3', 'L2', 'M5', 'M4']:
293
- if peak['distance_to_onset'] < 10:
294
- area = np.sqrt(2 * np.pi) * peak['amplitude'] * np.abs(peak['width']/np.sqrt(2 * np.log(2)))
295
- if peak['associated_edge'] not in white_lines:
296
- white_lines[peak['associated_edge']] = 0.
297
- if area > 0:
298
- white_lines[peak['associated_edge']] += area # TODO: only positive ones?
299
- white_line_ratios = {}
300
- white_line_sum = {}
301
- for sym, area in white_lines.items():
302
- if sym[-2:] in ['L2', 'M4', 'M2']:
303
- if area > 0 and f"{sym[:-1]}{int(sym[-1]) + 1}" in white_lines:
304
- if white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"] > 0:
305
- white_line_ratios[f"{sym}/{sym[-2]}{int(sym[-1]) + 1}"] = area / white_lines[
306
- f"{sym[:-1]}{int(sym[-1]) + 1}"]
307
- white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] = (
308
- area + white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"])
309
-
310
- areal_density = 1.
311
- if 'edges' in self.dataset.metadata:
312
- for key, edge in self.dataset.metadata['edges'].items():
313
- if key.isdigit():
314
- if edge['element'] == sym.split('-')[0]:
315
- areal_density = edge['areal_density']
316
- break
317
- white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] /= areal_density
318
-
319
- self.peaks['white_lines'] = white_lines
320
- self.peaks['white_line_ratios'] = white_line_ratios
321
- self.peaks['white_line_sums'] = white_line_sum
322
- self.ui.wl_list = []
323
- self.ui.wls_list = []
324
- if len(self.peaks['white_line_ratios']) > 0:
325
- for key in self.peaks['white_line_ratios']:
326
- self.ui.wl_list.append(key)
327
- for key in self.peaks['white_line_sums']:
328
- self.ui.wls_list.append(key)
329
-
330
- self.ui.listwl.clear()
331
- self.ui.listwl.addItems(self.ui.wl_list)
332
- self.ui.listwl.setCurrentIndex(0)
333
- self.ui.unitswl.setText(f"{self.peaks['white_line_ratios'][self.ui.wl_list[0]]:.2f}")
334
-
335
- self.ui.listwls.clear()
336
- self.ui.listwls.addItems(self.ui.wls_list)
337
- self.ui.listwls.setCurrentIndex(0)
338
- self.ui.unitswls.setText(f"{self.peaks['white_line_sums'][self.ui.wls_list[0]]*1e6:.4f} ppm")
339
- else:
340
- self.ui.wl_list.append('Ratio')
341
- self.ui.wls_list.append('Sum')
342
-
343
- self.ui.listwl.clear()
344
- self.ui.listwl.addItems(self.ui.wl_list)
345
- self.ui.listwl.setCurrentIndex(0)
346
- self.ui.unitswl.setText('')
347
-
348
- self.ui.listwls.clear()
349
- self.ui.listwls.addItems(self.ui.wls_list)
350
- self.ui.listwls.setCurrentIndex(0)
351
- self.ui.unitswls.setText('')
919
+ if 'edges' in self.dataset.metadata:
920
+ white_lines = {}
921
+ for index, peak in self.peaks['peaks'].items():
922
+ if index.isdigit():
923
+ if 'associated_edge' in peak:
924
+ if peak['associated_edge'][-2:] in ['L3', 'L2', 'M5', 'M4']:
925
+ if peak['distance_to_onset'] < 10:
926
+ area = np.sqrt(2 * np.pi) * peak['amplitude'] * np.abs(peak['width']/np.sqrt(2 * np.log(2)))
927
+ if peak['associated_edge'] not in white_lines:
928
+ white_lines[peak['associated_edge']] = 0.
929
+ if area > 0:
930
+ white_lines[peak['associated_edge']] += area # TODO: only positive ones?
931
+ white_line_ratios = {}
932
+ white_line_sum = {}
933
+ for sym, area in white_lines.items():
934
+ if sym[-2:] in ['L2', 'M4', 'M2']:
935
+ if area > 0 and f"{sym[:-1]}{int(sym[-1]) + 1}" in white_lines:
936
+ if white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"] > 0:
937
+ white_line_ratios[f"{sym}/{sym[-2]}{int(sym[-1]) + 1}"] = area / white_lines[
938
+ f"{sym[:-1]}{int(sym[-1]) + 1}"]
939
+ white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] = (
940
+ area + white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"])
941
+
942
+ areal_density = 1.
943
+ if 'edges' in self.dataset.metadata:
944
+ for key, edge in self.dataset.metadata['edges'].items():
945
+ if key.isdigit():
946
+ if edge['element'] == sym.split('-')[0]:
947
+ areal_density = edge['areal_density']
948
+ break
949
+ white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] /= areal_density
950
+
951
+ self.peaks['white_lines'] = white_lines
952
+ self.peaks['white_line_ratios'] = white_line_ratios
953
+ self.peaks['white_line_sums'] = white_line_sum
954
+ self.ui.wl_list = []
955
+ self.ui.wls_list = []
956
+ if len(self.peaks['white_line_ratios']) > 0:
957
+ for key in self.peaks['white_line_ratios']:
958
+ self.ui.wl_list.append(key)
959
+ for key in self.peaks['white_line_sums']:
960
+ self.ui.wls_list.append(key)
961
+
962
+ self.ui.listwl.clear()
963
+ self.ui.listwl.addItems(self.ui.wl_list)
964
+ self.ui.listwl.setCurrentIndex(0)
965
+ self.ui.unitswl.setText(f"{self.peaks['white_line_ratios'][self.ui.wl_list[0]]:.2f}")
966
+
967
+ self.ui.listwls.clear()
968
+ self.ui.listwls.addItems(self.ui.wls_list)
969
+ self.ui.listwls.setCurrentIndex(0)
970
+ self.ui.unitswls.setText(f"{self.peaks['white_line_sums'][self.ui.wls_list[0]]*1e6:.4f} ppm")
971
+ else:
972
+ self.ui.wl_list.append('Ratio')
973
+ self.ui.wls_list.append('Sum')
974
+
975
+ self.ui.listwl.clear()
976
+ self.ui.listwl.addItems(self.ui.wl_list)
977
+ self.ui.listwl.setCurrentIndex(0)
978
+ self.ui.unitswl.setText('')
979
+
980
+ self.ui.listwls.clear()
981
+ self.ui.listwls.addItems(self.ui.wls_list)
982
+ self.ui.listwls.setCurrentIndex(0)
983
+ self.ui.unitswls.setText('')
352
984
 
353
985
  def find_peaks(self):
354
986
  number_of_peaks = int(str(self.ui.find_edit.displayText()).strip())
@@ -421,7 +1053,7 @@ if Qt_available:
421
1053
  self.peaks['peaks'][str(peak_index)]['width'] = value
422
1054
 
423
1055
  def on_list_enter(self):
424
- # self.setWindowTitle('list')
1056
+ self.setWindowTitle(f'list {self.sender}, {self.ui.list_model}')
425
1057
  if self.sender() == self.ui.list3:
426
1058
  if self.ui.list3.currentText().lower() == 'add peak':
427
1059
  peak_index = self.ui.list3.currentIndex()
@@ -439,7 +1071,16 @@ if Qt_available:
439
1071
  self.ui.unitswl.setText(f"{self.peaks['white_line_ratios'][self.ui.wl_list[wl_index]]:.2f}")
440
1072
  self.ui.listwls.setCurrentIndex(wl_index)
441
1073
  self.ui.unitswls.setText(f"{self.peaks['white_line_sums'][self.ui.wls_list[wl_index]] * 1e6:.4f} ppm")
442
-
1074
+ elif self.sender() == self.ui.list_model:
1075
+ self.setWindowTitle('list 1')
1076
+ if self.sender().currentIndex() == 1:
1077
+ if 'resolution_function' in self.datasets:
1078
+ self.setWindowTitle('list 2')
1079
+ self.dataset.metadata['model'] = np.array(self.datasets['resolution_function'])
1080
+ else:
1081
+ self.ui.list_model.setCurrentIndex(0)
1082
+ else:
1083
+ self.ui.list_model.setCurrentIndex(0)
443
1084
  def set_action(self):
444
1085
  pass
445
1086
  self.ui.edit1.editingFinished.connect(self.on_enter)
@@ -452,35 +1093,81 @@ if Qt_available:
452
1093
  self.ui.find_button.clicked.connect(self.find_peaks)
453
1094
  self.ui.smooth_button.clicked.connect(self.smooth)
454
1095
  self.ui.fit_button.clicked.connect(self.fit_peaks)
455
- self.ui.listwls.activated[str].connect(self.on_list_enter)
456
- self.ui.listwl.activated[str].connect(self.on_list_enter)
457
-
1096
+ if hasattr(self.ui, 'listwls'):
1097
+ self.ui.listwls.activated[str].connect(self.on_list_enter)
1098
+ self.ui.listwl.activated[str].connect(self.on_list_enter)
1099
+ else:
1100
+ self.ui.zl_button.clicked.connect(self.fit_zero_loss)
1101
+ self.ui.drude_button.clicked.connect(self.smooth)
1102
+ self.ui.list_model.activated[str].connect(self.on_list_enter)
1103
+
1104
+ def fit_zero_loss(self):
1105
+ """get shift of spectrum form zero-loss peak position"""
1106
+ zero_loss_fit_width=0.3
1107
+
1108
+ energy_scale = self.dataset.energy_loss
1109
+ zl_dataset = self.dataset.copy()
1110
+ zl_dataset.title = 'resolution_function'
1111
+ shifts = np.zeros(self.dataset.shape[0:2])
1112
+ zero_p = np.zeros([self.dataset.shape[0],self.dataset.shape[1],6])
1113
+ fwhm_p = np.zeros(self.dataset.shape[0:2])
1114
+ bin_x = bin_y = 1
1115
+ total_spec = int(self.dataset.shape[0]/bin_x)*int(self.dataset.shape[1]/bin_y)
1116
+ self.ui.progress.setMaximum(total_spec)
1117
+ self.ui.progress.setValue(0)
1118
+ zero_loss_fit_width=0.3
1119
+ ind = 0
1120
+ for x in range(self.dataset.shape[0]):
1121
+ for y in range(self.dataset.shape[1]):
1122
+ ind += 1
1123
+ self.ui.progress.setValue(ind)
1124
+ spectrum = self.dataset[x, y, :]
1125
+ fwhm, delta_e = eels_tools.fix_energy_scale(spectrum, energy_scale)
1126
+ z_loss, p_zl = eels_tools.resolution_function(energy_scale - delta_e, spectrum, zero_loss_fit_width)
1127
+ fwhm2, delta_e2 = eels_tools.fix_energy_scale(z_loss, energy_scale - delta_e)
1128
+ shifts[x, y] = delta_e + delta_e2
1129
+ zero_p[x,y,:] = p_zl
1130
+ zl_dataset[x,y] = z_loss
1131
+ fwhm_p[x,y] = fwhm2
1132
+
1133
+ zl_dataset.metadata['zero_loss'] = {'parameter': zero_p,
1134
+ 'shifts': shifts,
1135
+ 'fwhm': fwhm_p}
1136
+ self.dataset.metadata['zero_loss'] = {'parameter': zero_p,
1137
+ 'shifts': shifts,
1138
+ 'fwhm': fwhm_p}
1139
+
1140
+ self.datasets['resolution_function'] = zl_dataset
1141
+ self.update()
1142
+ self.plot()
1143
+
1144
+
458
1145
 
459
- def smooth(dataset, iterations, advanced_present):
460
- """Gaussian mixture model (non-Bayesian)
1146
+ def smooth(dataset, iterations, advanced_present):
1147
+ """Gaussian mixture model (non-Bayesian)
461
1148
 
462
- Fit lots of Gaussian to spectrum and let the program sort it out
463
- We sort the peaks by area under the Gaussians, assuming that small areas mean noise.
1149
+ Fit lots of Gaussian to spectrum and let the program sort it out
1150
+ We sort the peaks by area under the Gaussians, assuming that small areas mean noise.
464
1151
 
465
- """
1152
+ """
466
1153
 
467
- # TODO: add sensitivity to dialog and the two functions below
468
- peaks = dataset.metadata['peak_fit']
1154
+ # TODO: add sensitivity to dialog and the two functions below
1155
+ peaks = dataset.metadata['peak_fit']
469
1156
 
470
- if advanced_present and iterations > 1:
471
- peak_model, peak_out_list = advanced_eels_tools.smooth(dataset, peaks['fit_start'],
472
- peaks['fit_end'], iterations=iterations)
473
- else:
474
- peak_model, peak_out_list = eels.find_peaks(dataset, peaks['fit_start'], peaks['fit_end'])
475
- peak_out_list = [peak_out_list]
1157
+ if advanced_present and iterations > 1:
1158
+ peak_model, peak_out_list = advanced_eels_tools.smooth(dataset, peaks['fit_start'],
1159
+ peaks['fit_end'], iterations=iterations)
1160
+ else:
1161
+ peak_model, peak_out_list = eels_tools.find_peaks(dataset, peaks['fit_start'], peaks['fit_end'])
1162
+ peak_out_list = [peak_out_list]
476
1163
 
477
- flat_list = [item for sublist in peak_out_list for item in sublist]
478
- new_list = np.reshape(flat_list, [len(flat_list) // 3, 3])
479
- area = np.sqrt(2 * np.pi) * np.abs(new_list[:, 1]) * np.abs(new_list[:, 2] / np.sqrt(2 * np.log(2)))
480
- arg_list = np.argsort(area)[::-1]
481
- area = area[arg_list]
482
- peak_out_list = new_list[arg_list]
1164
+ flat_list = [item for sublist in peak_out_list for item in sublist]
1165
+ new_list = np.reshape(flat_list, [len(flat_list) // 3, 3])
1166
+ area = np.sqrt(2 * np.pi) * np.abs(new_list[:, 1]) * np.abs(new_list[:, 2] / np.sqrt(2 * np.log(2)))
1167
+ arg_list = np.argsort(area)[::-1]
1168
+ area = area[arg_list]
1169
+ peak_out_list = new_list[arg_list]
483
1170
 
484
- number_of_peaks = np.searchsorted(area * -1, -np.average(area))
1171
+ number_of_peaks = np.searchsorted(area * -1, -np.average(area))
485
1172
 
486
- return peak_model, peak_out_list, number_of_peaks
1173
+ return peak_model, peak_out_list, number_of_peaks