pyTEMlib 0.2025.4.2__py3-none-any.whl → 0.2025.9.1__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 (94) hide show
  1. build/lib/pyTEMlib/__init__.py +33 -0
  2. build/lib/pyTEMlib/animation.py +640 -0
  3. build/lib/pyTEMlib/atom_tools.py +238 -0
  4. build/lib/pyTEMlib/config_dir.py +31 -0
  5. build/lib/pyTEMlib/crystal_tools.py +1219 -0
  6. build/lib/pyTEMlib/diffraction_plot.py +756 -0
  7. build/lib/pyTEMlib/dynamic_scattering.py +293 -0
  8. build/lib/pyTEMlib/eds_tools.py +826 -0
  9. build/lib/pyTEMlib/eds_xsections.py +432 -0
  10. build/lib/pyTEMlib/eels_tools/__init__.py +44 -0
  11. build/lib/pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  12. build/lib/pyTEMlib/eels_tools/eels_database.py +134 -0
  13. build/lib/pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  14. build/lib/pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  15. build/lib/pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  16. build/lib/pyTEMlib/file_reader.py +274 -0
  17. build/lib/pyTEMlib/file_tools.py +811 -0
  18. build/lib/pyTEMlib/get_bote_salvat.py +69 -0
  19. build/lib/pyTEMlib/graph_tools.py +1153 -0
  20. build/lib/pyTEMlib/graph_viz.py +599 -0
  21. build/lib/pyTEMlib/image/__init__.py +37 -0
  22. build/lib/pyTEMlib/image/image_atoms.py +270 -0
  23. build/lib/pyTEMlib/image/image_clean.py +197 -0
  24. build/lib/pyTEMlib/image/image_distortion.py +299 -0
  25. build/lib/pyTEMlib/image/image_fft.py +277 -0
  26. build/lib/pyTEMlib/image/image_graph.py +926 -0
  27. build/lib/pyTEMlib/image/image_registration.py +316 -0
  28. build/lib/pyTEMlib/image/image_utilities.py +309 -0
  29. build/lib/pyTEMlib/image/image_window.py +421 -0
  30. build/lib/pyTEMlib/image_tools.py +699 -0
  31. build/lib/pyTEMlib/interactive_image.py +1 -0
  32. build/lib/pyTEMlib/kinematic_scattering.py +1196 -0
  33. build/lib/pyTEMlib/microscope.py +61 -0
  34. build/lib/pyTEMlib/probe_tools.py +906 -0
  35. build/lib/pyTEMlib/sidpy_tools.py +153 -0
  36. build/lib/pyTEMlib/simulation_tools.py +104 -0
  37. build/lib/pyTEMlib/test.py +437 -0
  38. build/lib/pyTEMlib/utilities.py +314 -0
  39. build/lib/pyTEMlib/version.py +5 -0
  40. build/lib/pyTEMlib/xrpa_x_sections.py +20976 -0
  41. pyTEMlib/__init__.py +25 -3
  42. pyTEMlib/animation.py +31 -22
  43. pyTEMlib/atom_tools.py +29 -34
  44. pyTEMlib/config_dir.py +2 -28
  45. pyTEMlib/crystal_tools.py +129 -165
  46. pyTEMlib/eds_tools.py +559 -342
  47. pyTEMlib/eds_xsections.py +432 -0
  48. pyTEMlib/eels_tools/__init__.py +44 -0
  49. pyTEMlib/eels_tools/core_loss_tools.py +751 -0
  50. pyTEMlib/eels_tools/eels_database.py +134 -0
  51. pyTEMlib/eels_tools/low_loss_tools.py +655 -0
  52. pyTEMlib/eels_tools/peak_fit_tools.py +175 -0
  53. pyTEMlib/eels_tools/zero_loss_tools.py +264 -0
  54. pyTEMlib/file_reader.py +274 -0
  55. pyTEMlib/file_tools.py +260 -1130
  56. pyTEMlib/get_bote_salvat.py +69 -0
  57. pyTEMlib/graph_tools.py +101 -174
  58. pyTEMlib/graph_viz.py +150 -0
  59. pyTEMlib/image/__init__.py +37 -0
  60. pyTEMlib/image/image_atoms.py +270 -0
  61. pyTEMlib/image/image_clean.py +197 -0
  62. pyTEMlib/image/image_distortion.py +299 -0
  63. pyTEMlib/image/image_fft.py +277 -0
  64. pyTEMlib/image/image_graph.py +926 -0
  65. pyTEMlib/image/image_registration.py +316 -0
  66. pyTEMlib/image/image_utilities.py +309 -0
  67. pyTEMlib/image/image_window.py +421 -0
  68. pyTEMlib/image_tools.py +154 -928
  69. pyTEMlib/kinematic_scattering.py +1 -1
  70. pyTEMlib/probe_tools.py +1 -1
  71. pyTEMlib/test.py +437 -0
  72. pyTEMlib/utilities.py +314 -0
  73. pyTEMlib/version.py +2 -3
  74. pyTEMlib/xrpa_x_sections.py +14 -10
  75. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/METADATA +13 -16
  76. pytemlib-0.2025.9.1.dist-info/RECORD +86 -0
  77. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/WHEEL +1 -1
  78. pytemlib-0.2025.9.1.dist-info/top_level.txt +6 -0
  79. pyTEMlib/core_loss_widget.py +0 -721
  80. pyTEMlib/eels_dialog.py +0 -754
  81. pyTEMlib/eels_dialog_utilities.py +0 -1199
  82. pyTEMlib/eels_tools.py +0 -2359
  83. pyTEMlib/file_tools_qt.py +0 -193
  84. pyTEMlib/image_dialog.py +0 -158
  85. pyTEMlib/image_dlg.py +0 -146
  86. pyTEMlib/info_widget.py +0 -1086
  87. pyTEMlib/info_widget3.py +0 -1120
  88. pyTEMlib/low_loss_widget.py +0 -479
  89. pyTEMlib/peak_dialog.py +0 -1129
  90. pyTEMlib/peak_dlg.py +0 -286
  91. pytemlib-0.2025.4.2.dist-info/RECORD +0 -38
  92. pytemlib-0.2025.4.2.dist-info/top_level.txt +0 -1
  93. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/entry_points.txt +0 -0
  94. {pytemlib-0.2025.4.2.dist-info → pytemlib-0.2025.9.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,175 @@
1
+ """
2
+ peak_fit-tools of eels_tools
3
+ Model based quantification of electron energy-loss data
4
+ Copyright by Gerd Duscher
5
+
6
+ The University of Tennessee, Knoxville
7
+ Department of Materials Science & Engineering
8
+
9
+
10
+ Units:
11
+ everything is in SI units, except length is given in nm and angles in mrad.
12
+
13
+ Usage:
14
+ See the notebooks for examples of these routines
15
+
16
+ All the input and output is done through a dictionary which is to be found in the meta_data
17
+ attribute of the sidpy.Dataset
18
+ """
19
+ import numpy as np
20
+ from numba import jit
21
+
22
+ import scipy
23
+ import sidpy
24
+
25
+
26
+ # ###############################################################
27
+ # Peak Fit Functions
28
+ # ################################################################
29
+
30
+ def residuals_smooth(p: np.ndarray,
31
+ x: np.ndarray,
32
+ y: np.ndarray,
33
+ only_positive_intensity: bool) -> np.ndarray:
34
+ """part of fit"""
35
+ err = y - model_smooth(x, p, only_positive_intensity)
36
+ return err
37
+
38
+
39
+ def model_smooth(x: np.ndarray,
40
+ p: np.ndarray,
41
+ only_positive_intensity: bool = False) -> np.ndarray:
42
+ """part of fit"""
43
+
44
+ y = np.zeros(len(x))
45
+ number_of_peaks = int(len(p) / 3)
46
+ for i in range(number_of_peaks):
47
+ if only_positive_intensity:
48
+ p[i * 3 + 1] = abs(p[i * 3 + 1])
49
+ p[i * 3 + 2] = abs(p[i * 3 + 2])
50
+ if p[i * 3 + 2] > abs(p[i * 3]) * 4.29193 / 2.0:
51
+ # width cannot extend beyond zero, maximum is FWTM/2
52
+ p[i * 3 + 2] = abs(p[i * 3]) * 4.29193 / 2.0
53
+ y = y + gauss(x, p[i * 3:])
54
+ return y
55
+
56
+ # ###############################################################
57
+ # Gaussian Mixing Model Functions
58
+ # ################################################################
59
+
60
+ @jit
61
+ def gauss(x, p):
62
+ """Gaussian Function
63
+
64
+ p[0]==mean, p[1]= amplitude p[2]==fwhm
65
+ area = np.sqrt(2* np.pi)* p[1] * np.abs(p[2] / 2.3548)
66
+ FWHM = 2 * np.sqrt(2 np.log(2)) * sigma = 2.3548 * sigma
67
+ sigma = FWHM/3548
68
+ """
69
+ if p[2] == 0:
70
+ return x * 0.
71
+ return p[1] * np.exp(-(x - p[0])**2 / (2.0 * (p[2] / 2.3548)**2))
72
+
73
+
74
+ @jit
75
+ def gmm(x, p):
76
+ """Gaussian Mixture Model"""
77
+ y = np.zeros(len(x))
78
+ number_of_peaks= int(len(p)/3)
79
+ for i in range(number_of_peaks):
80
+ index = i*3
81
+ p[index + 1] = p[index + 1]
82
+ # print(p[index + 1])
83
+ p[index + 2] = abs(p[index + 2])
84
+ y = y + gauss(x, p[index:index+3])
85
+ return y
86
+
87
+ @jit
88
+ def residuals3(pp, xx, yy):
89
+ """Residuals for Gaussian Mixture Model"""
90
+ err = yy - gmm(xx, pp)
91
+ return err
92
+
93
+
94
+ def find_maxima(y, number_of_peaks):
95
+ """ find the first most prominent peaks
96
+
97
+ peaks are then sorted by energy
98
+
99
+ Parameters
100
+ ----------
101
+ y: numpy array
102
+ (part) of spectrum
103
+ number_of_peaks: int
104
+
105
+ Returns
106
+ -------
107
+ numpy array
108
+ indices of peaks
109
+ """
110
+ blurred2 = scipy.ndimage.gaussian_filter(y, sigma=2)
111
+ peaks, _ = scipy.signal.find_peaks(blurred2)
112
+ prominences = scipy.signal.peak_prominences(blurred2, peaks)[0]
113
+ prominences_sorted = np.argsort(prominences)
114
+ peaks = peaks[prominences_sorted[-number_of_peaks:]]
115
+
116
+ peak_indices = np.argsort(peaks)
117
+ return peaks[peak_indices]
118
+
119
+
120
+ def find_peaks(dataset, energy_scale): #, fit_start, fit_end, sensitivity=2):
121
+ """find peaks in spectrum"""
122
+
123
+ peaks, _ = scipy.signal.find_peaks(np.abs(dataset)+1, width=5)
124
+ results_half = scipy.signal.peak_widths(np.abs(dataset)+1, peaks, rel_height=0.5)[0]
125
+ disp = energy_scale[1] - energy_scale[0]
126
+ p_in = []
127
+ if len(peaks) > 0:
128
+ p_in = np.ravel([[energy_scale[peaks[i]], dataset[peaks[i]],
129
+ results_half[i]*disp] for i in range(len(peaks))])
130
+ return p_in # model, p_in
131
+
132
+
133
+ def gaussian_mixture_model(dataset, p_in=None):
134
+ """Fit a Gaussian mixture model to a spectrum or a spectrum image"""
135
+ peak_model = None
136
+ if isinstance(dataset, sidpy.Dataset):
137
+ if dataset.data_type.name == 'SPECTRAL_IMAGE':
138
+ if hasattr(dataset.view, 'get_spectrum'):
139
+ spectrum = dataset.view.get_spectrum()
140
+ else:
141
+ spectrum = dataset[0,0]
142
+ spectrum.data_type = 'SPECTRUM'
143
+ else:
144
+ spectrum = dataset
145
+ spectrum.data_type = 'SPECTRUM'
146
+ energy_scale = dataset.get_spectral_dims(return_axis=True)[0].values
147
+ else:
148
+ spectrum = np.array(dataset)
149
+ energy_scale = np.arange(len(spectrum))
150
+ spectrum = np.array(spectrum)
151
+ #spectrum -= np.min(spectrum)-1
152
+ if p_in is None:
153
+ p_in = find_peaks(spectrum, energy_scale)
154
+
155
+ p = fit_gmm(energy_scale, np.array(spectrum), list(p_in))
156
+ peak_model = gmm(energy_scale, p)
157
+ return peak_model, p
158
+
159
+
160
+ def fit_gmm(x, y, pin):
161
+ """fit a Gaussian mixture model to a spectrum"""
162
+ [p, _] = scipy.optimize.leastsq(residuals3, pin, args=(x, y),maxfev = 10000)
163
+ return p
164
+
165
+
166
+ def sort_peaks(p, peak_shape):
167
+ """sort fitting parameters by peak position"""
168
+ number_of_peaks = int(len(p) / 3)
169
+ p3 = np.reshape(p, (number_of_peaks, 3))
170
+ sort_pin = np.argsort(p3[:, 0])
171
+
172
+ p = p3[sort_pin].flatten()
173
+ peak_shape = np.array(peak_shape)[sort_pin].tolist()
174
+
175
+ return p, peak_shape
@@ -0,0 +1,264 @@
1
+ """
2
+ zero-loss tools part of eels tools in pyTEMlib
3
+ """
4
+
5
+ import numpy as np
6
+ import scipy
7
+
8
+ import sidpy
9
+ from sidpy.proc.fitter import SidFitter
10
+
11
+ from ..utilities import lorentz, gauss
12
+
13
+
14
+ def zero_loss_function(x, p):
15
+ """ zero-loss function as product of two lorentzians """
16
+ return zl_func(x, *p)
17
+
18
+
19
+ def zl_func(x, center1, amplitude1, width1, center2, amplitude2, width2):
20
+ """ zero loss function as product of two lorentzians """
21
+ zero_loss = lorentz(x, center1, amplitude1, width1) * lorentz(x, center2, amplitude2, width2)
22
+ return zero_loss
23
+
24
+
25
+ def zl(x, p, p_zl):
26
+ """zero-loss function"""
27
+ p_zl_local = p_zl.copy()
28
+ p_zl_local[2] += p[0]
29
+ p_zl_local[5] += p[0]
30
+ zero_loss = zero_loss_function(x, p_zl_local)
31
+ return p[1] * zero_loss / zero_loss.max()
32
+
33
+
34
+ def get_channel_zero(spectrum: np.ndarray, energy: np.ndarray, width: int = 8):
35
+ """Determine shift of energy scale according to zero-loss peak position
36
+
37
+ This function assumes that the zero loss peak is the maximum of the spectrum.
38
+ """
39
+
40
+ zero = scipy.signal.find_peaks(spectrum/np.max(spectrum), height=0.98)[0][0]
41
+ width = int(width/2)
42
+ x = np.array(energy[int(zero-width):int(zero+width)])
43
+ y = np.array(spectrum[int(zero-width):int(zero+width)]).copy()
44
+
45
+ y[np.nonzero(y <= 0)] = 1e-12
46
+
47
+ p0 = [energy[zero], spectrum.max(), .5] # Initial guess is a normal distribution
48
+
49
+ def errfunc(pp, xx, yy):
50
+ return (gauss(xx, pp) - yy) / np.sqrt(yy) # Distance to the target function
51
+
52
+ [p1, _] = scipy.optimize.leastsq(errfunc, np.array(p0[:]), args=(x, y))
53
+ fit_mu, _, fwhm = p1
54
+
55
+ return fwhm, fit_mu
56
+
57
+
58
+ def get_zero_loss_energy(dataset: sidpy.Dataset) -> np.ndarray:
59
+ """ Determine zero-loss peaks of EELS spectral sidpy dataset """
60
+ spectrum = dataset.sum(axis=tuple(range(dataset.ndim - 1)))
61
+ startx = scipy.signal.find_peaks(spectrum/np.max(spectrum), height=0.98)[0][0]
62
+
63
+ end = startx + 3
64
+ start = startx - 3
65
+ for i in range(10):
66
+ if spectrum[startx - i] < 0.3 * spectrum[startx]:
67
+ start = startx - i
68
+ if spectrum[startx + i] < 0.3 * spectrum[startx]:
69
+ end = startx + i
70
+ if end - start < 7:
71
+ end = startx + 4
72
+ start = startx - 4
73
+ width = int((end-start)/2+0.5)
74
+
75
+ energy = dataset.get_spectral_dims(return_axis=True)[0].values
76
+
77
+ if dataset.ndim == 1: # single spectrum
78
+ _, shifts = get_channel_zero(np.array(dataset), energy, width)
79
+ shifts = np.array([shifts])
80
+ elif dataset.ndim == 2: # line scan
81
+ shifts = np.zeros(dataset.shape[:1])
82
+ for x in range(dataset.shape[0]):
83
+ _, shifts[x] = get_channel_zero(dataset[x, :], energy, width)
84
+ elif dataset.ndim == 3: # spectral image
85
+ shifts = np.zeros(dataset.shape[:2])
86
+ for x in range(dataset.shape[0]):
87
+ for y in range(dataset.shape[1]):
88
+ _, shifts[x, y] = get_channel_zero(dataset[x, y, :], energy, width)
89
+ return shifts
90
+
91
+
92
+ def shift_energy(dataset: sidpy.Dataset, shifts: np.ndarray) -> sidpy.Dataset:
93
+ """ Align zero-loss peaks of any spectral sidpy dataset """
94
+
95
+ new_si = dataset.copy()
96
+ new_si *= 0.0
97
+
98
+ image_dims = dataset.get_image_dims()
99
+ if len(image_dims) == 0:
100
+ image_dims =[0]
101
+ if len(image_dims) != shifts.ndim:
102
+ raise TypeError('array of energy shifts have to have same dimension as dataset')
103
+ if not isinstance(dataset, sidpy.Dataset):
104
+ raise TypeError('This function needs a sidpy Dataset to shift energy scale')
105
+ energy_scale = dataset.get_spectral_dims(return_axis=True)[0].values
106
+ if dataset.ndim == 1: # single spectrum
107
+ tck = scipy.interpolate.splrep(np.array(energy_scale - shifts), np.array(dataset), k=1, s=0)
108
+ new_si[:] = scipy.interpolate.splev(energy_scale, tck, der=0)
109
+ new_si.data_type = 'Spectrum'
110
+ elif dataset.ndim == 2: # line scan
111
+ for x in range(dataset.shape[0]):
112
+ tck = scipy.interpolate.splrep(np.array(energy_scale - shifts[x]),
113
+ np.array(dataset[x, :]), k=1, s=0)
114
+ new_si[x, :] = scipy.interpolate.splev(energy_scale, tck, der=0)
115
+ elif dataset.ndim == 3: # spectral image
116
+ for x in range(dataset.shape[0]):
117
+ for y in range(dataset.shape[1]):
118
+ tck = scipy.interpolate.splrep(np.array(energy_scale - shifts[x, y]),
119
+ np.array(dataset[x, y]), k=1, s=0)
120
+ new_si[x, y, :] = scipy.interpolate.splev(energy_scale, tck, der=0)
121
+
122
+ return new_si
123
+
124
+
125
+ def align_zero_loss(dataset: sidpy.Dataset) -> sidpy.Dataset:
126
+ """
127
+ Shifts the energy axis of the input dataset to be aligned with the zero-loss peak.
128
+
129
+ Parameters:
130
+ -----------
131
+ dataset : sidpy.Dataset
132
+ The input dataset containing the energy axis to be aligned.
133
+
134
+ Returns:
135
+ --------
136
+ sidpy.Dataset
137
+ The dataset with the energy axis shifted to align the zero-loss peak.
138
+ """
139
+ shifts = get_zero_loss_energy(dataset)
140
+ # print(shifts, dataset)
141
+ new_si = shift_energy(dataset, shifts)
142
+ new_si.metadata.update({'zero_loss': {'shifted': shifts}})
143
+ return new_si
144
+
145
+
146
+ def get_zero_losses(energy, z_loss_params):
147
+ """Calculate zero-loss peaks for a given energy range and parameters."""
148
+ z_loss_dset = np.zeros((z_loss_params.shape[0], z_loss_params.shape[1], energy.shape[0]))
149
+ for x in range(z_loss_params.shape[0]):
150
+ for y in range(z_loss_params.shape[1]):
151
+ z_loss_dset[x, y] += zl_func(energy, *z_loss_params[x, y])
152
+ return z_loss_dset
153
+
154
+
155
+
156
+
157
+ def get_resolution_functions(dataset: sidpy.Dataset, start_fit_energy: float=-1,
158
+ end_fit_energy: float=+1,
159
+ n_workers: int=1, n_threads: int=8):
160
+ """
161
+ Analyze and fit low-loss EELS data within a specified energy range to determine zero-loss peaks.
162
+
163
+ This function processes a low-loss EELS dataset from transmission electron microscopy
164
+ (TEM) data, focusing on a specified energy range for analyzing and fitting the spectrum.
165
+ It determines fitting parameters and applies these to extract zero-loss peak information
166
+ from the dataset. The function handles both 2D and 3D datasets.
167
+
168
+ Parameters:
169
+ -----------
170
+ dataset (sidpy.Dataset): The dataset containing TEM spectral data.
171
+ start_fit_energy (float): The start energy of the fitting window.
172
+ end_fit_energy (float): The end energy of the fitting window.
173
+ n_workers (int, optional): The number of workers for parallel processing (default is 1).
174
+ n_threads (int, optional): The number of threads for parallel processing (default is 8).
175
+
176
+ Returns:
177
+ --------
178
+ tuple: A tuple containing:
179
+ - z_loss_dset (sidpy.Dataset): The dataset with added zero-loss peak information.
180
+ - z_loss_params (numpy.ndarray): Array of parameters used
181
+ for the zero-loss peak fitting.
182
+
183
+ Raises:
184
+ -------
185
+ ValueError: If the input dataset does not have the expected dimensions or format.
186
+
187
+ Notes:
188
+ ------
189
+ - The function expects `dset` to have specific dimensionalities and will raise an error
190
+ if they are not met.
191
+ - Parallel processing is employed to enhance performance, particularly for large datasets.
192
+ """
193
+ energy = dataset.get_spectral_dims(return_axis=True)[0].values
194
+ start_fit_pixel = np.searchsorted(energy, start_fit_energy)
195
+ end_fit_pixel = np.searchsorted(energy, end_fit_energy)
196
+ guess_width = (end_fit_pixel - start_fit_pixel)/2
197
+ if end_fit_pixel - start_fit_pixel < 5:
198
+ start_fit_pixel -= 2
199
+ end_fit_pixel += 2
200
+
201
+ def get_good_guess(zl_peak, energy, spectrum):
202
+ popt, _ = scipy.optimize.curve_fit(zl_peak, energy, spectrum,
203
+ p0=[0, guess_amplitude, guess_width,
204
+ 0, guess_amplitude, guess_width])
205
+ return popt
206
+
207
+ fit_energy = energy[start_fit_pixel:end_fit_pixel]
208
+ # get a good guess for the fit parameters
209
+ if len(dataset.shape) == 3:
210
+ fit_dset = dataset[:, :, start_fit_pixel:end_fit_pixel]
211
+ guess_amplitude = np.sqrt(fit_dset.max())
212
+ image_size = fit_dset.shape[0]/fit_dset.shape[1]
213
+ guess_params = get_good_guess(zl_func, fit_energy,
214
+ fit_dset.sum(axis=(0, 1))/image_size)
215
+ elif len(dataset.shape) == 2:
216
+ fit_dset = dataset[:, start_fit_pixel:end_fit_pixel]
217
+ fit_energy = energy[start_fit_pixel:end_fit_pixel]
218
+ guess_amplitude = np.sqrt(fit_dset.max())
219
+ guess_params = get_good_guess(zl_func, fit_energy,
220
+ fit_dset.sum(axis=0)/fit_dset.shape[0])
221
+ elif len(dataset.shape) == 1:
222
+ fit_dset = dataset[start_fit_pixel:end_fit_pixel]
223
+ fit_energy = energy[start_fit_pixel:end_fit_pixel]
224
+ guess_amplitude = np.sqrt(fit_dset.max())
225
+ guess_params = get_good_guess(zl_func, fit_energy, fit_dset)
226
+ z_loss_dset = dataset.copy()
227
+ z_loss_dset *= 0.0
228
+ z_loss_dset += zl_func(energy, *guess_params)
229
+ if 'zero_loss' not in z_loss_dset.metadata:
230
+ z_loss_dset.metadata['zero_loss'] = {}
231
+ tags = {'start_fit_energy': start_fit_energy,
232
+ 'end_fit_energy': end_fit_energy,
233
+ 'fit_parameter': guess_params,
234
+ 'original_low_loss': dataset.title}
235
+ z_loss_dset.metadata['zero_loss'].update(tags)
236
+ return z_loss_dset
237
+ else:
238
+ print('Error: need a spectrum or spectral image sidpy dataset')
239
+ print('Not dset.shape = ', dataset.shape)
240
+ return None
241
+
242
+ # define guess function for SidFitter
243
+ def guess_function(xvec, yvec):
244
+ return guess_params
245
+
246
+ # apply to all spectra
247
+ zero_loss_fitter = SidFitter(fit_dset, zl_func, num_workers=n_workers,
248
+ guess_fn=guess_function, threads=n_threads,
249
+ return_cov=False, return_fit=False,
250
+ return_std=False, km_guess=False, num_fit_parms=6)
251
+ [z_loss_params] = zero_loss_fitter.do_fit()
252
+ z_loss_dset = dataset.copy()
253
+ z_loss_dset *= 0.0
254
+ z_loss_params = np.array(z_loss_params)
255
+ z_loss_dset += get_zero_losses(np.array(energy), np.array(z_loss_params))
256
+
257
+ # shifts = z_loss_params[:, :, 0] * z_loss_params[:, :, 3]
258
+ # widths = z_loss_params[:, :, 2] * z_loss_params[:, :, 5]
259
+ tags = {'start_fit_energy': start_fit_energy,
260
+ 'end_fit_energy': end_fit_energy,
261
+ 'fit_parameter': z_loss_params,
262
+ 'original_low_loss': dataset.title}
263
+ z_loss_dset.metadata['zero_loss'].update(tags)
264
+ return z_loss_dset