pyTEMlib 0.2024.9.0__py3-none-any.whl → 0.2025.2.2__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/animation.py +1 -1
- pyTEMlib/atom_tools.py +2 -1
- pyTEMlib/core_loss_widget.py +337 -272
- pyTEMlib/eels_dialog.py +15 -10
- pyTEMlib/eels_tools.py +452 -125
- pyTEMlib/file_tools.py +319 -30
- pyTEMlib/image_tools.py +91 -15
- pyTEMlib/info_widget.py +211 -58
- pyTEMlib/info_widget3.py +1120 -0
- pyTEMlib/low_loss_widget.py +344 -41
- pyTEMlib/peak_dialog.py +141 -59
- pyTEMlib/probe_tools.py +65 -8
- pyTEMlib/version.py +2 -2
- {pyTEMlib-0.2024.9.0.dist-info → pytemlib-0.2025.2.2.dist-info}/METADATA +15 -5
- {pyTEMlib-0.2024.9.0.dist-info → pytemlib-0.2025.2.2.dist-info}/RECORD +19 -18
- {pyTEMlib-0.2024.9.0.dist-info → pytemlib-0.2025.2.2.dist-info}/WHEEL +1 -1
- {pyTEMlib-0.2024.9.0.dist-info → pytemlib-0.2025.2.2.dist-info}/LICENSE +0 -0
- {pyTEMlib-0.2024.9.0.dist-info → pytemlib-0.2025.2.2.dist-info}/entry_points.txt +0 -0
- {pyTEMlib-0.2024.9.0.dist-info → pytemlib-0.2025.2.2.dist-info}/top_level.txt +0 -0
pyTEMlib/peak_dialog.py
CHANGED
|
@@ -136,13 +136,13 @@ def get_sidebar():
|
|
|
136
136
|
return side_bar
|
|
137
137
|
|
|
138
138
|
class PeakFitWidget(object):
|
|
139
|
-
def __init__(self, datasets
|
|
139
|
+
def __init__(self, datasets, key):
|
|
140
140
|
self.datasets = datasets
|
|
141
141
|
if not isinstance(datasets, dict):
|
|
142
142
|
raise TypeError('need dictioary of sidpy datasets')
|
|
143
143
|
|
|
144
144
|
self.sidebar = get_sidebar()
|
|
145
|
-
self.key =
|
|
145
|
+
self.key = key
|
|
146
146
|
self.dataset = datasets[self.key]
|
|
147
147
|
if not isinstance(self.dataset, sidpy.Dataset):
|
|
148
148
|
raise TypeError('dataset or first item inhas to be a sidpy dataset')
|
|
@@ -199,16 +199,22 @@ class PeakFitWidget(object):
|
|
|
199
199
|
spectrum = self.dataset.view.get_spectrum()
|
|
200
200
|
else:
|
|
201
201
|
spectrum = self.dataset
|
|
202
|
+
#if 'features' in self.peaks:
|
|
203
|
+
if 'resolution_function' in self.datasets.keys():
|
|
204
|
+
|
|
205
|
+
zl = self.datasets['resolution_function'] # self.peaks['features']]
|
|
206
|
+
additional_spectra = {}
|
|
202
207
|
if len(self.model) > 1:
|
|
203
208
|
additional_spectra = {'model': self.model,
|
|
204
|
-
'difference': spectrum-self.model
|
|
209
|
+
'difference': spectrum-self.model,
|
|
210
|
+
'zero_loss': self.datasets['resolution_function']}
|
|
205
211
|
else:
|
|
206
212
|
additional_spectra = {}
|
|
207
213
|
if 'peaks' in self.peaks:
|
|
208
214
|
if len(self.peaks)>0:
|
|
209
215
|
for index, peak in self.peaks['peaks'].items(): # ll
|
|
210
216
|
p = [peak['position'], peak['amplitude'], peak['width']]
|
|
211
|
-
additional_spectra[f'peak {index}']=
|
|
217
|
+
additional_spectra[f'peak {index}']= gauss(np.array(self.energy_scale), p)
|
|
212
218
|
self.view.plot(scale=True, additional_spectra=additional_spectra )
|
|
213
219
|
self.change_y_scale = 1.
|
|
214
220
|
|
|
@@ -263,7 +269,9 @@ class PeakFitWidget(object):
|
|
|
263
269
|
if self.dataset.data_type.name =='SPECTRAL_IMAGE':
|
|
264
270
|
self.view = eels_dialog_utilities.SIPlot(self.dataset)
|
|
265
271
|
else:
|
|
266
|
-
self.view = eels_dialog_utilities.SpectrumPlot(self.dataset)
|
|
272
|
+
self.view = eels_dialog_utilities.SpectrumPlot(self.dataset)
|
|
273
|
+
self.dataset.view = self.view
|
|
274
|
+
#self.view.legend(loc='Upper Right')
|
|
267
275
|
self.y_scale = 1.0
|
|
268
276
|
self.change_y_scale = 1.0
|
|
269
277
|
|
|
@@ -309,19 +317,26 @@ class PeakFitWidget(object):
|
|
|
309
317
|
self.peaks['peaks'][str(peak_index)]['asymmetry'] = 0.
|
|
310
318
|
self.sidebar[12, 0].value = self.peaks['peaks'][str(peak_index)]['asymmetry']
|
|
311
319
|
|
|
312
|
-
|
|
313
|
-
def
|
|
314
|
-
"""Fit spectrum with peaks given in peaks dictionary"""
|
|
315
|
-
# print('Fitting peaks...')
|
|
320
|
+
|
|
321
|
+
def get_input(self):
|
|
316
322
|
p_in = []
|
|
317
323
|
for key, peak in self.peaks['peaks'].items():
|
|
318
324
|
if key.isdigit():
|
|
319
325
|
p_in.append(peak['position'])
|
|
320
326
|
p_in.append(peak['amplitude'])
|
|
321
327
|
p_in.append(peak['width'])
|
|
328
|
+
return p_in
|
|
322
329
|
|
|
323
|
-
|
|
324
|
-
|
|
330
|
+
|
|
331
|
+
def fit_peaks(self, value=0):
|
|
332
|
+
"""Fit spectrum with peaks given in peaks dictionary"""
|
|
333
|
+
# print('Fitting peaks...')
|
|
334
|
+
|
|
335
|
+
if self.dataset.data_type.name == 'SPECTRUM':
|
|
336
|
+
spectrum = np.array(self.dataset)
|
|
337
|
+
else:
|
|
338
|
+
spectrum = self.dataset.view.get_spectrum()
|
|
339
|
+
spectrum -= spectrum.min() - 1
|
|
325
340
|
# set the energy scale and fit start and end points
|
|
326
341
|
energy_scale = np.array(self.energy_scale)
|
|
327
342
|
start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
|
|
@@ -335,25 +350,31 @@ class PeakFitWidget(object):
|
|
|
335
350
|
# print('Core loss model found. Fitting on top of the model.')
|
|
336
351
|
model = self.dataset.metadata['edges']['model']['spectrum'][start_channel:end_channel]
|
|
337
352
|
else:
|
|
353
|
+
|
|
338
354
|
# print('No core loss model found. Fitting to the full spectrum.')
|
|
339
355
|
model = np.zeros(end_channel - start_channel)
|
|
340
356
|
|
|
341
357
|
# if we have a core loss model we will only fit the difference between the model and the data.
|
|
342
358
|
difference = np.array(spectrum[start_channel:end_channel] - model)
|
|
343
|
-
|
|
359
|
+
p_in = self.get_input()
|
|
344
360
|
# find the optimum fitting parameters
|
|
345
|
-
[self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals_smooth, np.array(p_in), ftol=1e-3,
|
|
346
|
-
|
|
361
|
+
#[self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals_smooth, np.array(p_in), ftol=1e-3,
|
|
362
|
+
# args=(energy_scale, difference, False))
|
|
347
363
|
|
|
364
|
+
[self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals3, np.array(p_in, dtype=np.float64),
|
|
365
|
+
args=(energy_scale, difference) ) # , False))
|
|
348
366
|
# 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.
|
|
354
|
-
self.
|
|
355
|
-
|
|
356
|
-
self.
|
|
367
|
+
#self.peak_model = np.zeros(len(self.energy_scale))
|
|
368
|
+
#self.model = np.zeros(len(self.energy_scale))
|
|
369
|
+
#self.model[start_channel:end_channel] = model
|
|
370
|
+
#fit = eels_tools.model_smooth(energy_scale, self.p_out, False)
|
|
371
|
+
fit = eels_tools.gmm(energy_scale, self.p_out) # , False)
|
|
372
|
+
self.peak_model = fit
|
|
373
|
+
|
|
374
|
+
#self.peak_model[start_channel:end_channel] = fit
|
|
375
|
+
#self.dataset.metadata['peak_fit']['edge_model'] = self.model
|
|
376
|
+
#self.model = self.model + self.peak_model
|
|
377
|
+
#self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
|
|
357
378
|
|
|
358
379
|
for key, peak in self.peaks['peaks'].items():
|
|
359
380
|
if key.isdigit():
|
|
@@ -410,13 +431,29 @@ class PeakFitWidget(object):
|
|
|
410
431
|
self.peak_list = []
|
|
411
432
|
self.peaks['peaks'] = {}
|
|
412
433
|
new_number_of_peaks = 0
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
434
|
+
|
|
435
|
+
peaks, prop = scipy.signal.find_peaks(self.peak_model, width=5)
|
|
436
|
+
print(len(peaks), number_of_peaks, len(peaks)>= number_of_peaks)
|
|
437
|
+
if len(peaks) >= number_of_peaks:
|
|
438
|
+
if self.dataset.data_type.name == 'SPECTRUM':
|
|
439
|
+
spectrum = np.array(self.dataset)
|
|
440
|
+
else:
|
|
441
|
+
spectrum = self.dataset.view.get_spectrum()
|
|
442
|
+
for i in range(number_of_peaks):
|
|
443
|
+
self.peak_list.append((f'Peak {i+1}', i))
|
|
444
|
+
p = [self.energy_scale[peaks[i]], np.float32(spectrum[peaks[i]]), np.sqrt(prop['widths'][i])]
|
|
445
|
+
if p[1]>0:
|
|
446
|
+
self.peaks['peaks'][str(new_number_of_peaks)] = {'position': p[0], 'amplitude': p[1], 'width': p[2], 'type': 'Gauss',
|
|
447
|
+
'asymmetry': 0}
|
|
448
|
+
new_number_of_peaks += 1
|
|
449
|
+
else:
|
|
450
|
+
for i in range(number_of_peaks):
|
|
451
|
+
self.peak_list.append((f'Peak {i+1}', i))
|
|
452
|
+
p = self.peak_out_list[i]
|
|
453
|
+
if p[1]>0:
|
|
454
|
+
self.peaks['peaks'][str(new_number_of_peaks)] = {'position': p[0], 'amplitude': p[1], 'width': p[2], 'type': 'Gauss',
|
|
455
|
+
'asymmetry': 0}
|
|
456
|
+
new_number_of_peaks += 1
|
|
420
457
|
self.sidebar[5, 0].value = str(new_number_of_peaks)
|
|
421
458
|
self.peak_list.append((f'add peak', -1))
|
|
422
459
|
|
|
@@ -437,9 +474,13 @@ class PeakFitWidget(object):
|
|
|
437
474
|
"""
|
|
438
475
|
iterations = self.sidebar[4, 0].value
|
|
439
476
|
self.sidebar[5, 0].value = 0
|
|
440
|
-
|
|
477
|
+
|
|
478
|
+
if self.key == self.datasets['_relationship']['low_loss']:
|
|
479
|
+
if 'resolution_function' in self.datasets['_relationship'].keys():
|
|
480
|
+
self.model = np.array(self.datasets['resolution_function'])
|
|
481
|
+
|
|
441
482
|
|
|
442
|
-
self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset, iterations, advanced_present)
|
|
483
|
+
self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset-self.model, iterations, advanced_present)
|
|
443
484
|
|
|
444
485
|
spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
|
|
445
486
|
if spec_dim[1][0] > 0:
|
|
@@ -454,7 +495,9 @@ class PeakFitWidget(object):
|
|
|
454
495
|
self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
|
|
455
496
|
self.dataset.metadata['peak_fit']['peak_out_list'] = self.peak_out_list
|
|
456
497
|
|
|
457
|
-
|
|
498
|
+
peaks, prop = scipy.signal.find_peaks(self.peak_model, width=5)
|
|
499
|
+
|
|
500
|
+
self.sidebar[5, 0].value = str(len(peaks))
|
|
458
501
|
self.update()
|
|
459
502
|
self.plot()
|
|
460
503
|
|
|
@@ -471,15 +514,20 @@ class PeakFitWidget(object):
|
|
|
471
514
|
energy_scale = np.array(self.energy_scale)
|
|
472
515
|
start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
|
|
473
516
|
end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
|
|
474
|
-
energy_scale = self.energy_scale[start_channel:end_channel]
|
|
517
|
+
energy_scale = self.energy_scale # [start_channel:end_channel]
|
|
475
518
|
# select the core loss model if it exists. Otherwise, we will fit to the full spectrum.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
519
|
+
|
|
520
|
+
p_peaks = np.array(p_peaks, dtype=np.float64)
|
|
521
|
+
|
|
522
|
+
fit = eels_tools.gmm(energy_scale, p_peaks) # , False)
|
|
523
|
+
self.peak_model = fit
|
|
524
|
+
#self.peak_model[start_channel:end_channel] = fit
|
|
525
|
+
"""if 'edge_model' in self.dataset.metadata['peak_fit']:
|
|
480
526
|
self.model = self.dataset.metadata['peak_fit']['edge_model'] + self.peak_model
|
|
481
527
|
else:
|
|
482
528
|
self.model = np.zeros(self.dataset.shape)
|
|
529
|
+
"""
|
|
530
|
+
self.model = fit
|
|
483
531
|
|
|
484
532
|
def modify_peak_position(self, value=-1):
|
|
485
533
|
peak_index = self.sidebar[7, 0].value
|
|
@@ -683,22 +731,24 @@ if Qt_available:
|
|
|
683
731
|
self.axis.clear()
|
|
684
732
|
|
|
685
733
|
self.axis.plot(self.energy_scale, spectrum, label='spectrum')
|
|
686
|
-
if '
|
|
687
|
-
|
|
734
|
+
#if 'features' in self.peaks:
|
|
735
|
+
zl = self.datasets[self.peaks['features']]
|
|
736
|
+
self.axis.plot(self.energy_scale, zl, label='zero_loss')
|
|
688
737
|
|
|
689
738
|
if len(self.model) > 1:
|
|
690
739
|
self.axis.plot(self.energy_scale, self.model, label='model')
|
|
691
740
|
self.axis.plot(self.energy_scale, spectrum - self.model, label='difference')
|
|
692
741
|
#self.axis.plot(self.energy_scale, (spectrum - self.model) / np.sqrt(spectrum), label='Poisson')
|
|
693
|
-
|
|
742
|
+
|
|
694
743
|
self.axis.set_xlim(x_limit)
|
|
695
744
|
self.axis.set_ylim(y_limit)
|
|
696
|
-
|
|
697
|
-
|
|
745
|
+
|
|
698
746
|
for index, peak in self.peaks['peaks'].items():
|
|
699
747
|
p = [peak['position'], peak['amplitude'], peak['width']]
|
|
700
748
|
self.axis.plot(self.energy_scale, eels_tools.gauss(self.energy_scale, p))
|
|
701
|
-
|
|
749
|
+
self.axis.legend(loc="upper right")
|
|
750
|
+
self.axis.figure.canvas.draw_idle()
|
|
751
|
+
|
|
702
752
|
def fit_peaks(self):
|
|
703
753
|
"""Fit spectrum with peaks given in peaks dictionary"""
|
|
704
754
|
print('Fitting peaks...')
|
|
@@ -714,10 +764,10 @@ if Qt_available:
|
|
|
714
764
|
spectrum = self.dataset.view.get_spectrum()
|
|
715
765
|
else:
|
|
716
766
|
spectrum = np.array(self.dataset)
|
|
717
|
-
|
|
767
|
+
spectrum -= spectrum.min()-1
|
|
718
768
|
# set the energy scale and fit start and end points
|
|
719
769
|
energy_scale = np.array(self.energy_scale)
|
|
720
|
-
start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
|
|
770
|
+
"""start_channel = np.searchsorted(energy_scale, self.peaks['fit_start'])
|
|
721
771
|
end_channel = np.searchsorted(energy_scale, self.peaks['fit_end'])
|
|
722
772
|
|
|
723
773
|
energy_scale = self.energy_scale[start_channel:end_channel]
|
|
@@ -732,19 +782,29 @@ if Qt_available:
|
|
|
732
782
|
model = np.zeros(end_channel - start_channel)
|
|
733
783
|
|
|
734
784
|
# if we have a core loss model we will only fit the difference between the model and the data.
|
|
785
|
+
|
|
786
|
+
|
|
735
787
|
difference = np.array(spectrum[start_channel:end_channel] - model)
|
|
788
|
+
"""
|
|
789
|
+
difference = spectrum
|
|
790
|
+
if self.key == self.datasets['_relationships']['low_loss']:
|
|
791
|
+
if 'resolution_function' in self.datasets['_relationships'].keys():
|
|
792
|
+
difference -= np.array(self.datasets['_relationships']['resolution_function'])
|
|
793
|
+
self.peaks['peaks']['features'] = 'resolution_function'
|
|
794
|
+
self.model = np.array(self.datasets['_relationships']['resolution_function'])
|
|
736
795
|
|
|
737
796
|
# find the optimum fitting parameters
|
|
738
|
-
[self.p_out, _] = scipy.optimize.leastsq(eels_tools.
|
|
797
|
+
[self.p_out, _] = scipy.optimize.leastsq(eels_tools.residuals3, np.array(p_in), ftol=1e-3,
|
|
739
798
|
args=(energy_scale, difference, False))
|
|
740
799
|
|
|
741
800
|
# 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.
|
|
746
|
-
self.peak_model
|
|
747
|
-
self.
|
|
801
|
+
#self.peak_model = np.zeros(len(self.energy_scale))
|
|
802
|
+
#self.model = np.zeros(len(self.energy_scale))
|
|
803
|
+
#self.model[start_channel:end_channel] = model
|
|
804
|
+
fit = eels_tools.gmm(energy_scale, self.p_out, False)
|
|
805
|
+
self.peak_model = fit
|
|
806
|
+
#self.peak_model[start_channel:end_channel] = fit
|
|
807
|
+
#self.dataset.metadata['peak_fit']['edge_model'] = self.model
|
|
748
808
|
self.model = self.model + self.peak_model
|
|
749
809
|
self.dataset.metadata['peak_fit']['peak_model'] = self.peak_model
|
|
750
810
|
|
|
@@ -773,8 +833,13 @@ if Qt_available:
|
|
|
773
833
|
if 'resolution_function' in self.datasets:
|
|
774
834
|
self.dataset.metadata['model'] = np.array(self.datasets['resolution_function'])
|
|
775
835
|
iterations = int(self.ui.smooth_list.currentIndex())
|
|
836
|
+
|
|
837
|
+
if self.key == self.datasets['_relationships']['low_loss']:
|
|
838
|
+
if 'resolution_function' in self.datasets['_relationships'].keys():
|
|
839
|
+
self.model = np.array(self.datasets['_relationships']['resolution_function'])
|
|
840
|
+
|
|
776
841
|
|
|
777
|
-
self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset, iterations, advanced_present)
|
|
842
|
+
self.peak_model, self.peak_out_list, number_of_peaks = smooth(self.dataset-self.model, iterations, advanced_present)
|
|
778
843
|
|
|
779
844
|
spec_dim = ft.get_dimensions_by_type('SPECTRAL', self.dataset)[0]
|
|
780
845
|
if spec_dim[1][0] > 0:
|
|
@@ -1015,6 +1080,8 @@ if Qt_available:
|
|
|
1015
1080
|
|
|
1016
1081
|
|
|
1017
1082
|
def smooth(dataset, iterations, advanced_present):
|
|
1083
|
+
from pyTEMlib import advanced_eels_tools
|
|
1084
|
+
|
|
1018
1085
|
"""Gaussian mixture model (non-Bayesian)
|
|
1019
1086
|
|
|
1020
1087
|
Fit lots of Gaussian to spectrum and let the program sort it out
|
|
@@ -1023,15 +1090,15 @@ def smooth(dataset, iterations, advanced_present):
|
|
|
1023
1090
|
"""
|
|
1024
1091
|
|
|
1025
1092
|
# TODO: add sensitivity to dialog and the two functions below
|
|
1026
|
-
peaks = dataset.metadata['peak_fit']
|
|
1093
|
+
#peaks = dataset.metadata['peak_fit']
|
|
1094
|
+
|
|
1095
|
+
#peak_model, peak_out_list = eels_tools.find_peaks(dataset, peaks['fit_start'], peaks['fit_end'])
|
|
1096
|
+
peak_model, peak_out_list = eels_tools.gaussian_mixture_model(dataset, p_in=None)
|
|
1027
1097
|
|
|
1028
|
-
peak_model, peak_out_list = eels_tools.find_peaks(dataset,
|
|
1029
|
-
peaks['fit_start'],
|
|
1030
|
-
peaks['fit_end'])
|
|
1031
1098
|
#
|
|
1032
|
-
#
|
|
1099
|
+
# if advanced_present and iterations > 1:
|
|
1033
1100
|
# peak_model, peak_out_list = advanced_eels_tools.smooth(dataset, peaks['fit_start'],
|
|
1034
|
-
#
|
|
1101
|
+
# peaks['fit_end'], iterations=iterations)
|
|
1035
1102
|
# else:
|
|
1036
1103
|
# peak_model, peak_out_list = eels_tools.find_peaks(dataset, peaks['fit_start'], peaks['fit_end'])
|
|
1037
1104
|
# peak_out_list = [peak_out_list]
|
|
@@ -1045,3 +1112,18 @@ def smooth(dataset, iterations, advanced_present):
|
|
|
1045
1112
|
number_of_peaks = np.searchsorted(area * -1, -np.average(area))
|
|
1046
1113
|
|
|
1047
1114
|
return peak_model, peak_out_list, number_of_peaks
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
def gauss(x, p): # p[0]==mean, p[1]= amplitude p[2]==fwhm,
|
|
1118
|
+
"""Gaussian Function
|
|
1119
|
+
|
|
1120
|
+
p[0]==mean, p[1]= amplitude p[2]==fwhm
|
|
1121
|
+
area = np.sqrt(2* np.pi)* p[1] * np.abs(p[2] / 2.3548)
|
|
1122
|
+
FWHM = 2 * np.sqrt(2 np.log(2)) * sigma = 2.3548 * sigma
|
|
1123
|
+
sigma = FWHM/3548
|
|
1124
|
+
"""
|
|
1125
|
+
if p[2] == 0:
|
|
1126
|
+
return x * 0.
|
|
1127
|
+
else:
|
|
1128
|
+
return p[1] * np.exp(-(x - p[0]) ** 2 / (2.0 * (p[2] / 2.3548) ** 2))
|
|
1129
|
+
|
pyTEMlib/probe_tools.py
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import pyTEMlib.image_tools
|
|
4
4
|
import scipy.ndimage as ndimage
|
|
5
|
+
import skimage
|
|
6
|
+
|
|
7
|
+
get_wavelength = pyTEMlib.image_tools.get_wavelength
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
|
|
7
11
|
def make_gauss(size_x, size_y, width=1.0, x0=0.0, y0=0.0, intensity=1.0):
|
|
@@ -23,6 +27,7 @@ def make_lorentz(size_x, size_y, gamma=1.0, x0=0., y0=0., intensity=1.):
|
|
|
23
27
|
x, y = np.mgrid[-size_x:size_x, -size_y:size_y]
|
|
24
28
|
g = gamma / (2 * np.pi) / np.power(((x - x0) ** 2 + (y - y0) ** 2 + gamma ** 2), 1.5)
|
|
25
29
|
probe = g / g.sum() * intensity
|
|
30
|
+
|
|
26
31
|
return probe
|
|
27
32
|
|
|
28
33
|
|
|
@@ -32,6 +37,7 @@ def zero_loss_peak_weight():
|
|
|
32
37
|
y = [0.0143, 0.0193, 0.0281, 0.0440, 0.0768, 0.1447, 0.2785, 0.4955, 0.7442, 0.9380, 1.0000, 0.9483, 0.8596,
|
|
33
38
|
0.7620, 0.6539, 0.5515, 0.4478, 0.3500, 0.2683, 0.1979, 0.1410, 0.1021, 0.0752, 0.0545, 0.0401, 0.0300,
|
|
34
39
|
0.0229, 0.0176, 0.0139]
|
|
40
|
+
|
|
35
41
|
return x, y
|
|
36
42
|
|
|
37
43
|
|
|
@@ -92,6 +98,7 @@ def get_chi(ab, size_x, size_y, verbose=False):
|
|
|
92
98
|
chi = make_chi(phi, theta, ab)
|
|
93
99
|
|
|
94
100
|
# Aperture function
|
|
101
|
+
print(aperture_angle)
|
|
95
102
|
mask = theta >= aperture_angle
|
|
96
103
|
|
|
97
104
|
aperture = np.ones((size_x, size_y), dtype=float)
|
|
@@ -106,15 +113,15 @@ def print_aberrations(ab):
|
|
|
106
113
|
output += f"Aberrations [nm] for acceleration voltage: {ab['acceleration_voltage'] / 1e3:.0f} kV"
|
|
107
114
|
output += '<table>'
|
|
108
115
|
output += f"<tr><td> C10 </td><td> {ab['C10']:.1f} </tr>"
|
|
109
|
-
output += f"<tr><td> C12a </td><td> {ab['C12a']:20.1f} <td> C12b </td><td> {ab['C12b']:20.1f} </tr>"
|
|
110
|
-
output += f"<tr><td> C21a </td><td> {ab['C21a']:.1f} <td> C21b </td><td> {ab['C21b']:.1f} "
|
|
111
|
-
output += f" <td> C23a </td><td> {ab['C23a']:.1f} <td> C23b </td><td> {ab['C23b']:.1f} </tr>"
|
|
116
|
+
output += f"<tr><td> C12a (A1) </td><td> {ab['C12a']:20.1f} <td> C12b (A1) </td><td> {ab['C12b']:20.1f} </tr>"
|
|
117
|
+
output += f"<tr><td> C21a (B2) </td><td> {ab['C21a']:.1f} <td> C21b (B2)</td><td> {ab['C21b']:.1f} "
|
|
118
|
+
output += f" <td> C23a (A2) </td><td> {ab['C23a']:.1f} <td> C23b (A2) </td><td> {ab['C23b']:.1f} </tr>"
|
|
112
119
|
output += f"<tr><td> C30 </td><td> {ab['C30']:.1f} </tr>"
|
|
113
|
-
output += f"<tr><td> C32a </td><td> {ab['C32a']:20.1f} <td> C32b </td><td> {ab['C32b']:20.1f} "
|
|
114
|
-
output += f"<td> C34a </td><td> {ab['C34a']:20.1f} <td> C34b </td><td> {ab['C34b']:20.1f} </tr>"
|
|
115
|
-
output += f"<tr><td> C41a </td><td> {ab['C41a']:.3g} <td> C41b </td><td> {ab['C41b']:.3g} "
|
|
116
|
-
output += f" <td> C43a </td><td> {ab['C43a']:.3g} <td> C43b </td><td> {ab['C41b']:.3g} "
|
|
117
|
-
output += f" <td> C45a </td><td> {ab['C45a']:.3g} <td> C45b </td><td> {ab['C45b']:.3g} </tr>"
|
|
120
|
+
output += f"<tr><td> C32a (S3) </td><td> {ab['C32a']:20.1f} <td> C32b (S3)</td><td> {ab['C32b']:20.1f} "
|
|
121
|
+
output += f"<td> C34a (A3) </td><td> {ab['C34a']:20.1f} <td> C34b (A3) </td><td> {ab['C34b']:20.1f} </tr>"
|
|
122
|
+
output += f"<tr><td> C41a (B4) </td><td> {ab['C41a']:.3g} <td> C41b (B4) </td><td> {ab['C41b']:.3g} "
|
|
123
|
+
output += f" <td> C43a (D4) </td><td> {ab['C43a']:.3g} <td> C43b (D4) </td><td> {ab['C41b']:.3g} "
|
|
124
|
+
output += f" <td> C45a (A4) </td><td> {ab['C45a']:.3g} <td> C45b (A4)</td><td> {ab['C45b']:.3g} </tr>"
|
|
118
125
|
output += f"<tr><td> C50 </td><td> {ab['C50']:.3g} </tr>"
|
|
119
126
|
output += f"<tr><td> C52a </td><td> {ab['C52a']:20.1f} <td> C52b </td><td> {ab['C52b']:20.1f} "
|
|
120
127
|
output += f"<td> C54a </td><td> {ab['C54a']:20.1f} <td> C54b </td><td> {ab['C54b']:20.1f} "
|
|
@@ -158,11 +165,44 @@ def get_ronchigram(size, ab, scale='mrad'):
|
|
|
158
165
|
extent = [-fov_mrad, fov_mrad, -fov_mrad, fov_mrad]
|
|
159
166
|
ylabel = 'reciprocal distance [mrad]'
|
|
160
167
|
|
|
168
|
+
ab['chi'] = chi
|
|
161
169
|
ab['ronchi_extent'] = extent
|
|
162
170
|
ab['ronchi_label'] = ylabel
|
|
163
171
|
return ronchigram
|
|
164
172
|
|
|
165
173
|
|
|
174
|
+
def make_probe (chi, aperture):
|
|
175
|
+
chi2 = np.fft.ifftshift(chi)
|
|
176
|
+
chiT = np.fft.ifftshift (np.vectorize(complex)(np.cos(chi2), -np.sin(chi2)) )
|
|
177
|
+
## Aply aperture function
|
|
178
|
+
chiT = chiT*aperture
|
|
179
|
+
## inverse fft of aberration function
|
|
180
|
+
i2 = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift (chiT)))
|
|
181
|
+
## intensity
|
|
182
|
+
probe = np.real(i2 * np.conjugate(i2))
|
|
183
|
+
|
|
184
|
+
return probe
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_probe( ab, sizeX, sizeY, scale = 'mrad', verbose= True):
|
|
188
|
+
|
|
189
|
+
chi, A_k = get_chi( ab, sizeX, sizeY, verbose= False)
|
|
190
|
+
probe = make_probe (chi, A_k)
|
|
191
|
+
|
|
192
|
+
return probe, A_k, chi
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_probe_large(ab):
|
|
196
|
+
ab['FOV'] = 20
|
|
197
|
+
sizeX = 512*2
|
|
198
|
+
probe, A_k, chi = pyTEMlib.probe_tools.get_probe( ab, sizeX, sizeX, scale = 'mrad', verbose= True)
|
|
199
|
+
|
|
200
|
+
res = np.zeros((512, 512))
|
|
201
|
+
res[256-32:256+32, 256-32:256+32 ] = skimage.transform.resize(probe, (64, 64))
|
|
202
|
+
|
|
203
|
+
return res
|
|
204
|
+
|
|
205
|
+
|
|
166
206
|
def get_chi_2(ab, u, v):
|
|
167
207
|
chi1 = ab['C10'] * (u ** 2 + v ** 2) / 2 \
|
|
168
208
|
+ ab['C12a'] * (u ** 2 - v ** 2) / 2 \
|
|
@@ -400,6 +440,22 @@ def get_target_aberrations(TEM_name, acceleration_voltage):
|
|
|
400
440
|
ab['TEM_name'] = TEM_name
|
|
401
441
|
|
|
402
442
|
ab['wavelength'] = pyTEMlib.image_tools.get_wavelength(ab['acceleration_voltage'])
|
|
443
|
+
|
|
444
|
+
if TEM_name == 'Spectra300':
|
|
445
|
+
ab = {'C10': 0, 'C12a': 0, 'C12b': 0.38448128113770325,
|
|
446
|
+
'C21a': -68.45251255685642, 'C21b': 64.85359774641199, 'C23a': 11.667578600494137, 'C23b': -29.775627778458194,
|
|
447
|
+
'C30': 123,
|
|
448
|
+
'C32a': 95.3047364258614, 'C32b': -189.72105710231244, 'C34a': -47.45099594807912, 'C34b': -94.67424667529909,
|
|
449
|
+
'C41a': -905.31842572806, 'C41b': 981.316128853203, 'C43a': 4021.8433526960034, 'C43b': 131.72716642732158,
|
|
450
|
+
'C45a': -4702.390968272048, 'C45b': -208.25028574642903, 'C50': -663.1,
|
|
451
|
+
'C50': 552000., 'C52a': -0., 'C52b': 0.,
|
|
452
|
+
'C54a': -0., 'C54b': -0., 'C56a': -36663.643489934424, 'C56b': 21356.079837905396,
|
|
453
|
+
'acceleration_voltage': 200000,
|
|
454
|
+
'FOV': 34.241659495148205,
|
|
455
|
+
'Cc': 1* 1e6,
|
|
456
|
+
'convergence_angle': 30,
|
|
457
|
+
'wavelength': 0.0025079340450548005}
|
|
458
|
+
|
|
403
459
|
return ab
|
|
404
460
|
|
|
405
461
|
|
|
@@ -456,6 +512,7 @@ def get_ronchigram_2(size, ab, scale='mrad', threshold=3):
|
|
|
456
512
|
extent = [-fov_mrad, fov_mrad, -fov_mrad, fov_mrad]
|
|
457
513
|
ylabel = 'reciprocal distance [mrad]'
|
|
458
514
|
|
|
515
|
+
ab['chi'] = chi
|
|
459
516
|
ab['ronchi_extent'] = extent
|
|
460
517
|
ab['ronchi_label'] = ylabel
|
|
461
518
|
|
pyTEMlib/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: pyTEMlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2025.2.2
|
|
4
4
|
Summary: pyTEM: TEM Data Quantification library through a model-based approach
|
|
5
5
|
Home-page: https://pycroscopy.github.io/pyTEMlib/about.html
|
|
6
6
|
Author: Gerd Duscher
|
|
@@ -38,9 +38,19 @@ Requires-Dist: ipympl
|
|
|
38
38
|
Requires-Dist: spglib
|
|
39
39
|
Requires-Dist: scikit-image
|
|
40
40
|
Requires-Dist: scikit-learn
|
|
41
|
-
Requires-Dist: pyNSID
|
|
42
|
-
Requires-Dist: sidpy
|
|
43
|
-
Requires-Dist: SciFiReaders
|
|
41
|
+
Requires-Dist: pyNSID>=0.0.7
|
|
42
|
+
Requires-Dist: sidpy>=0.12.1
|
|
43
|
+
Requires-Dist: SciFiReaders>=0.0.8
|
|
44
|
+
Dynamic: author
|
|
45
|
+
Dynamic: author-email
|
|
46
|
+
Dynamic: classifier
|
|
47
|
+
Dynamic: description
|
|
48
|
+
Dynamic: home-page
|
|
49
|
+
Dynamic: keywords
|
|
50
|
+
Dynamic: license
|
|
51
|
+
Dynamic: platform
|
|
52
|
+
Dynamic: requires-dist
|
|
53
|
+
Dynamic: summary
|
|
44
54
|
|
|
45
55
|
pyTEMlib
|
|
46
56
|
========
|
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
pyTEMlib/__init__.py,sha256=nEN93amIEoZxO7rJgN71ABeCoXrnmaywBbE97l5lPio,178
|
|
2
|
-
pyTEMlib/animation.py,sha256=
|
|
3
|
-
pyTEMlib/atom_tools.py,sha256=
|
|
2
|
+
pyTEMlib/animation.py,sha256=G9ykYo6yB5jexjKienShO9C2b8xEXKbf7t47apwcwTw,25188
|
|
3
|
+
pyTEMlib/atom_tools.py,sha256=uoyZOs0lZKyBwGH6onKSAKzA6IxseqHpgw7ZTos9Nd0,7335
|
|
4
4
|
pyTEMlib/config_dir.py,sha256=4evlo9P2Yht-AnqaLI-WweLjDQcislbAP3I7P7EZsPU,2085
|
|
5
|
-
pyTEMlib/core_loss_widget.py,sha256=
|
|
5
|
+
pyTEMlib/core_loss_widget.py,sha256=EbCn2imCjzeLAL2h9n87APHPou1G4vvN-kA4_RQs_OE,30991
|
|
6
6
|
pyTEMlib/crystal_tools.py,sha256=g4OXyvd5NLw7vaXhjDP3P6VZpVV6eiyuPn8MdgR2amI,61652
|
|
7
7
|
pyTEMlib/diffraction_plot.py,sha256=pM5d3bdBGa8LlPZ5lw8sLT94mlYTXELxPLv-jUP2FWY,27959
|
|
8
8
|
pyTEMlib/dynamic_scattering.py,sha256=O9MxnxfndWJ2VhQRjksKNQ4yY7y-gN_hitRQ4Qox4ns,9472
|
|
9
9
|
pyTEMlib/eds_tools.py,sha256=Ilof2Cars-1ILXx5g2RsU2G4BgrPwjOHgQ7-OabmbrU,28000
|
|
10
|
-
pyTEMlib/eels_dialog.py,sha256=
|
|
10
|
+
pyTEMlib/eels_dialog.py,sha256=NmPjO1SVjxzah2cCEK0bR1AqwX7Dl6xwejTZkruqIUA,32619
|
|
11
11
|
pyTEMlib/eels_dialog_utilities.py,sha256=73W9jFbPx-eeLEiSaBptTgGLr40bIYYfSyzLnZbhfvo,51761
|
|
12
|
-
pyTEMlib/eels_tools.py,sha256=
|
|
13
|
-
pyTEMlib/file_tools.py,sha256=
|
|
12
|
+
pyTEMlib/eels_tools.py,sha256=7gfDmu-CqfatVmtCPYwObJZsP5RMDx3bmFXclZzJINE,88739
|
|
13
|
+
pyTEMlib/file_tools.py,sha256=gdP42-99Pjnl0tri50x8QXc33ye-1bs6Yiof3JKVn7I,59969
|
|
14
14
|
pyTEMlib/file_tools_qt.py,sha256=tLZACS4JyGH_AOzNR_SGAhjA01y4VJB261opPhGMlm8,7223
|
|
15
15
|
pyTEMlib/graph_tools.py,sha256=iu0Y2hIPU6CkQHQEh-dI1vKnUHnSNXx4-CXs2M-1Sr8,44097
|
|
16
16
|
pyTEMlib/graph_viz.py,sha256=m5PwSn6l2r0bsaLWBDSHc9IGR3_PneG2BrZgnEdi07I,13644
|
|
17
17
|
pyTEMlib/image_dialog.py,sha256=F-ZgKq7UnMtPPd1b9eqb7t8MXDfWN-8hVKwB2Il0x28,6235
|
|
18
18
|
pyTEMlib/image_dlg.py,sha256=n5gradDiYOFGEQ3k_Wlk9RUYYzl4bl_hKLzNVcYteNE,5694
|
|
19
|
-
pyTEMlib/image_tools.py,sha256=
|
|
20
|
-
pyTEMlib/info_widget.py,sha256=
|
|
19
|
+
pyTEMlib/image_tools.py,sha256=guHd29VLo2z0KXz1-XxcHYWqOiHGoKZ0A9SE_FdSO2g,52655
|
|
20
|
+
pyTEMlib/info_widget.py,sha256=lkzQOuNVlkaasiZDtc5UtYk541-plYNfnW4DQwQB_iA,53467
|
|
21
|
+
pyTEMlib/info_widget3.py,sha256=QSbdSj6m57KQTir2fNhulVgjOu9EQL31c-9SzTlghnE,55495
|
|
21
22
|
pyTEMlib/interactive_image.py,sha256=5PwypcA1OjLAD-fi8bmWWFHuOjdIPVY9Dh59V24WuDA,34
|
|
22
23
|
pyTEMlib/kinematic_scattering.py,sha256=CUdJnclkok7d8qm_jDUF92MVHrmaTeMkKdvxxB6AqvA,43309
|
|
23
|
-
pyTEMlib/low_loss_widget.py,sha256=
|
|
24
|
+
pyTEMlib/low_loss_widget.py,sha256=0SxHOAuUnuNjDrJSNS2PeWD6hjyuB5FCYxmVfsDTq5c,25573
|
|
24
25
|
pyTEMlib/microscope.py,sha256=iigUF1UImHEfmL2wqEBBj3aNRgEYouDbIln8VCo4_KM,1545
|
|
25
|
-
pyTEMlib/peak_dialog.py,sha256
|
|
26
|
+
pyTEMlib/peak_dialog.py,sha256=r3mhYvECC9niFFItS1oGa96F2nFthsE5kfpA3yVXQaE,51872
|
|
26
27
|
pyTEMlib/peak_dlg.py,sha256=qcjcnhwpGa4jBCeXzwQz9sCyX-tHsLLQ67ToqfKOiQY,11550
|
|
27
|
-
pyTEMlib/probe_tools.py,sha256=
|
|
28
|
+
pyTEMlib/probe_tools.py,sha256=xVwoThZSSOY6hjUMO53va9R_qI7SL5dfwIigz6sd3DI,29242
|
|
28
29
|
pyTEMlib/sidpy_tools.py,sha256=0oIx-qMtEmcZmLazQKW19dd-KoxyY3B15aIeMcyHA8E,4878
|
|
29
30
|
pyTEMlib/simulation_tools.py,sha256=RmegD5TpQMU68uASvzZWVplAqs7bM5KkF6bWDWLjyc0,2799
|
|
30
|
-
pyTEMlib/version.py,sha256=
|
|
31
|
+
pyTEMlib/version.py,sha256=6GC1KyGj1D_9Hs8_3OJrq7lHdhziwk9qllcxLoWcEns,94
|
|
31
32
|
pyTEMlib/xrpa_x_sections.py,sha256=m4gaH7gaJiNi-CsIT9aKoH4fB6MQIAe876kxEmzSebI,1825392
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
pytemlib-0.2025.2.2.dist-info/LICENSE,sha256=7HdBF6SXIBd38bHOKkQd4DYR1KV-OYm9mwB16fM-984,1062
|
|
34
|
+
pytemlib-0.2025.2.2.dist-info/METADATA,sha256=tOVUZqAPzyfc6ZXDHRyz5jp6OGzNJvv9CcyE-1bxtxI,3493
|
|
35
|
+
pytemlib-0.2025.2.2.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
|
|
36
|
+
pytemlib-0.2025.2.2.dist-info/entry_points.txt,sha256=zn2yO1IWTutI3c7C9e3GdARCvm43JURoOhqQ8YylV4Y,43
|
|
37
|
+
pytemlib-0.2025.2.2.dist-info/top_level.txt,sha256=rPLVH0UJxrPSPgSoKScTjL1K_X69JFzsYYnDnYTYIlU,9
|
|
38
|
+
pytemlib-0.2025.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|