pyTEMlib 0.2020.11.1__py3-none-any.whl → 0.2024.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyTEMlib might be problematic. Click here for more details.

Files changed (60) hide show
  1. pyTEMlib/__init__.py +11 -11
  2. pyTEMlib/animation.py +631 -0
  3. pyTEMlib/atom_tools.py +240 -245
  4. pyTEMlib/config_dir.py +57 -33
  5. pyTEMlib/core_loss_widget.py +658 -0
  6. pyTEMlib/crystal_tools.py +1255 -0
  7. pyTEMlib/diffraction_plot.py +756 -0
  8. pyTEMlib/dynamic_scattering.py +293 -0
  9. pyTEMlib/eds_tools.py +609 -0
  10. pyTEMlib/eels_dialog.py +749 -491
  11. pyTEMlib/{interactive_eels.py → eels_dialog_utilities.py} +1199 -1177
  12. pyTEMlib/eels_tools.py +2031 -1698
  13. pyTEMlib/file_tools.py +1276 -560
  14. pyTEMlib/file_tools_qt.py +193 -0
  15. pyTEMlib/graph_tools.py +1166 -450
  16. pyTEMlib/graph_viz.py +449 -0
  17. pyTEMlib/image_dialog.py +158 -0
  18. pyTEMlib/image_dlg.py +146 -232
  19. pyTEMlib/image_tools.py +1399 -1028
  20. pyTEMlib/info_widget.py +933 -0
  21. pyTEMlib/interactive_image.py +1 -226
  22. pyTEMlib/kinematic_scattering.py +1196 -0
  23. pyTEMlib/low_loss_widget.py +176 -0
  24. pyTEMlib/microscope.py +61 -81
  25. pyTEMlib/peak_dialog.py +1047 -410
  26. pyTEMlib/peak_dlg.py +286 -242
  27. pyTEMlib/probe_tools.py +653 -207
  28. pyTEMlib/sidpy_tools.py +153 -136
  29. pyTEMlib/simulation_tools.py +104 -87
  30. pyTEMlib/version.py +6 -3
  31. pyTEMlib/xrpa_x_sections.py +20972 -0
  32. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/LICENSE +21 -21
  33. pyTEMlib-0.2024.9.0.dist-info/METADATA +92 -0
  34. pyTEMlib-0.2024.9.0.dist-info/RECORD +37 -0
  35. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/WHEEL +5 -5
  36. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/entry_points.txt +0 -1
  37. pyTEMlib/KinsCat.py +0 -2758
  38. pyTEMlib/__version__.py +0 -2
  39. pyTEMlib/data/TEMlibrc +0 -68
  40. pyTEMlib/data/edges_db.csv +0 -189
  41. pyTEMlib/data/edges_db.pkl +0 -0
  42. pyTEMlib/data/fparam.txt +0 -103
  43. pyTEMlib/data/microscopes.csv +0 -7
  44. pyTEMlib/data/microscopes.xml +0 -167
  45. pyTEMlib/data/path.txt +0 -1
  46. pyTEMlib/defaults_parser.py +0 -90
  47. pyTEMlib/dm3_reader.py +0 -613
  48. pyTEMlib/edges_db.py +0 -76
  49. pyTEMlib/eels_dlg.py +0 -224
  50. pyTEMlib/hdf_utils.py +0 -483
  51. pyTEMlib/image_tools1.py +0 -2194
  52. pyTEMlib/info_dialog.py +0 -237
  53. pyTEMlib/info_dlg.py +0 -202
  54. pyTEMlib/nion_reader.py +0 -297
  55. pyTEMlib/nsi_reader.py +0 -170
  56. pyTEMlib/structure_tools.py +0 -316
  57. pyTEMlib/test.py +0 -2072
  58. pyTEMlib-0.2020.11.1.dist-info/METADATA +0 -20
  59. pyTEMlib-0.2020.11.1.dist-info/RECORD +0 -45
  60. {pyTEMlib-0.2020.11.1.dist-info → pyTEMlib-0.2024.9.0.dist-info}/top_level.txt +0 -0
pyTEMlib/peak_dialog.py CHANGED
@@ -1,410 +1,1047 @@
1
- """
2
- PeakDialog for interactive_eels
3
- """
4
-
5
- from PyQt5 import QtCore, QtWidgets
6
-
7
- import numpy as np
8
- import scipy
9
- import scipy.optimize
10
- import scipy.signal
11
-
12
- import sidpy
13
- import pyTEMlib.file_tools as ft
14
- import pyTEMlib.eels_tools as eels
15
- import pyTEMlib.peak_dlg as peak_dlg
16
-
17
- advanced_present = True
18
- try:
19
- import advanced_eels_tools
20
- print('advanced EELS features enabled')
21
- except ModuleNotFoundError:
22
- advanced_present = False
23
-
24
- _version = .001
25
-
26
-
27
- class PeakFitDialog(QtWidgets.QDialog):
28
- """
29
- EELS Input Dialog for Peak-Fit Analysis
30
- """
31
-
32
- def __init__(self, dataset=None):
33
- super().__init__(None, QtCore.Qt.WindowStaysOnTopHint)
34
- # Create an instance of the GUI
35
- self.ui = peak_dlg.UiDialog(self)
36
-
37
- self.set_action()
38
-
39
- self.dataset = dataset
40
- self.energy_scale = np.array([])
41
- self.peak_out_list = []
42
- self.p_out = []
43
- self.axis = None
44
- self.show_regions = False
45
- self.show()
46
-
47
- if dataset is None:
48
- # make a dummy dataset
49
- dataset = ft.make_dummy_dataset('spectrum')
50
-
51
- if not isinstance(dataset, sidpy.Dataset):
52
- raise TypeError('dataset has to be a sidpy dataset')
53
- self.dataset = dataset
54
- self.spec_dim = ft.get_dimensions_by_type('spectral', dataset)
55
- if len(self.spec_dim) != 1:
56
- raise TypeError('We need exactly one SPECTRAL dimension')
57
- self.spec_dim = self.spec_dim[0]
58
- self.energy_scale = self.spec_dim[1].values.copy()
59
-
60
- if 'peak_fit' not in self.dataset.metadata:
61
- self.dataset.metadata['peak_fit'] = {}
62
- if 'edges' in self.dataset.metadata:
63
- if 'fit_area' in self.dataset.metadata['edges']:
64
- self.dataset.metadata['peak_fit']['fit_start'] = \
65
- self.dataset.metadata['edges']['fit_area']['fit_start']
66
- self.dataset.metadata['peak_fit']['fit_end'] = self.dataset.metadata['edges']['fit_area']['fit_end']
67
- self.dataset.metadata['peak_fit']['peaks'] = {'0': {'position': self.energy_scale[1],
68
- 'amplitude': 1000.0, 'width': 1.0,
69
- 'type': 'Gauss', 'asymmetry': 0}}
70
-
71
- self.peaks = self.dataset.metadata['peak_fit']
72
- if 'fit_start' not in self.peaks:
73
- self.peaks['fit_start'] = self.energy_scale[1]
74
- self.peaks['fit_end'] = self.energy_scale[-2]
75
-
76
- if 'peak_model' in self.peaks:
77
- self.peak_model = self.peaks['peak_model']
78
- self.model = self.peak_model
79
- if 'edge_model' in self.peaks:
80
- self.model = self.model + self.peaks['edge_model']
81
- else:
82
- self.model = np.array([])
83
- self.peak_model = np.array([])
84
- if 'peak_out_list' in self.peaks:
85
- self.peak_out_list = self.peaks['peak_out_list']
86
- self.set_peak_list()
87
-
88
- self.update()
89
- self.dataset.plot()
90
- if hasattr(self.dataset.view, 'axes'):
91
- self.axis = self.dataset.view.axes[-1]
92
- elif hasattr(self.dataset.view, 'axis'):
93
- self.axis = self.dataset.view.axis
94
- self.figure = self.axis.figure
95
-
96
- if not advanced_present:
97
- self.ui.iteration_list = ['0']
98
- self.ui.smooth_list.clear()
99
- self.ui.smooth_list.addItems(self.ui.iteration_list)
100
- self.ui.smooth_list.setCurrentIndex(0)
101
- self.plot()
102
-
103
- def update(self):
104
- # self.setWindowTitle('update')
105
- self.ui.edit1.setText(f"{self.peaks['fit_start']:.2f}")
106
- self.ui.edit2.setText(f"{self.peaks['fit_end']:.2f}")
107
-
108
- peak_index = self.ui.list3.currentIndex()
109
- if str(peak_index) not in self.peaks['peaks']:
110
- self.peaks['peaks'][str(peak_index)] = {'position': self.energy_scale[1], 'amplitude': 1000.0,
111
- 'width': 1.0, 'type': 'Gauss', 'asymmetry': 0}
112
- self.ui.list4.setCurrentText(self.peaks['peaks'][str(peak_index)]['type'])
113
- if 'associated_edge' in self.peaks['peaks'][str(peak_index)]:
114
- self.ui.unit3.setText(self.peaks['peaks'][str(peak_index)]['associated_edge'])
115
- else:
116
- self.ui.unit3.setText('')
117
- self.ui.edit5.setText(f"{self.peaks['peaks'][str(peak_index)]['position']:.2f}")
118
- self.ui.edit6.setText(f"{self.peaks['peaks'][str(peak_index)]['amplitude']:.2f}")
119
- self.ui.edit7.setText(f"{self.peaks['peaks'][str(peak_index)]['width']:.2f}")
120
- if 'asymmetry' not in self.peaks['peaks'][str(peak_index)]:
121
- self.peaks['peaks'][str(peak_index)]['asymmetry'] = 0.
122
- self.ui.edit8.setText(f"{self.peaks['peaks'][str(peak_index)]['asymmetry']:.2f}")
123
-
124
- def plot(self):
125
- spec_dim = ft.get_dimensions_by_type(sidpy.DimensionTypes.SPECTRAL, self.dataset)
126
- spec_dim = spec_dim[0]
127
- self.energy_scale = spec_dim[1].values
128
- if self.dataset.data_type == sidpy.DataTypes.SPECTRAL_IMAGE:
129
- spectrum = self.dataset.view.get_spectrum()
130
- self.axis = self.dataset.view.axes[1]
131
- else:
132
- spectrum = np.array(self.dataset)
133
- self.axis = self.dataset.view.axis
134
-
135
- x_limit = self.axis.get_xlim()
136
- y_limit = self.axis.get_ylim()
137
- self.axis.clear()
138
-
139
- self.axis.plot(self.energy_scale, spectrum, label='spectrum')
140
- if len(self.model) > 1:
141
- self.axis.plot(self.energy_scale, self.model, label='model')
142
- self.axis.plot(self.energy_scale, spectrum - self.model, label='difference')
143
- self.axis.plot(self.energy_scale, (spectrum - self.model) / np.sqrt(spectrum), label='Poisson')
144
- self.axis.legend()
145
- self.axis.set_xlim(x_limit)
146
- self.axis.set_ylim(y_limit)
147
- self.axis.figure.canvas.draw_idle()
148
-
149
- for index, peak in self.peaks['peaks'].items():
150
- p = [peak['position'], peak['amplitude'], peak['width']]
151
- self.axis.plot(self.energy_scale, eels.gauss(self.energy_scale, p))
152
-
153
- def fit_peaks(self):
154
- p_in = []
155
- for key, peak in self.peaks['peaks'].items():
156
- if key.isdigit():
157
- p_in.append(peak['position'])
158
- p_in.append(peak['amplitude'])
159
- p_in.append(peak['width'])
160
- if self.dataset.data_type == sidpy.DataTypes.SPECTRAL_IMAGE:
161
- spectrum = self.dataset.view.get_spectrum()
162
- else:
163
- spectrum = np.array(self.dataset)
164
- energy_scale = np.array(self.energy_scale)
165
- start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
166
- end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
167
-
168
- energy_scale = self.energy_scale[start_channel:end_channel]
169
- if self.energy_scale[0] > 0:
170
- if 'edges' not in self.dataset.metadata:
171
- return
172
- if 'model' not in self.dataset.metadata['edges']:
173
- return
174
- model = self.dataset.metadata['edges']['model']['spectrum'][start_channel:end_channel]
175
-
176
- else:
177
- model = np.zeros(end_channel - start_channel)
178
-
179
- difference = np.array(spectrum[start_channel:end_channel] - model)
180
-
181
- [self.p_out, _] = scipy.optimize.leastsq(eels.residuals_smooth, np.array(p_in), ftol=1e-3,
182
- args=(energy_scale, difference, False))
183
- self.peak_model = np.zeros(len(self.energy_scale))
184
- fit = eels.model_smooth(energy_scale, self.p_out, False)
185
- self.peak_model[start_channel:end_channel] = fit
186
- if self.energy_scale[0] > 0:
187
- self.model = self.dataset.metadata['edges']['model']['spectrum']
188
- else:
189
- self.model = np.zeros(len(self.energy_scale))
190
-
191
- self.dataset.metadata['peak_fit']['edge_model'] = self.model
192
- self.model = self.model + self.peak_model
193
- self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
194
-
195
- for key, peak in self.peaks['peaks'].items():
196
- if key.isdigit():
197
- p_index = int(key)*3
198
- self.peaks['peaks'][key] = {'position': self.p_out[p_index],
199
- 'amplitude': self.p_out[p_index+1],
200
- 'width': self.p_out[p_index+2],
201
- 'associated_edge': ''}
202
- self.update()
203
- self.plot()
204
-
205
- def smooth(self):
206
- iterations = int(self.ui.smooth_list.currentIndex())
207
-
208
- # TODO: add sensitivity to dialog and the two functions below
209
- if advanced_present:
210
- self.peak_model, self.peak_out_list = advanced_eels_tools.smooth(self.dataset,
211
- self.peaks['fit_start'],
212
- self.peaks['fit_end'],
213
- iterations=iterations)
214
- else:
215
- self.peak_model, peak_out_list = eels.find_peaks(self.dataset, self.peaks['fit_start'],
216
- self.peaks['fit_end'])
217
- self.peak_out_list = [peak_out_list]
218
- spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
219
- if spec_dim[1][0] > 0:
220
- self.model = self.dataset.metadata['edges']['model']['spectrum']
221
- else:
222
- self.model = np.zeros(len(spec_dim[1]))
223
-
224
- self.dataset.metadata['peak_fit']['edge_model'] = self.model
225
- self.model = self.model + self.peak_model
226
- self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
227
- self.dataset.metadata['peak_fit']['peak_out_list'] = self.peak_out_list
228
-
229
- self.plot()
230
-
231
- def find_associated_edges(self):
232
- onsets = []
233
- edges = []
234
- if 'edges' in self.dataset.metadata:
235
- for key, edge in self.dataset.metadata['edges'].items():
236
- if key.isdigit():
237
- element = edge['element']
238
- for sym in edge['all_edges']: # TODO: Could be replaced with exclude
239
- onsets.append(edge['all_edges'][sym]['onset'] + edge['chemical_shift'])
240
- # if 'sym' == edge['symmetry']:
241
- edges.append([key, f"{element}-{sym}", onsets[-1]])
242
- for key, peak in self.peaks['peaks'].items():
243
- if key.isdigit():
244
- distance = self.energy_scale[-1]
245
- index = -1
246
- for ii, onset in enumerate(onsets):
247
- if onset-5 < peak['position'] < onset+50:
248
- if distance > np.abs(peak['position'] - onset):
249
- distance = np.abs(peak['position'] - onset) # TODO: check whether absolute is good
250
- index = ii
251
- if index > 0:
252
- peak['associated_edge'] = edges[index][1] # check if more info is necessary
253
-
254
- def find_white_lines(self):
255
- white_lines = {}
256
- for index, peak in self.peaks['peaks'].items():
257
- if index.isdigit():
258
- if 'associated_edge' in peak:
259
- if peak['associated_edge'][-2:] in ['L3', 'L2', 'M5', 'M4']:
260
- area = np.sqrt(2 * np.pi) * peak['amplitude'] * np.abs(peak['width'] / np.sqrt(2 * np.log(2)))
261
- if peak['associated_edge'] not in white_lines:
262
- white_lines[peak['associated_edge']] = 0.
263
- white_lines[peak['associated_edge']] += area # TODO: only positive ones?
264
- white_line_ratios = {}
265
- white_line_sum = {}
266
- for sym, area in white_lines.items():
267
- if sym[-2:] in ['L2', 'M4', 'M2']:
268
- if area > 0 and f"{sym[:-1]}{int(sym[-1]) + 1}" in white_lines:
269
- if white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"] > 0:
270
- white_line_ratios[f"{sym}/{sym[-2]}{int(sym[-1]) + 1}"] = area / white_lines[
271
- f"{sym[:-1]}{int(sym[-1]) + 1}"]
272
- white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] = (
273
- area + white_lines[f"{sym[:-1]}{int(sym[-1]) + 1}"])
274
-
275
- areal_density = 1.
276
- if 'edges' in self.dataset.metadata:
277
- for key, edge in self.dataset.metadata['edges'].items():
278
- if key.isdigit():
279
- if edge['element'] == sym.split('-')[0]:
280
- areal_density = edge['areal_density']
281
- break
282
- white_line_sum[f"{sym}+{sym[-2]}{int(sym[-1]) + 1}"] /= areal_density
283
-
284
- self.peaks['white_lines'] = white_lines
285
- self.peaks['white_line_ratios'] = white_line_ratios
286
- self.peaks['white_line_sums'] = white_line_sum
287
- self.ui.wl_list = []
288
- self.ui.wls_list = []
289
- if len(self.peaks['white_line_ratios']) > 0:
290
- for key in self.peaks['white_line_ratios']:
291
- self.ui.wl_list.append(key)
292
- for key in self.peaks['white_line_sums']:
293
- self.ui.wls_list.append(key)
294
-
295
- self.ui.listwl.clear()
296
- self.ui.listwl.addItems(self.ui.wl_list)
297
- self.ui.listwl.setCurrentIndex(0)
298
- self.ui.unitswl.setText(f"{self.peaks['white_line_ratios'][self.ui.wl_list[0]]:.2f}")
299
-
300
- self.ui.listwls.clear()
301
- self.ui.listwls.addItems(self.ui.wls_list)
302
- self.ui.listwls.setCurrentIndex(0)
303
- self.ui.unitswls.setText(f"{self.peaks['white_line_sums'][self.ui.wls_list[0]]*1e6:.4f} ppm")
304
- else:
305
- self.ui.wl_list.append('Ratio')
306
- self.ui.wls_list.append('Sum')
307
-
308
- self.ui.listwl.clear()
309
- self.ui.listwl.addItems(self.ui.wl_list)
310
- self.ui.listwl.setCurrentIndex(0)
311
- self.ui.unitswl.setText('')
312
-
313
- self.ui.listwls.clear()
314
- self.ui.listwls.addItems(self.ui.wls_list)
315
- self.ui.listwls.setCurrentIndex(0)
316
- self.ui.unitswls.setText('')
317
-
318
- def find_peaks(self):
319
- number_of_peaks = int(str(self.ui.find_edit.displayText()).strip())
320
-
321
- flat_list = [item for sublist in self.peak_out_list for item in sublist]
322
- new_list = np.reshape(flat_list, [len(flat_list) // 3, 3])
323
- arg_list = np.argsort(np.abs(new_list[:, 1]))
324
-
325
- self.ui.peak_list = []
326
- self.peaks['peaks'] = {}
327
- for i in range(number_of_peaks):
328
- self.ui.peak_list.append(f'Peak {i+1}')
329
- p = new_list[arg_list[-i-1]]
330
- self.peaks['peaks'][str(i)] = {'position': p[0], 'amplitude': p[1], 'width': p[2], 'type': 'Gauss',
331
- 'asymmetry': 0}
332
-
333
- self.ui.peak_list.append(f'add peak')
334
- self.ui.list3.clear()
335
- self.ui.list3.addItems(self.ui.peak_list)
336
- self.ui.list3.setCurrentIndex(0)
337
- self.find_associated_edges()
338
- self.find_white_lines()
339
-
340
- self.update()
341
- self.plot()
342
-
343
- def set_peak_list(self):
344
- self.ui.peak_list = []
345
- if 'peaks' not in self.peaks:
346
- self.peaks['peaks'] = {}
347
- key = 0
348
- for key in self.peaks['peaks']:
349
- if key.isdigit():
350
- self.ui.peak_list.append(f'Peak {int(key) + 1}')
351
- self.ui.find_edit.setText(str(int(key) + 1))
352
- self.ui.peak_list.append(f'add peak')
353
- self.ui.list3.clear()
354
- self.ui.list3.addItems(self.ui.peak_list)
355
- self.ui.list3.setCurrentIndex(0)
356
-
357
- def on_enter(self):
358
- if self.sender() == self.ui.edit1:
359
- value = float(str(self.ui.edit1.displayText()).strip())
360
- if value < self.energy_scale[0]:
361
- value = self.energy_scale[0]
362
- if value > self.energy_scale[-5]:
363
- value = self.energy_scale[-5]
364
- self.peaks['fit_start'] = value
365
- self.ui.edit1.setText(str(self.peaks['fit_start']))
366
- elif self.sender() == self.ui.edit2:
367
- value = float(str(self.ui.edit2.displayText()).strip())
368
- if value < self.energy_scale[5]:
369
- value = self.energy_scale[5]
370
- if value > self.energy_scale[-1]:
371
- value = self.energy_scale[-1]
372
- self.peaks['fit_end'] = value
373
- self.ui.edit2.setText(str(self.peaks['fit_end']))
374
- elif self.sender() == self.ui.edit5:
375
- value = float(str(self.ui.edit5.displayText()).strip())
376
- peak_index = self.ui.list3.currentIndex()
377
- self.peaks['peaks'][str(peak_index)]['position'] = value
378
- elif self.sender() == self.ui.edit6:
379
- value = float(str(self.ui.edit6.displayText()).strip())
380
- peak_index = self.ui.list3.currentIndex()
381
- self.peaks['peaks'][str(peak_index)]['amplitude'] = value
382
- elif self.sender() == self.ui.edit7:
383
- value = float(str(self.ui.edit7.displayText()).strip())
384
- peak_index = self.ui.list3.currentIndex()
385
- self.peaks['peaks'][str(peak_index)]['width'] = value
386
-
387
- def on_list_enter(self):
388
- # self.setWindowTitle('list')
389
- if self.sender() == self.ui.list3:
390
- if self.ui.list3.currentText().lower() == 'add peak':
391
- peak_index = self.ui.list3.currentIndex()
392
- self.ui.list3.insertItem(peak_index, f'Peak {peak_index+1}')
393
- self.peaks['peaks'][str(peak_index+1)] = {'position': self.energy_scale[1],
394
- 'amplitude': 1000.0, 'width': 1.0,
395
- 'type': 'Gauss', 'asymmetry': 0}
396
- self.ui.list3.setCurrentIndex(peak_index)
397
- self.update()
398
-
399
- def set_action(self):
400
- pass
401
- self.ui.edit1.editingFinished.connect(self.on_enter)
402
- self.ui.edit2.editingFinished.connect(self.on_enter)
403
- self.ui.edit5.editingFinished.connect(self.on_enter)
404
- self.ui.edit6.editingFinished.connect(self.on_enter)
405
- self.ui.edit7.editingFinished.connect(self.on_enter)
406
- self.ui.edit8.editingFinished.connect(self.on_enter)
407
- self.ui.list3.activated[str].connect(self.on_list_enter)
408
- self.ui.find_button.clicked.connect(self.find_peaks)
409
- self.ui.smooth_button.clicked.connect(self.smooth)
410
- self.ui.fit_button.clicked.connect(self.fit_peaks)
1
+ """
2
+ EELS Input Dialog for ELNES Analysis
3
+ """
4
+ from os import error
5
+ Qt_available = True
6
+ try:
7
+ from PyQt5 import QtCore, QtWidgets
8
+ except:
9
+ Qt_available = False
10
+ # print('Qt dialogs are not available')
11
+
12
+ import numpy as np
13
+ import scipy
14
+ import scipy.optimize
15
+ import scipy.signal
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
+
23
+ import sidpy
24
+ import pyTEMlib.file_tools as ft
25
+ from pyTEMlib import eels_tools
26
+ from pyTEMlib import peak_dlg
27
+ from pyTEMlib import eels_dialog_utilities
28
+
29
+ advanced_present = True
30
+ try:
31
+ import advanced_eels_tools
32
+ print('advanced EELS features enabled')
33
+ except ModuleNotFoundError:
34
+ advanced_present = False
35
+
36
+ _version = .001
37
+
38
+ def get_sidebar():
39
+ side_bar = ipywidgets.GridspecLayout(16, 3, width='auto', grid_gap="0px")
40
+ row = 0
41
+ side_bar[row, :3] = ipywidgets.Button(description='Fit Area',
42
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
43
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
44
+ row += 1
45
+ side_bar[row, :2] = ipywidgets.FloatText(value=7.5,description='Fit Start:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
46
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
47
+ row += 1
48
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Fit End:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
49
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='20px'))
50
+
51
+ row += 1
52
+ side_bar[row, :3] = ipywidgets.Button(description='Peak Finding',
53
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
54
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
55
+
56
+ row += 1
57
+
58
+
59
+ side_bar[row, :2] = ipywidgets.Dropdown(
60
+ options=[('0', 0), ('1', 1), ('2', 2), ('3', 3), ('4', 4)],
61
+ value=0,
62
+ description='Peaks:',
63
+ disabled=False,
64
+ layout=ipywidgets.Layout(width='200px'))
65
+
66
+ side_bar[row, 2] = ipywidgets.Button(
67
+ description='Smooth',
68
+ disabled=False,
69
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
70
+ tooltip='Do Gaussian Mixing',
71
+ layout=ipywidgets.Layout(width='100px'))
72
+
73
+ row += 1
74
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Number:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
75
+ side_bar[row, 2] = ipywidgets.Button(
76
+ description='Find',
77
+ disabled=False,
78
+ button_style='', # 'success', 'info', 'warning', 'danger' or ''
79
+ tooltip='Find first peaks from Gaussian mixture',
80
+ layout=ipywidgets.Layout(width='100px'))
81
+
82
+ row += 1
83
+
84
+ side_bar[row, :3] = ipywidgets.Button(description='Peaks',
85
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
86
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
87
+ row += 1
88
+ side_bar[row, :2] = ipywidgets.Dropdown(
89
+ options=[('Peak 1', 0), ('add peak', -1)],
90
+ value=0,
91
+ description='Peaks:',
92
+ disabled=False,
93
+ layout=ipywidgets.Layout(width='200px'))
94
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="", layout=ipywidgets.Layout(width='100px'))
95
+ row += 1
96
+ side_bar[row, :2] = ipywidgets.Dropdown(
97
+ options=[ 'Gauss', 'Lorentzian', 'Drude', 'Zero-Loss'],
98
+ value='Gauss',
99
+ description='Symmetry:',
100
+ disabled=False,
101
+ layout=ipywidgets.Layout(width='200px'))
102
+ row += 1
103
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Position:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
104
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
105
+ row += 1
106
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Amplitude:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
107
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
108
+ row += 1
109
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Width FWHM:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
110
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="eV", layout=ipywidgets.Layout(width='100px'))
111
+ row += 1
112
+ side_bar[row, :2] = ipywidgets.FloatText(value=0.1, description='Asymmetry:', disabled=False, color='black', layout=ipywidgets.Layout(width='200px'))
113
+ side_bar[row, 2] = ipywidgets.widgets.Label(value="a.u.", layout=ipywidgets.Layout(width='100px'))
114
+ row += 1
115
+
116
+ side_bar[row, :3] = ipywidgets.Button(description='White-Line',
117
+ layout=ipywidgets.Layout(width='auto', grid_area='header'),
118
+ style=ipywidgets.ButtonStyle(button_color='lightblue'))
119
+
120
+ row += 1
121
+ side_bar[row, :2] = ipywidgets.Dropdown(
122
+ options=[('None', 0)],
123
+ value=0,
124
+ description='Ratio:',
125
+ disabled=False,
126
+ layout=ipywidgets.Layout(width='200px'))
127
+ side_bar[row, 2] = ipywidgets.widgets.Label(value=" ", layout=ipywidgets.Layout(width='100px'))
128
+ row += 1
129
+ side_bar[row, :2] = ipywidgets.Dropdown(
130
+ options=[('None', 0)],
131
+ value=0,
132
+ description= 'Sum:',
133
+ disabled=False,
134
+ layout=ipywidgets.Layout(width='200px'))
135
+ side_bar[row, 2] = ipywidgets.widgets.Label(value=" ", layout=ipywidgets.Layout(width='100px'))
136
+ return side_bar
137
+
138
+ class PeakFitWidget(object):
139
+ def __init__(self, datasets=None):
140
+ self.datasets = datasets
141
+ if not isinstance(datasets, dict):
142
+ raise TypeError('need dictioary of sidpy datasets')
143
+
144
+ self.sidebar = get_sidebar()
145
+ self.key = list(self.datasets)[0]
146
+ self.dataset = datasets[self.key]
147
+ if not isinstance(self.dataset, sidpy.Dataset):
148
+ raise TypeError('dataset or first item inhas to be a sidpy dataset')
149
+
150
+ self.model = np.array([])
151
+ self.y_scale = 1.0
152
+ self.change_y_scale = 1.0
153
+ self.spectrum_ll = None
154
+ self.low_loss_key = None
155
+
156
+ self.peaks = {}
157
+
158
+ self.show_regions = False
159
+
160
+ self.set_dataset()
161
+
162
+ self.app_layout = ipywidgets.AppLayout(
163
+ left_sidebar=self.sidebar,
164
+ center=self.view.panel,
165
+ footer=None,#message_bar,
166
+ pane_heights=[0, 10, 0],
167
+ pane_widths=[4, 10, 0],
168
+ )
169
+ display(self.app_layout)
170
+ self.set_action()
171
+
172
+ def line_select_callback(self, x_min, x_max):
173
+ self.start_cursor.value = np.round(x_min,3)
174
+ self.end_cursor.value = np.round(x_max, 3)
175
+ self.start_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.start_cursor.value)
176
+ self.end_channel = np.searchsorted(self.datasets[self.key].energy_loss, self.end_cursor.value)
177
+
178
+
179
+ def set_peak_list(self):
180
+ self.peak_list = []
181
+ if 'peaks' not in self.peaks:
182
+ self.peaks['peaks'] = {}
183
+ key = 0
184
+ for key in self.peaks['peaks']:
185
+ if key.isdigit():
186
+ self.peak_list.append((f'Peak {int(key) + 1}', int(key)))
187
+ self.peak_list.append(('add peak', -1))
188
+ #self.sidebar[7, 0].options = self.peak_list
189
+ #self.sidebar[7, 0].value = 0
190
+
191
+
192
+ def plot(self, scale=True):
193
+
194
+ self.view.change_y_scale = self.change_y_scale
195
+ self.view.y_scale = self.y_scale
196
+ self.energy_scale = self.dataset.energy_loss.values
197
+
198
+ if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
199
+ spectrum = self.dataset.view.get_spectrum()
200
+ else:
201
+ spectrum = self.dataset
202
+ if len(self.model) > 1:
203
+ additional_spectra = {'model': self.model,
204
+ 'difference': spectrum-self.model}
205
+ else:
206
+ additional_spectra = {}
207
+ if 'peaks' in self.peaks:
208
+ if len(self.peaks)>0:
209
+ for index, peak in self.peaks['peaks'].items(): # ll
210
+ p = [peak['position'], peak['amplitude'], peak['width']]
211
+ additional_spectra[f'peak {index}']= eels_tools.gauss(self.energy_scale, p)
212
+ self.view.plot(scale=True, additional_spectra=additional_spectra )
213
+ self.change_y_scale = 1.
214
+
215
+ self.view.figure.canvas.draw_idle()
216
+
217
+
218
+ def set_dataset(self, index=0):
219
+ self.spec_dim = ft.get_dimensions_by_type('spectral', self.dataset)
220
+ if len(self.spec_dim) != 1:
221
+ raise TypeError('We need exactly one SPECTRAL dimension')
222
+ self.spec_dim = self.spec_dim[0]
223
+ self.energy_scale = self.spec_dim[1]
224
+
225
+ self.y_scale = 1.0
226
+ self.change_y_scale = 1.0
227
+
228
+ if 'peak_fit' not in self.dataset.metadata:
229
+ self.dataset.metadata['peak_fit'] = {}
230
+ if 'edges' in self.dataset.metadata:
231
+ if 'fit_area' in self.dataset.metadata['edges']:
232
+ self.dataset.metadata['peak_fit']['fit_start'] = self.dataset.metadata['edges']['fit_area']['fit_start']
233
+ self.dataset.metadata['peak_fit']['fit_end'] = self.dataset.metadata['edges']['fit_area']['fit_end']
234
+ self.dataset.metadata['peak_fit']['peaks'] = {'0': {'position': self.energy_scale[1],
235
+ 'amplitude': 1000.0, 'width': 1.0,
236
+ 'type': 'Gauss', 'asymmetry': 0}}
237
+
238
+ self.peaks = self.dataset.metadata['peak_fit']
239
+ if 'fit_start' not in self.peaks:
240
+ self.peaks['fit_start'] = self.energy_scale[1]
241
+ if 'fit_end' not in self.peaks:
242
+ self.peaks['fit_end'] = self.energy_scale[-2]
243
+
244
+ if 'peak_model' in self.peaks:
245
+ self.peak_model = self.peaks['peak_model']
246
+ self.model = self.peak_model
247
+ if 'edge_model' in self.peaks:
248
+ self.model = self.model + self.peaks['edge_model']
249
+ else:
250
+ self.model = np.array([])
251
+ self.peak_model = np.array([])
252
+ if 'peak_out_list' in self.peaks:
253
+ self.peak_out_list = self.peaks['peak_out_list']
254
+ self.set_peak_list()
255
+
256
+ # check whether a core loss analysis has been done previously
257
+ if not hasattr(self, 'core_loss') and 'edges' in self.dataset.metadata:
258
+ self.core_loss = True
259
+ else:
260
+ self.core_loss = False
261
+
262
+ self.update()
263
+ if self.dataset.data_type.name =='SPECTRAL_IMAGE':
264
+ self.view = eels_dialog_utilities.SIPlot(self.dataset)
265
+ else:
266
+ self.view = eels_dialog_utilities.SpectrumPlot(self.dataset)
267
+ self.y_scale = 1.0
268
+ self.change_y_scale = 1.0
269
+
270
+ def set_fit_area(self, value):
271
+
272
+ self.peaks['fit_start'] = self.sidebar[1, 0].value
273
+ self.peaks['fit_end'] = self.sidebar[2, 0].value
274
+
275
+ self.plot()
276
+
277
+ def set_y_scale(self, value):
278
+ self.change_y_scale = 1/self.y_scale
279
+ if self.sidebar[12, 0].value:
280
+ dispersion = self.energy_scale[1] - self.energy_scale[0]
281
+ self.y_scale = 1/self.dataset.metadata['experiment']['flux_ppm'] * dispersion
282
+ else:
283
+ self.y_scale = 1.0
284
+
285
+ self.change_y_scale *= self.y_scale
286
+ self.update()
287
+ self.plot()
288
+
289
+ def update(self, index=0):
290
+
291
+ # self.setWindowTitle('update')
292
+ self.sidebar[1, 0].value = self.peaks['fit_start']
293
+ self.sidebar[2, 0].value = self.peaks['fit_end']
294
+
295
+ peak_index = self.sidebar[7, 0].value
296
+ self.peak_index = self.sidebar[7, 0].value
297
+ if str(peak_index) not in self.peaks['peaks']:
298
+ self.peaks['peaks'][str(peak_index)] = {'position': self.energy_scale[1], 'amplitude': 1000.0,
299
+ 'width': 1.0, 'type': 'Gauss', 'asymmetry': 0}
300
+ self.sidebar[8, 0].value = self.peaks['peaks'][str(peak_index)]['type']
301
+ if 'associated_edge' in self.peaks['peaks'][str(peak_index)]:
302
+ self.sidebar[7, 2].value = (self.peaks['peaks'][str(peak_index)]['associated_edge'])
303
+ else:
304
+ self.sidebar[7, 2].value = ''
305
+ self.sidebar[9, 0].value = self.peaks['peaks'][str(peak_index)]['position']
306
+ self.sidebar[10, 0].value = self.peaks['peaks'][str(peak_index)]['amplitude']
307
+ self.sidebar[11, 0].value = self.peaks['peaks'][str(peak_index)]['width']
308
+ if 'asymmetry' not in self.peaks['peaks'][str(peak_index)]:
309
+ self.peaks['peaks'][str(peak_index)]['asymmetry'] = 0.
310
+ self.sidebar[12, 0].value = self.peaks['peaks'][str(peak_index)]['asymmetry']
311
+
312
+
313
+ def fit_peaks(self, value = 0):
314
+ """Fit spectrum with peaks given in peaks dictionary"""
315
+ # print('Fitting peaks...')
316
+ p_in = []
317
+ for key, peak in self.peaks['peaks'].items():
318
+ if key.isdigit():
319
+ p_in.append(peak['position'])
320
+ p_in.append(peak['amplitude'])
321
+ p_in.append(peak['width'])
322
+
323
+ spectrum = np.array(self.dataset)
324
+
325
+ # set the energy scale and fit start and end points
326
+ energy_scale = np.array(self.energy_scale)
327
+ start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
328
+ end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
329
+
330
+ energy_scale = self.energy_scale[start_channel:end_channel]
331
+ # select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
332
+ if 'model' in self.dataset.metadata:
333
+ model = self.dataset.metadata['model'][start_channel:end_channel]
334
+ elif self.core_loss:
335
+ # print('Core loss model found. Fitting on top of the model.')
336
+ model = self.dataset.metadata['edges']['model']['spectrum'][start_channel:end_channel]
337
+ else:
338
+ # print('No core loss model found. Fitting to the full spectrum.')
339
+ model = np.zeros(end_channel - start_channel)
340
+
341
+ # if we have a core loss model we will only fit the difference between the model and the data.
342
+ difference = np.array(spectrum[start_channel:end_channel] - model)
343
+
344
+ # find the optimum fitting parameters
345
+ [self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals_smooth, np.array(p_in), ftol=1e-3,
346
+ args=(energy_scale, difference, False))
347
+
348
+ # construct the fit data from the optimized parameters
349
+ self.peak_model = np.zeros(len(self.energy_scale))
350
+ self.model = np.zeros(len(self.energy_scale))
351
+ self.model[start_channel:end_channel] = model
352
+ fit = eels_tools.model_smooth(energy_scale, self.p_out, False)
353
+ self.peak_model[start_channel:end_channel] = fit
354
+ self.dataset.metadata['peak_fit']['edge_model'] = self.model
355
+ self.model = self.model + self.peak_model
356
+ self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
357
+
358
+ for key, peak in self.peaks['peaks'].items():
359
+ if key.isdigit():
360
+ p_index = int(key)*3
361
+ self.peaks['peaks'][key] = {'position': self.p_out[p_index],
362
+ 'amplitude': self.p_out[p_index+1],
363
+ 'width': self.p_out[p_index+2],
364
+ 'type': 'Gauss',
365
+ 'associated_edge': ''}
366
+
367
+ eels_tools.find_associated_edges(self.dataset)
368
+ self.find_white_lines()
369
+ self.update()
370
+ self.plot()
371
+
372
+
373
+
374
+ def find_white_lines(self):
375
+ eels_tools.find_white_lines(self.dataset)
376
+ self.wl_list = []
377
+ self.wls_list = []
378
+ if 'white_line_ratios' in self.dataset.metadata['peak_fit']:
379
+ if len(self.dataset.metadata['peak_fit']['white_line_ratios']) > 0:
380
+ for key in self.dataset.metadata['peak_fit']['white_line_ratios']:
381
+ self.wl_list.append(key)
382
+ for key in self.dataset.metadata['peak_fit']['white_line_sums']:
383
+ self.wls_list.append(key)
384
+
385
+ self.sidebar[14, 0].options = self.wl_list
386
+ self.sidebar[14, 0].value = self.wl_list[0]
387
+ self.sidebar[14, 2].value = f"{self.dataset.metadata['peak_fit']['white_line_ratios'][self.wl_list[0]]:.2f}"
388
+
389
+ self.sidebar[15, 0].options = self.wls_list
390
+ self.sidebar[15, 0].value = self.wls_list[0]
391
+ self.sidebar[15, 2].value = f"{self.dataset.metadata['peak_fit']['white_line_sums'][self.wls_list[0]]*1e6:.4f} ppm"
392
+
393
+ else:
394
+ self.wl_list.append('Ratio')
395
+ self.wls_list.append('Sum')
396
+
397
+ self.sidebar[14, 0].options = ['None']
398
+ self.sidebar[14, 0].value = 'None'
399
+ self.sidebar[14, 2].value = ' '
400
+
401
+ self.sidebar[15, 0].options = ['None']
402
+ self.sidebar[15, 0].value = 'None'
403
+ self.sidebar[15, 2].value = ' '
404
+
405
+ def find_peaks(self, value=0):
406
+ number_of_peaks = int(self.sidebar[5, 0].value)
407
+ if number_of_peaks > len(self.peak_out_list):
408
+ number_of_peaks = len(self.peak_out_list)
409
+ self.sidebar[5, 0].value = str(len(self.peak_out_list))
410
+ self.peak_list = []
411
+ self.peaks['peaks'] = {}
412
+ new_number_of_peaks = 0
413
+ for i in range(number_of_peaks):
414
+ self.peak_list.append((f'Peak {i+1}', i))
415
+ p = self.peak_out_list[i]
416
+ if p[1]>0:
417
+ self.peaks['peaks'][str(new_number_of_peaks)] = {'position': p[0], 'amplitude': p[1], 'width': p[2], 'type': 'Gauss',
418
+ 'asymmetry': 0}
419
+ new_number_of_peaks += 1
420
+ self.sidebar[5, 0].value = str(new_number_of_peaks)
421
+ self.peak_list.append((f'add peak', -1))
422
+
423
+ self.sidebar[7, 0].options = self.peak_list
424
+ self.sidebar[7, 0].value = 0
425
+
426
+ #eels_tools.find_associated_edges(self.dataset)
427
+ #self.find_white_lines()
428
+
429
+ self.update()
430
+ self.plot()
431
+
432
+ def smooth(self, value=0):
433
+ """Fit lots of Gaussian to spectrum and let the program sort it out
434
+
435
+ We sort the peaks by area under the Gaussians, assuming that small areas mean noise.
436
+
437
+ """
438
+ iterations = self.sidebar[4, 0].value
439
+ self.sidebar[5, 0].value = 0
440
+ advanced_present=False
441
+
442
+ self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset, iterations, advanced_present)
443
+
444
+ spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
445
+ if spec_dim[1][0] > 0:
446
+ self.model = self.dataset.metadata['edges']['model']['spectrum']
447
+ elif 'model' in self.dataset.metadata:
448
+ self.model = self.dataset.metadata['model']
449
+ else:
450
+ self.model = np.zeros(len(spec_dim[1]))
451
+
452
+ self.dataset.metadata['peak_fit']['edge_model'] = self.model
453
+ self.model = self.model + self.peak_model
454
+ self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
455
+ self.dataset.metadata['peak_fit']['peak_out_list'] = self.peak_out_list
456
+
457
+ self.sidebar[5, 0].value = str(len(self.peak_out_list))
458
+ self.update()
459
+ self.plot()
460
+
461
+ def make_model(self):
462
+ p_peaks = []
463
+ for key, peak in self.peaks['peaks'].items():
464
+ if key.isdigit():
465
+ p_peaks.append(peak['position'])
466
+ p_peaks.append(peak['amplitude'])
467
+ p_peaks.append(peak['width'])
468
+
469
+
470
+ # set the energy scale and fit start and end points
471
+ energy_scale = np.array(self.energy_scale)
472
+ start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
473
+ end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
474
+ energy_scale = self.energy_scale[start_channel:end_channel]
475
+ # select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
476
+
477
+ fit = eels_tools.model_smooth(energy_scale, p_peaks, False)
478
+ self.peak_model[start_channel:end_channel] = fit
479
+ if 'edge_model' in self.dataset.metadata['peak_fit']:
480
+ self.model = self.dataset.metadata['peak_fit']['edge_model'] + self.peak_model
481
+ else:
482
+ self.model = np.zeros(self.dataset.shape)
483
+
484
+ def modify_peak_position(self, value=-1):
485
+ peak_index = self.sidebar[7, 0].value
486
+ self.peaks['peaks'][str(peak_index)]['position'] = self.sidebar[9,0].value
487
+ self.make_model()
488
+ self.plot()
489
+
490
+ def modify_peak_amplitude(self, value=-1):
491
+ peak_index = self.sidebar[7, 0].value
492
+ self.peaks['peaks'][str(peak_index)]['amplitude'] = self.sidebar[10,0].value
493
+ self.make_model()
494
+ self.plot()
495
+
496
+ def modify_peak_width(self, value=-1):
497
+ peak_index = self.sidebar[7, 0].value
498
+ self.peaks['peaks'][str(peak_index)]['width'] = self.sidebar[11,0].value
499
+ self.make_model()
500
+ self.plot()
501
+
502
+ def peak_selection(self, change=None):
503
+ options = list(self.sidebar[7,0].options)
504
+
505
+ if self.sidebar[7, 0].value < 0:
506
+ options.insert(-1, (f'Peak {len(options)}', len(options)-1))
507
+ self.sidebar[7, 0].value = 0
508
+ self.sidebar[7,0].options = options
509
+ self.sidebar[7, 0].value = int(len(options)-2)
510
+
511
+ self.update()
512
+
513
+ def set_action(self):
514
+ self.sidebar[1, 0].observe(self.set_fit_area, names='value')
515
+ self.sidebar[2, 0].observe(self.set_fit_area, names='value')
516
+
517
+ self.sidebar[4, 2].on_click(self.smooth)
518
+ self.sidebar[7,0].observe(self.peak_selection)
519
+ self.sidebar[5,2].on_click(self.find_peaks)
520
+
521
+ self.sidebar[6, 0].on_click(self.fit_peaks)
522
+ self.sidebar[9, 0].observe(self.modify_peak_position, names='value')
523
+ self.sidebar[10, 0].observe(self.modify_peak_amplitude, names='value')
524
+ self.sidebar[11, 0].observe(self.modify_peak_width, names='value')
525
+
526
+
527
+
528
+
529
+ if Qt_available:
530
+ class PeakFitDialog(QtWidgets.QDialog):
531
+ """
532
+ EELS Input Dialog for ELNES Analysis
533
+ """
534
+
535
+ def __init__(self, datasets=None):
536
+ super().__init__(None, QtCore.Qt.WindowStaysOnTopHint)
537
+
538
+ if datasets is None:
539
+ # make a dummy dataset
540
+ datasets = ft.make_dummy_dataset('spectrum')
541
+ if not isinstance(datasets, dict):
542
+ datasets= {'Channel_000': datasets}
543
+
544
+ self.dataset = datasets[list(datasets.keys())[0]]
545
+ self.datasets = datasets
546
+ # Create an instance of the GUI
547
+ if 'low_loss' in self.dataset.metadata:
548
+ mode = 'low_loss'
549
+ else:
550
+ mode = 'core_loss'
551
+
552
+ self.ui = peak_dlg.UiDialog(self, mode=mode)
553
+
554
+ self.set_action()
555
+
556
+ self.energy_scale = np.array([])
557
+ self.peak_out_list = []
558
+ self.p_out = []
559
+ self.axis = None
560
+ self.show_regions = False
561
+ self.show()
562
+
563
+
564
+
565
+ if not isinstance(self.dataset, sidpy.Dataset):
566
+ raise TypeError('dataset has to be a sidpy dataset')
567
+ self.spec_dim = ft.get_dimensions_by_type('spectral', self.dataset)
568
+ if len(self.spec_dim) != 1:
569
+ raise TypeError('We need exactly one SPECTRAL dimension')
570
+ self.spec_dim = self.spec_dim[0]
571
+ self.energy_scale = self.spec_dim[1].values.copy()
572
+
573
+ if 'peak_fit' not in self.dataset.metadata:
574
+ self.dataset.metadata['peak_fit'] = {}
575
+ if 'edges' in self.dataset.metadata:
576
+ if 'fit_area' in self.dataset.metadata['edges']:
577
+ self.dataset.metadata['peak_fit']['fit_start'] = \
578
+ self.dataset.metadata['edges']['fit_area']['fit_start']
579
+ self.dataset.metadata['peak_fit']['fit_end'] = self.dataset.metadata['edges']['fit_area']['fit_end']
580
+ self.dataset.metadata['peak_fit']['peaks'] = {'0': {'position': self.energy_scale[1],
581
+ 'amplitude': 1000.0, 'width': 1.0,
582
+ 'type': 'Gauss', 'asymmetry': 0}}
583
+
584
+
585
+ self.peaks = self.dataset.metadata['peak_fit']
586
+ if 'fit_start' not in self.peaks:
587
+ self.peaks['fit_start'] = self.energy_scale[1]
588
+ self.peaks['fit_end'] = self.energy_scale[-2]
589
+
590
+ if 'peak_model' in self.peaks:
591
+ self.peak_model = self.peaks['peak_model']
592
+ self.model = self.peak_model
593
+ if 'edge_model' in self.peaks:
594
+ self.model = self.model + self.peaks['edge_model']
595
+ else:
596
+ self.model = np.array([])
597
+ self.peak_model = np.array([])
598
+ if 'peak_out_list' in self.peaks:
599
+ self.peak_out_list = self.peaks['peak_out_list']
600
+ self.set_peak_list()
601
+
602
+ # check whether a core loss analysis has been done previously
603
+ if not hasattr(self, 'core_loss') and 'edges' in self.dataset.metadata:
604
+ self.core_loss = True
605
+ else:
606
+ self.core_loss = False
607
+
608
+ self.update()
609
+ self.dataset.plot()
610
+
611
+ if self.dataset.data_type.name == 'SPECTRAL_IMAGE':
612
+ if 'SI_bin_x' not in self.dataset.metadata['experiment']:
613
+ self.dataset.metadata['experiment']['SI_bin_x'] = 1
614
+ self.dataset.metadata['experiment']['SI_bin_y'] = 1
615
+ bin_x = self.dataset.metadata['experiment']['SI_bin_x']
616
+ bin_y = self.dataset.metadata['experiment']['SI_bin_y']
617
+
618
+ self.dataset.view.set_bin([bin_x, bin_y])
619
+
620
+ if hasattr(self.dataset.view, 'axes'):
621
+ self.axis = self.dataset.view.axes[-1]
622
+ elif hasattr(self.dataset.view, 'axis'):
623
+ self.axis = self.dataset.view.axis
624
+ self.figure = self.axis.figure
625
+
626
+ if not advanced_present:
627
+ self.ui.iteration_list = ['0']
628
+ self.ui.smooth_list.clear()
629
+ self.ui.smooth_list.addItems(self.ui.iteration_list)
630
+ self.ui.smooth_list.setCurrentIndex(0)
631
+
632
+ if 'low_loss' in self.dataset.metadata:
633
+ self.ui.iteration_list = ['0']
634
+
635
+
636
+ self.figure.canvas.mpl_connect('button_press_event', self.plot)
637
+
638
+
639
+ self.plot()
640
+
641
+ def update(self):
642
+ # self.setWindowTitle('update')
643
+ self.ui.edit1.setText(f"{self.peaks['fit_start']:.2f}")
644
+ self.ui.edit2.setText(f"{self.peaks['fit_end']:.2f}")
645
+
646
+ peak_index = self.ui.list3.currentIndex()
647
+ if str(peak_index) not in self.peaks['peaks']:
648
+ self.peaks['peaks'][str(peak_index)] = {'position': self.energy_scale[1], 'amplitude': 1000.0,
649
+ 'width': 1.0, 'type': 'Gauss', 'asymmetry': 0}
650
+ self.ui.list4.setCurrentText(self.peaks['peaks'][str(peak_index)]['type'])
651
+ if 'associated_edge' in self.peaks['peaks'][str(peak_index)]:
652
+ self.ui.unit3.setText(self.peaks['peaks'][str(peak_index)]['associated_edge'])
653
+ else:
654
+ self.ui.unit3.setText('')
655
+ self.ui.edit5.setText(f"{self.peaks['peaks'][str(peak_index)]['position']:.2f}")
656
+ self.ui.edit6.setText(f"{self.peaks['peaks'][str(peak_index)]['amplitude']:.2f}")
657
+ self.ui.edit7.setText(f"{self.peaks['peaks'][str(peak_index)]['width']:.2f}")
658
+ if 'asymmetry' not in self.peaks['peaks'][str(peak_index)]:
659
+ self.peaks['peaks'][str(peak_index)]['asymmetry'] = 0.
660
+ self.ui.edit8.setText(f"{self.peaks['peaks'][str(peak_index)]['asymmetry']:.2f}")
661
+
662
+ def plot(self):
663
+
664
+ spec_dim = ft.get_dimensions_by_type(sidpy.DimensionType.SPECTRAL, self.dataset)
665
+ spec_dim = spec_dim[0]
666
+ self.energy_scale = spec_dim[1].values
667
+ if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
668
+ spectrum = self.dataset.view.get_spectrum()
669
+ self.axis = self.dataset.view.axes[1]
670
+ name = 's'
671
+ if 'zero_loss' in self.dataset.metadata:
672
+ x = self.dataset.view.x
673
+ y = self.dataset.view.y
674
+ self.energy_scale -= self.dataset.metadata['zero_loss']['shifts'][x, y]
675
+ name = f"shift { self.dataset.metadata['zero_loss']['shifts'][x, y]:.3f}"
676
+ self.setWindowTitle(f'plot {x}')
677
+ else:
678
+ spectrum = np.array(self.dataset)
679
+ self.axis = self.dataset.view.axis
680
+
681
+ x_limit = self.axis.get_xlim()
682
+ y_limit = self.axis.get_ylim()
683
+ self.axis.clear()
684
+
685
+ self.axis.plot(self.energy_scale, spectrum, label='spectrum')
686
+ if 'zero_loss' in self.dataset.metadata:
687
+ self.axis.plot(self.energy_scale, spectrum, label=name)
688
+
689
+ if len(self.model) > 1:
690
+ self.axis.plot(self.energy_scale, self.model, label='model')
691
+ self.axis.plot(self.energy_scale, spectrum - self.model, label='difference')
692
+ #self.axis.plot(self.energy_scale, (spectrum - self.model) / np.sqrt(spectrum), label='Poisson')
693
+ self.axis.legend()
694
+ self.axis.set_xlim(x_limit)
695
+ self.axis.set_ylim(y_limit)
696
+ self.axis.figure.canvas.draw_idle()
697
+
698
+ for index, peak in self.peaks['peaks'].items():
699
+ p = [peak['position'], peak['amplitude'], peak['width']]
700
+ self.axis.plot(self.energy_scale, eels_tools.gauss(self.energy_scale, p))
701
+
702
+ def fit_peaks(self):
703
+ """Fit spectrum with peaks given in peaks dictionary"""
704
+ print('Fitting peaks...')
705
+ p_in = []
706
+ for key, peak in self.peaks['peaks'].items():
707
+ if key.isdigit():
708
+ p_in.append(peak['position'])
709
+ p_in.append(peak['amplitude'])
710
+ p_in.append(peak['width'])
711
+
712
+ # check whether we have a spectral image or just a single spectrum
713
+ if self.dataset.data_type == sidpy.DataType.SPECTRAL_IMAGE:
714
+ spectrum = self.dataset.view.get_spectrum()
715
+ else:
716
+ spectrum = np.array(self.dataset)
717
+
718
+ # set the energy scale and fit start and end points
719
+ energy_scale = np.array(self.energy_scale)
720
+ start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
721
+ end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
722
+
723
+ energy_scale = self.energy_scale[start_channel:end_channel]
724
+ # select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
725
+ if 'model' in self.dataset.metadata:
726
+ model = self.dataset.metadata['model'][start_channel:end_channel]
727
+ elif self.core_loss:
728
+ print('Core loss model found. Fitting on top of the model.')
729
+ model = self.dataset.metadata['edges']['model']['spectrum'][start_channel:end_channel]
730
+ else:
731
+ print('No core loss model found. Fitting to the full spectrum.')
732
+ model = np.zeros(end_channel - start_channel)
733
+
734
+ # if we have a core loss model we will only fit the difference between the model and the data.
735
+ difference = np.array(spectrum[start_channel:end_channel] - model)
736
+
737
+ # find the optimum fitting parameters
738
+ [self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals_smooth, np.array(p_in), ftol=1e-3,
739
+ args=(energy_scale, difference, False))
740
+
741
+ # construct the fit data from the optimized parameters
742
+ self.peak_model = np.zeros(len(self.energy_scale))
743
+ self.model = np.zeros(len(self.energy_scale))
744
+ self.model[start_channel:end_channel] = model
745
+ fit = eels_tools.model_smooth(energy_scale, self.p_out, False)
746
+ self.peak_model[start_channel:end_channel] = fit
747
+ self.dataset.metadata['peak_fit']['edge_model'] = self.model
748
+ self.model = self.model + self.peak_model
749
+ self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
750
+
751
+ for key, peak in self.peaks['peaks'].items():
752
+ if key.isdigit():
753
+ p_index = int(key)*3
754
+ self.peaks['peaks'][key] = {'position': self.p_out[p_index],
755
+ 'amplitude': self.p_out[p_index+1],
756
+ 'width': self.p_out[p_index+2],
757
+ 'associated_edge': ''}
758
+
759
+ self.find_associated_edges()
760
+ self.find_white_lines()
761
+ self.update()
762
+ self.plot()
763
+
764
+ def smooth(self):
765
+ """Fit lots of Gaussian to spectrum and let the program sort it out
766
+
767
+ We sort the peaks by area under the Gaussians, assuming that small areas mean noise.
768
+
769
+ """
770
+ if 'edges' in self.dataset.metadata:
771
+ if 'model' in self.dataset.metadata['edges']:
772
+ self.dataset.metadata['model'] = self.dataset.metadata['edges']['model']
773
+ if 'resolution_function' in self.datasets:
774
+ self.dataset.metadata['model'] = np.array(self.datasets['resolution_function'])
775
+ iterations = int(self.ui.smooth_list.currentIndex())
776
+
777
+ self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset, iterations, advanced_present)
778
+
779
+ spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
780
+ if spec_dim[1][0] > 0:
781
+ self.model = self.dataset.metadata['edges']['model']['spectrum']
782
+ elif 'model' in self.dataset.metadata:
783
+ self.model = self.dataset.metadata['model']
784
+ else:
785
+ self.model = np.zeros(len(spec_dim[1]))
786
+
787
+ self.ui.find_edit.setText(str(number_of_peaks))
788
+
789
+ self.dataset.metadata['peak_fit']['edge_model'] = self.model
790
+ self.model = self.model + self.peak_model
791
+ self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
792
+ self.dataset.metadata['peak_fit']['peak_out_list'] = self.peak_out_list
793
+
794
+ self.update()
795
+ self.plot()
796
+
797
+ def find_associated_edges(self):
798
+ onsets = []
799
+ edges = []
800
+ if 'edges' in self.dataset.metadata:
801
+ for key, edge in self.dataset.metadata['edges'].items():
802
+ if key.isdigit():
803
+ element = edge['element']
804
+ for sym in edge['all_edges']: # TODO: Could be replaced with exclude
805
+ onsets.append(edge['all_edges'][sym]['onset'] + edge['chemical_shift'])
806
+ # if 'sym' == edge['symmetry']:
807
+ edges.append([key, f"{element}-{sym}", onsets[-1]])
808
+ for key, peak in self.peaks['peaks'].items():
809
+ if key.isdigit():
810
+ distance = self.energy_scale[-1]
811
+ index = -1
812
+ for ii, onset in enumerate(onsets):
813
+ if onset < peak['position'] < onset+50:
814
+ if distance > np.abs(peak['position'] - onset):
815
+ distance = np.abs(peak['position'] - onset) # TODO: check whether absolute is good
816
+ distance_onset = peak['position'] - onset
817
+ index = ii
818
+ if index >= 0:
819
+ peak['associated_edge'] = edges[index][1] # check if more info is necessary
820
+ peak['distance_to_onset'] = distance_onset
821
+
822
+ def find_white_lines(self):
823
+ eels_tools.find_white_lines(self.dataset)
824
+
825
+ self.ui.wl_list = []
826
+ self.ui.wls_list = []
827
+ if len(self.peaks['white_line_ratios']) > 0:
828
+ for key in self.peaks['white_line_ratios']:
829
+ self.ui.wl_list.append(key)
830
+ for key in self.peaks['white_line_sums']:
831
+ self.ui.wls_list.append(key)
832
+
833
+ self.ui.listwl.clear()
834
+ self.ui.listwl.addItems(self.ui.wl_list)
835
+ self.ui.listwl.setCurrentIndex(0)
836
+ self.ui.unitswl.setText(f"{self.peaks['white_line_ratios'][self.ui.wl_list[0]]:.2f}")
837
+
838
+ self.ui.listwls.clear()
839
+ self.ui.listwls.addItems(self.ui.wls_list)
840
+ self.ui.listwls.setCurrentIndex(0)
841
+ self.ui.unitswls.setText(f"{self.peaks['white_line_sums'][self.ui.wls_list[0]]*1e6:.4f} ppm")
842
+ else:
843
+ self.ui.wl_list.append('Ratio')
844
+ self.ui.wls_list.append('Sum')
845
+
846
+ self.ui.listwl.clear()
847
+ self.ui.listwl.addItems(self.ui.wl_list)
848
+ self.ui.listwl.setCurrentIndex(0)
849
+ self.ui.unitswl.setText('')
850
+
851
+ self.ui.listwls.clear()
852
+ self.ui.listwls.addItems(self.ui.wls_list)
853
+ self.ui.listwls.setCurrentIndex(0)
854
+ self.ui.unitswls.setText('')
855
+
856
+ def find_peaks(self):
857
+ number_of_peaks = int(str(self.ui.find_edit.displayText()).strip())
858
+
859
+ # is now sorted in smooth function
860
+ # flat_list = [item for sublist in self.peak_out_list for item in sublist]
861
+ # new_list = np.reshape(flat_list, [len(flat_list) // 3, 3])
862
+ # arg_list = np.argsort(np.abs(new_list[:, 1]))
863
+
864
+ self.ui.peak_list = []
865
+ self.peaks['peaks'] = {}
866
+ for i in range(number_of_peaks):
867
+ self.ui.peak_list.append(f'Peak {i+1}')
868
+ p = self.peak_out_list[i]
869
+ self.peaks['peaks'][str(i)] = {'position': p[0], 'amplitude': p[1], 'width': p[2], 'type': 'Gauss',
870
+ 'asymmetry': 0}
871
+
872
+ self.ui.peak_list.append(f'add peak')
873
+ self.ui.list3.clear()
874
+ self.ui.list3.addItems(self.ui.peak_list)
875
+ self.ui.list3.setCurrentIndex(0)
876
+ self.find_associated_edges()
877
+ self.find_white_lines()
878
+
879
+ self.update()
880
+ self.plot()
881
+
882
+ def set_peak_list(self):
883
+ self.ui.peak_list = []
884
+ if 'peaks' not in self.peaks:
885
+ self.peaks['peaks'] = {}
886
+ key = 0
887
+ for key in self.peaks['peaks']:
888
+ if key.isdigit():
889
+ self.ui.peak_list.append(f'Peak {int(key) + 1}')
890
+ self.ui.find_edit.setText(str(int(key) + 1))
891
+ self.ui.peak_list.append(f'add peak')
892
+ self.ui.list3.clear()
893
+ self.ui.list3.addItems(self.ui.peak_list)
894
+ self.ui.list3.setCurrentIndex(0)
895
+
896
+ def on_enter(self):
897
+ if self.sender() == self.ui.edit1:
898
+ value = float(str(self.ui.edit1.displayText()).strip())
899
+ if value < self.energy_scale[0]:
900
+ value = self.energy_scale[0]
901
+ if value > self.energy_scale[-5]:
902
+ value = self.energy_scale[-5]
903
+ self.peaks['fit_start'] = value
904
+ self.ui.edit1.setText(str(self.peaks['fit_start']))
905
+ elif self.sender() == self.ui.edit2:
906
+ value = float(str(self.ui.edit2.displayText()).strip())
907
+ if value < self.energy_scale[5]:
908
+ value = self.energy_scale[5]
909
+ if value > self.energy_scale[-1]:
910
+ value = self.energy_scale[-1]
911
+ self.peaks['fit_end'] = value
912
+ self.ui.edit2.setText(str(self.peaks['fit_end']))
913
+ elif self.sender() == self.ui.edit5:
914
+ value = float(str(self.ui.edit5.displayText()).strip())
915
+ peak_index = self.ui.list3.currentIndex()
916
+ self.peaks['peaks'][str(peak_index)]['position'] = value
917
+ elif self.sender() == self.ui.edit6:
918
+ value = float(str(self.ui.edit6.displayText()).strip())
919
+ peak_index = self.ui.list3.currentIndex()
920
+ self.peaks['peaks'][str(peak_index)]['amplitude'] = value
921
+ elif self.sender() == self.ui.edit7:
922
+ value = float(str(self.ui.edit7.displayText()).strip())
923
+ peak_index = self.ui.list3.currentIndex()
924
+ self.peaks['peaks'][str(peak_index)]['width'] = value
925
+
926
+ def on_list_enter(self):
927
+ self.setWindowTitle(f'list {self.sender}, {self.ui.list_model}')
928
+ if self.sender() == self.ui.list3:
929
+ if self.ui.list3.currentText().lower() == 'add peak':
930
+ peak_index = self.ui.list3.currentIndex()
931
+ self.ui.list3.insertItem(peak_index, f'Peak {peak_index+1}')
932
+ self.peaks['peaks'][str(peak_index+1)] = {'position': self.energy_scale[1],
933
+ 'amplitude': 1000.0, 'width': 1.0,
934
+ 'type': 'Gauss', 'asymmetry': 0}
935
+ self.ui.list3.setCurrentIndex(peak_index)
936
+ self.update()
937
+
938
+ elif self.sender() == self.ui.listwls or self.sender() == self.ui.listwl:
939
+ wl_index = self.sender().currentIndex()
940
+
941
+ self.ui.listwl.setCurrentIndex(wl_index)
942
+ self.ui.unitswl.setText(f"{self.peaks['white_line_ratios'][self.ui.wl_list[wl_index]]:.2f}")
943
+ self.ui.listwls.setCurrentIndex(wl_index)
944
+ self.ui.unitswls.setText(f"{self.peaks['white_line_sums'][self.ui.wls_list[wl_index]] * 1e6:.4f} ppm")
945
+ elif self.sender() == self.ui.list_model:
946
+ self.setWindowTitle('list 1')
947
+ if self.sender().currentIndex() == 1:
948
+ if 'resolution_function' in self.datasets:
949
+ self.setWindowTitle('list 2')
950
+ self.dataset.metadata['model'] = np.array(self.datasets['resolution_function'])
951
+ else:
952
+ self.ui.list_model.setCurrentIndex(0)
953
+ else:
954
+ self.ui.list_model.setCurrentIndex(0)
955
+ def set_action(self):
956
+ pass
957
+ self.ui.edit1.editingFinished.connect(self.on_enter)
958
+ self.ui.edit2.editingFinished.connect(self.on_enter)
959
+ self.ui.edit5.editingFinished.connect(self.on_enter)
960
+ self.ui.edit6.editingFinished.connect(self.on_enter)
961
+ self.ui.edit7.editingFinished.connect(self.on_enter)
962
+ self.ui.edit8.editingFinished.connect(self.on_enter)
963
+ self.ui.list3.activated[str].connect(self.on_list_enter)
964
+ self.ui.find_button.clicked.connect(self.find_peaks)
965
+ self.ui.smooth_button.clicked.connect(self.smooth)
966
+ self.ui.fit_button.clicked.connect(self.fit_peaks)
967
+ if hasattr(self.ui, 'listwls'):
968
+ self.ui.listwls.activated[str].connect(self.on_list_enter)
969
+ self.ui.listwl.activated[str].connect(self.on_list_enter)
970
+ else:
971
+ self.ui.zl_button.clicked.connect(self.fit_zero_loss)
972
+ self.ui.drude_button.clicked.connect(self.smooth)
973
+ self.ui.list_model.activated[str].connect(self.on_list_enter)
974
+
975
+ def fit_zero_loss(self):
976
+ """get shift of spectrum form zero-loss peak position"""
977
+ zero_loss_fit_width=0.3
978
+
979
+ energy_scale = self.dataset.energy_loss
980
+ zl_dataset = self.dataset.copy()
981
+ zl_dataset.title = 'resolution_function'
982
+ shifts = np.zeros(self.dataset.shape[0:2])
983
+ zero_p = np.zeros([self.dataset.shape[0],self.dataset.shape[1],6])
984
+ fwhm_p = np.zeros(self.dataset.shape[0:2])
985
+ bin_x = bin_y = 1
986
+ total_spec = int(self.dataset.shape[0]/bin_x)*int(self.dataset.shape[1]/bin_y)
987
+ self.ui.progress.setMaximum(total_spec)
988
+ self.ui.progress.setValue(0)
989
+ zero_loss_fit_width=0.3
990
+ ind = 0
991
+ for x in range(self.dataset.shape[0]):
992
+ for y in range(self.dataset.shape[1]):
993
+ ind += 1
994
+ self.ui.progress.setValue(ind)
995
+ spectrum = self.dataset[x, y, :]
996
+ fwhm, delta_e = eels_tools.fix_energy_scale(spectrum, energy_scale)
997
+ z_loss, p_zl = eels_tools.resolution_function(energy_scale - delta_e, spectrum, zero_loss_fit_width)
998
+ fwhm2, delta_e2 = eels_tools.fix_energy_scale(z_loss, energy_scale - delta_e)
999
+ shifts[x, y] = delta_e + delta_e2
1000
+ zero_p[x,y,:] = p_zl
1001
+ zl_dataset[x,y] = z_loss
1002
+ fwhm_p[x,y] = fwhm2
1003
+
1004
+ zl_dataset.metadata['zero_loss'] = {'parameter': zero_p,
1005
+ 'shifts': shifts,
1006
+ 'fwhm': fwhm_p}
1007
+ self.dataset.metadata['zero_loss'] = {'parameter': zero_p,
1008
+ 'shifts': shifts,
1009
+ 'fwhm': fwhm_p}
1010
+
1011
+ self.datasets['resolution_function'] = zl_dataset
1012
+ self.update()
1013
+ self.plot()
1014
+
1015
+
1016
+
1017
+ def smooth(dataset, iterations, advanced_present):
1018
+ """Gaussian mixture model (non-Bayesian)
1019
+
1020
+ Fit lots of Gaussian to spectrum and let the program sort it out
1021
+ We sort the peaks by area under the Gaussians, assuming that small areas mean noise.
1022
+
1023
+ """
1024
+
1025
+ # TODO: add sensitivity to dialog and the two functions below
1026
+ peaks = dataset.metadata['peak_fit']
1027
+
1028
+ peak_model, peak_out_list = eels_tools.find_peaks(dataset,
1029
+ peaks['fit_start'],
1030
+ peaks['fit_end'])
1031
+ #
1032
+ #cif advanced_present and iterations > 1:
1033
+ # peak_model, peak_out_list = advanced_eels_tools.smooth(dataset, peaks['fit_start'],
1034
+ # peaks['fit_end'], iterations=iterations)
1035
+ # else:
1036
+ # peak_model, peak_out_list = eels_tools.find_peaks(dataset, peaks['fit_start'], peaks['fit_end'])
1037
+ # peak_out_list = [peak_out_list]
1038
+
1039
+ new_list = np.reshape(peak_out_list, [len(peak_out_list) // 3, 3])
1040
+ area = np.sqrt(2 * np.pi) * np.abs(new_list[:, 1]) * np.abs(new_list[:, 2] / np.sqrt(2 * np.log(2)))
1041
+ arg_list = np.argsort(area)[::-1]
1042
+ area = area[arg_list]
1043
+ peak_out_list = new_list[arg_list]
1044
+
1045
+ number_of_peaks = np.searchsorted(area * -1, -np.average(area))
1046
+
1047
+ return peak_model, peak_out_list, number_of_peaks