def test_model_nan_policy(self): x = np.linspace(0, 10, 201) np.random.seed(0) y = gaussian(x, 10.0, 6.15, 0.8) y += gaussian(x, 8.0, 6.35, 1.1) y += gaussian(x, 0.25, 6.00, 7.5) y += np.random.normal(size=len(x), scale=0.5) y[55] = y[91] = np.nan mod = PseudoVoigtModel() params = mod.make_params(amplitude=20, center=5.5, sigma=1, fraction=0.25) params['fraction'].vary = False # with raise, should get a ValueError result = lambda: mod.fit(y, params, x=x, nan_policy='raise') self.assertRaises(ValueError, result) # with propagate, should get no error, but bad results result = mod.fit(y, params, x=x, nan_policy='propagate') self.assertTrue(result.success) self.assertTrue(np.isnan(result.chisqr)) self.assertTrue(np.isnan(result.aic)) self.assertFalse(result.errorbars) self.assertTrue(result.params['amplitude'].stderr is None) self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 0.001) # with omit, should get good results result = mod.fit(y, params, x=x, nan_policy='omit') self.assertTrue(result.success) self.assertTrue(result.chisqr > 2.0) self.assertTrue(result.aic < -100) self.assertTrue(result.errorbars) self.assertTrue(result.params['amplitude'].stderr > 0.1) self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 5.0) self.assertTrue(abs(result.params['center'].value - 6.0) < 0.5)
def __init__(self, type): self.peak = [None] * (defPar.NumPeaks) if type == 0: for i in range(0, defPar.NumPeaks): self.peak[i] = PseudoVoigtModel(prefix="p" + str(i) + "_") self.typec = "PseudoVoigt" elif type == 1: for i in range(0, defPar.NumPeaks): self.peak[i] = GaussianModel(prefix="p" + str(i) + "_") self.typec = "Gauss" elif type == 2: for i in range(0, defPar.NumPeaks): self.peak[i] = LorentzianModel(prefix="p" + str(i) + "_") self.typec = "Lorentz" elif type == 3: for i in range(0, defPar.NumPeaks): self.peak[i] = VoigtModel(prefix="p" + str(i) + "_") self.typec = "Voigt" else: print("Warning: type undefined. Using PseudoVoigt") for i in range(0, defPar.NumPeaks): self.peak[i] = PseudoVoigtModel(prefix="p" + str(i) + "_") self.typec = "PVoigt"
def calc_center_pseudoVoigt(vx,vy): mod = PseudoVoigtModel() y = vy pars = mod.guess(y, x=vx) out = mod.fit(y, pars, x=vx) center = out.best_values['center'] return center
def make_model(peak_positions, fwhm=0.05, max_fwhm=0.5, pos_range=0.5, amplitude=1000.): n_peaks = len(peak_positions) pars = Parameters() bg = LinearModel(prefix='bg_') pars.update(bg.make_params(slope=0, intercept=0)) mod = bg #pars['bg_intercept'].set(vary=True) #pars['bg_slope'].set(vary=True) for i in range(n_peaks): prefix = 'pk{}_'.format(i) peak = PseudoVoigtModel(prefix=prefix) # Set this zero pars.update(peak.make_params()) pars[prefix + 'center'].set(peak_positions[i], min=peak_positions[i] - pos_range, max=peak_positions[i] + pos_range, vary=True) pars[prefix + 'sigma'].set(fwhm, min=0., max=max_fwhm, vary=True) pars[prefix + 'amplitude'].set(amplitude, min=0., vary=True) pars[prefix + 'fraction'].set(0.0, min=0., max=1., vary=True) mod += peak return mod, pars
def guess_params(self, peak): """Guesses parameters for a new peak and adds it. See add_peak().""" if peak.region is not self._region: logger.error("peak with ID {} does not belong to region ID {}" "".format(peak.ID, self._region.ID)) raise ValueError("Peak does not belong to Region") if peak.model_name == "PseudoVoigt": model = PseudoVoigtModel(prefix=peak.prefix) # model.set_param_hint("fraction", vary=False) else: raise NotImplementedError("Only PseudoVoigt models supported") other_models_cps = [0] * len(self._region.energy) for other_peak in self._region.peaks: if other_peak == peak: continue other_models_cps += self.get_peak_cps(other_peak, peak.energy) y = self._region.cps - self._region.background - other_models_cps params = model.guess(y, x=peak.energy) self._params += params fwhmname = "{}fwhm".format(peak.prefix) sigmaname = "{}sigma".format(peak.prefix) ampname = "{}amplitude".format(peak.prefix) centername = "{}center".format(peak.prefix) params[fwhmname].set(value=params[fwhmname].value, vary=True, min=0) params[sigmaname].set(expr="{}/2".format(fwhmname)) params[ampname].set(min=0) params[centername].set(min=0) self._single_models[peak.prefix] = model
def voigt_mod(N): ''' Returns a model consisting of N pseudo-voigt curves ''' # initialize model model = PseudoVoigtModel(prefix='voi1_') # Add N-1 pseudo-voigts for i in range(N - 1): model += PseudoVoigtModel(prefix='voi' + str(i + 2) + '_') return model
def fit_three_peaks(peak_data, pv_1_cent=3.43, pv_1_min=3.36, pv_1_max=3.44, pv_2_cent=3.52, pv_2_min=3.42, pv_2_max=3.55, pv_3_cent=3.59, pv_3_min=3.54, pv_3_max=3.6, initParams=None): """ Pseudo-Voigt fit to the lattice plane peak intensity for three peaks (adjust function to include more). Parameters for each peak also require limits and centres to be set in the case of overlapping peaks. Return results of the fit as an lmfit class, which contains the fitted parameters (amplitude, fwhm, etc.) and the fit line calculated using the fit parameters and 100x two-theta points. """ ttheta = peak_data[:, 0] intensity = peak_data[:, 1] PV_1 = PseudoVoigtModel(prefix='pv_1') PV_2 = PseudoVoigtModel(prefix='pv_2') PV_3 = PseudoVoigtModel(prefix='pv_3') model = PV_1 + PV_2 + PV_3 + Model(line) if initParams is None: # if reflection == '(0002),(110),(10-11)': use starting centres 3.43(min=3.36,max=3.44), 3.54(min=3.42,max=3.55), 3.59(min=3.54,max=3.6)... #if reflection == '(20-20),(11-22),(20-21)': use starting centres 6.32(min=6.23,max=6.33), 6.45(min=6.37,max=6.47), 6.54(min=6.46,max=6.56)... pars_1 = PV_1.guess(intensity, x=ttheta) #note, min and max values are not the same as bounds around the peak, but values that they can go up to! pars_1['pv_1center'].set(pv_1_cent, min=pv_1_min, max=pv_1_max) #pars_1['pv_1sigma'].set(min=0.001, max=0.1) #pars_1['pv_1amplitude'].set(min=0) #pars_1['pv_1height'].set(min=10) #pars_1['pv_1fwhm'].set(min=0.02, max=0.2) pars_2 = PV_2.guess(intensity, x=ttheta) pars_2['pv_2center'].set(pv_2_cent, min=pv_2_min, max=pv_2_max) #pars_2['pv_2sigma'].set(min=0.001, max=0.1) #pars_2['pv_2amplitude'].set(min=0) #pars_2['pv_2height'].set(min=10, max = 5000) #pars_2['pv_2fwhm'].set(min=0.02, max=0.2) pars_3 = PV_3.guess(intensity, x=ttheta) pars_3['pv_3center'].set(pv_3_cent, min=pv_3_min, max=pv_3_max) #pars_3['pv_3sigma'].set(min=0.001, max=0.1) #pars_3['pv_3amplitude'].set(min=1) #pars_3['pv_3height'].set(min=10) #pars_3['pv_3fwhm'].set(min=0.02, max=0.2) pars = pars_1 + pars_2 + pars_3 pars.add("constBG", 0) else: pars = initParams fit_results = model.fit(intensity, pars, x=ttheta) fit_ttheta = np.linspace(ttheta[0], ttheta[-1], 100) fit_line = [fit_ttheta, model.eval(fit_results.params, x=fit_ttheta)] return fit_results, fit_line
def set_params(peaks): """ This module takes in the list of peaks from the peak detection modules, and then uses that to initialize parameters for a set of Pseudo-Voigt models that are not yet fit. There is a single model for every peak. Args: peaks (list): A list containing the x and y-values (in tuples) of the peaks. Returns: mod (lmfit.models.PseudoVoigtModel or lmfit.model.CompositeModel): This is an array of the initialized Pseudo-Voigt models. The array contains all of the values that are found in `pars` that are fed to an lmfit lorentzian model class. pars (lmfit.parameter.Parameters): An array containing the parameters for each peak that were generated through the use of a Lorentzian fit. The pars array contains a center value, a height, a sigma, and an amplitude value. The center value is allowed to vary +- 10 wavenumber from the peak max that was detected in scipy. Some wiggle room was allowed to help mitigate problems from slight issues in the peakdetect algorithm for peaks that might have relatively flat maxima. The height value was allowed to vary between 0 and 1, as it is assumed the y-values are normalized. Sigma is set to a maximum of 500, as we found that giving it an unbound maximum led to a number of peaks that were unrealistic for Raman spectra (ie, they were far too broad, and shallow, to correspond to real data. Finally, the amplitude for the peak was set to a minimum of 0, to prevent negatives. """ # handling errors in inputs if not isinstance(peaks, list): raise TypeError('Passed value of `peaks` is not a list! Instead, it is: ' + str(type(peaks))) for i, _ in enumerate(peaks): if not isinstance(peaks[i], tuple): raise TypeError("""Passed value of `peaks[{}]` is not a tuple. Instead, it is: """.format(i) + str(type(peaks[i]))) peak_list = [] for i, _ in enumerate(peaks): prefix = 'p{}_'.format(i+1) peak = PseudoVoigtModel(prefix=prefix) if i == 0: pars = peak.make_params() else: pars.update(peak.make_params()) pars[prefix+'center'].set(peaks[i][0], vary=False) pars[prefix+'height'].set(peaks[i][1], vary=False) pars[prefix+'sigma'].set(50, min=0, max=500) pars[prefix+'amplitude'].set(min=0) peak_list.append(peak) if i == 0: mod = peak_list[i] else: mod = mod + peak_list[i] return mod, pars
def voigt(x, y): # Voigt fit to a curve x_shifted = x - x.min() # Shifting to 0 y_shifted = y - y.min() # Shifting to 0 mod = PseudoVoigtModel() # Setting model type pars = mod.guess(y_shifted, x=x_shifted) # Estimating fit out = mod.fit(y_shifted, pars, x=x_shifted) # Fitting fit # print(out.fit_report(min_correl=0.25)) # Outputting best fit results print("Voigt FWHM = ", out.params['fwhm'].value) # Outputting only FWHM out.plot() # Plotting fit
def fit_one_Psudo_Voigt(x_lst,y_lst): ''' Fits one Pseudo Voigt returns the results object ''' mod = PseudoVoigtModel(independent_vars=['x'],nan_policy='raise') x_lst = np.asarray(x_lst) y_lst = np.asarray(y_lst) # Guess good values computer mod.guess(y_lst,x = x_lst) result = mod.fit(y_lst, x = x_lst) return result
def test_figure_title_using_title_keyword_argument(peakdata): """Test setting figure title using title keyword argument.""" x, y = peakdata pvmodel = PseudoVoigtModel() params = pvmodel.guess(y, x=x) result = pvmodel.fit(y, params, x=x) ax = result.plot_fit(title='test') assert ax.axes.get_title() == 'test' ax = result.plot_residuals(title='test') assert ax.axes.get_title() == 'test' fig, _ = result.plot(title='test') assert fig.axes[0].get_title() == 'test' assert fig.axes[1].get_title() == '' # no title for fit subplot
def test_figure_default_title(peakdata): """Test default figure title.""" x, y = peakdata pvmodel = PseudoVoigtModel() params = pvmodel.guess(y, x=x) result = pvmodel.fit(y, params, x=x) ax = result.plot_fit() assert ax.axes.get_title() == 'Model(pvoigt)' ax = result.plot_residuals() assert ax.axes.get_title() == 'Model(pvoigt)' fig, _ = result.plot() assert fig.axes[0].get_title() == 'Model(pvoigt)' # default model.name assert fig.axes[1].get_title() == '' # no title for fit subplot
def center_psi(file_name): #print(file_name) psi, vx, vy = read_file(file_name) vy = processing_of_data(psi,vx,vy) legenda = file_name.split('/')[-1] #plt.grid() #plt.legend(loc=0) #import pdb; pdb.set_trace() plt.plot(vx,vy,label=legenda) mod = PseudoVoigtModel() y=vy pars = mod.guess(y, x=vx) out = mod.fit(y, pars, x=vx) center =out.best_values['center'] print('center: {} <--> psi: {}'.format(center,psi)) return psi, center
def set_params(peaks): """ This function takes in the list of peaks from the peak detection modules, and then uses that to initialize parameters for a set of Pseudo-Voigt models that are not yet fit. There is a single model for every peak. Args: peaks (list): A list containing tuples of the x_data (wavenumber) and y_data (counts) values of the peaks. Returns: mod (lmfit.models.PseudoVoigtModel or lmfit.model.CompositeModel): This is an array of the initialized pseudo-Voigt models. The array contains all of the values that are found in `pars` that are fed to an lmfit lorentzian model class. pars (lmfit.parameter.Parameters): An array containing the parameters for each peak that were generated through the use of a Lorentzian fit. The pars array contains values for fraction, center, height, sigma, the full width at half maximum (fwhm = 2*sigma), and amplitude. """ # handling errors in inputs if not isinstance(peaks, list): raise TypeError( 'Passed value of `peaks` is not a list! Instead, it is: ' + str(type(peaks))) for i, peak in enumerate(peaks): if not isinstance(peak, tuple): raise TypeError("""The {} value of `peaks` is not a tuple. Instead, it is: """.format(i) + str(type(peak))) peak_list = [] for i, value in enumerate(peaks): prefix = 'p{}_'.format(i + 1) peak = PseudoVoigtModel(prefix=prefix) if i == 0: pars = peak.make_params() else: pars.update(peak.make_params()) # constraints on profile desciptors pars[prefix + 'center'].set(value[0], vary=False) pars[prefix + 'height'].set(min=0.1 * value[1]) pars[prefix + 'sigma'].set(10, min=1, max=100) pars[prefix + 'amplitude'].set(100 * value[1], min=0) peak_list.append(peak) if i == 0: mod = peak_list[i] else: mod = mod + peak_list[i] return mod, pars
def fit_data(x, y, xmin=None, xmax=None): if y is None: return "No spectrum! Fit not performed!" else: if xmin is None: xmin = x.min() elif xmin < x.min(): xmin = x.min() if xmax is None: xmax = x.max() elif xmax < x.max(): xmax = x.max() y = np.copy(y[np.logical_and(x > xmin, x < xmax)]) x = np.copy(x[np.logical_and(x > xmin, x < xmax)]) x, y = remove_spike(y, x=x) mod = (ConstantModel() + PseudoVoigtModel(prefix="peak1_") + PseudoVoigtModel(prefix="peak2_")) params = mod.make_params() ymax_ind = np.argmax(y) xmax = x[ymax_ind] params["c"].set(0) params.add("psplit", value=1.5, vary=True, min=0.5, max=2.0) params["peak2_center"].set(xmax, min=xmax - 0.5, max=x.max()) params["peak2_sigma"].set(0.5, min=0.01) params["peak2_amplitude"].set(y[ymax_ind] * 0.5 * np.sqrt(2 * np.pi), min=0) params["peak2_fraction"].set(0.5, min=0, max=1) params["peak1_center"].set(vary=False, expr="peak2_center-psplit") params["peak1_sigma"].set(0.5, min=0.01) params["peak1_amplitude"].set(y[ymax_ind] * 0.5 * np.sqrt(2 * np.pi) / 2, min=0) params["peak1_fraction"].set(0.5, min=0, max=1) fit = mod.fit(y, params, x=x) return fit
def fit_experimental_data_gauss(exp_x, exp_y, expected_peak_pos, deg_of_bck_poly=5, maxfev=25000): num_of_peaks = len(expected_peak_pos) mod = PolynomialModel(deg_of_bck_poly, prefix='poly_') for c in range(num_of_peaks): mod = mod + PseudoVoigtModel(prefix='p{}_'.format(c)) params = mod.make_params() center = 0 sigma = 0 amplitude = 0 fraction = 0 for param in params: if 'center' in param: params[param].set(value=expected_peak_pos[center]) params[param].set(min=expected_peak_pos[center] - 0.5) params[param].set(max=expected_peak_pos[center] + 0.5) center += 1 if 'poly' in param: if param == 'poly_c0': params[param].set(value=50) params[param].set(min=-100) params[param].set(max=100) continue if param == 'poly_c1': params[param].set(value=-1) params[param].set(min=-100) params[param].set(max=100) continue params[param].set(value=0) ## params[param].set(min = 3e-1) ## params[param].set(max = 3e-1) if 'sigma' in param: params[param].set(value=0.5) params[param].set(min=0.0001) params[param].set(max=0.8) sigma += 1 if 'amplitude' in param: params[param].set(value=5.5) params[param].set(min=0.0001) amplitude += 1 if 'fraction' in param: params[param].set(value=0.0) params[param].set(min=0.000) params[param].set(max=0.000001) fraction += 1 result = mod.fit(np.asarray(exp_y), params, x=np.asarray(exp_x), fit_kws={'maxfev': maxfev}) print(result.fit_report()) return result
def methodfunciont(key): dic = { "VoigtModel": VoigtModel(), "PseudoVoigtModel": PseudoVoigtModel(), "GaussianModel": GaussianModel() } return dic[key]
def prepare_for_fitting(self, poly_order, maxwidth, centerrange): """ :param x_center: numpy array of initial x values at picked centers :param y_center: numpy array of initial y values at picked centers :param fwhm: single float number for initial fwhm value """ self.set_baseline(poly_order) baseline_mod = PolynomialModel(poly_order, prefix='b_') mod = baseline_mod pars = baseline_mod.make_params() peakinfo = {} for i in range(poly_order + 1): prefix = "b_c{0:d}".format(i) pars[prefix].set(value=self.baseline_in_queue[i]['value'], vary=self.baseline_in_queue[i]['vary']) i = 0 for peak in self.peaks_in_queue: prefix = "p{0:d}_".format(i) peak_mod = PseudoVoigtModel(prefix=prefix, ) pars.update(peak_mod.make_params()) pars[prefix + 'center'].set(value=peak['center'], min=peak['center'] - centerrange, max=peak['center'] + centerrange, vary=peak['center_vary']) pars[prefix + 'sigma'].set(value=peak['sigma'], min=0.0, vary=peak['sigma_vary'], max=maxwidth) pars[prefix + 'amplitude'].set(value=peak['amplitude'], min=0, vary=peak['amplitude_vary']) pars[prefix + 'fraction'].set(value=peak['fraction'], min=0., max=1., vary=peak['fraction_vary']) peakinfo[prefix + 'phasename'] = peak['phasename'] peakinfo[prefix + 'h'] = peak['h'] peakinfo[prefix + 'k'] = peak['k'] peakinfo[prefix + 'l'] = peak['l'] mod += peak_mod i += 1 self.parameters = pars self.peakinfo = peakinfo self.fit_model = mod
def fit_peak(peak_data, initParams=None): """ Pseudo-Voigt fit to the lattice plane peak intensity. Return results of the fit as an lmfit class, which contains the fitted parameters (amplitude, fwhm, etc.) and the fit line calculated using the fit parameters and 100x two-theta points. """ ttheta = peak_data[:, 0] intensity = peak_data[:, 1] pvModel = PseudoVoigtModel() model = pvModel + Model(line) if initParams is None: pars = pvModel.guess(intensity, x=ttheta) pars['sigma'].set(min=0.01, max=1) #sigma is the width? pars['amplitude'].set(min=0.05) pars.add("constBG", 0) else: pars = initParams fit_results = model.fit(intensity, pars, x=ttheta) fit_ttheta = np.linspace(ttheta[0], ttheta[-1], 100) fit_line = [fit_ttheta, model.eval(fit_results.params, x=fit_ttheta)] return fit_results, fit_line
def test_figure_title_using_title_to_ax_kws(peakdata): """Test setting figure title by supplying ax_{fit,res}_kws.""" x, y = peakdata pvmodel = PseudoVoigtModel() params = pvmodel.guess(y, x=x) result = pvmodel.fit(y, params, x=x) ax = result.plot_fit(ax_kws={'title': 'ax_kws'}) assert ax.axes.get_title() == 'ax_kws' ax = result.plot_residuals(ax_kws={'title': 'ax_kws'}) assert ax.axes.get_title() == 'ax_kws' fig, _ = result.plot(ax_res_kws={'title': 'ax_res_kws'}) assert fig.axes[0].get_title() == 'ax_res_kws' assert fig.axes[1].get_title() == '' fig, _ = result.plot(ax_fit_kws={'title': 'ax_fit_kws'}) assert fig.axes[0].get_title() == 'Model(pvoigt)' # default model.name assert fig.axes[1].get_title() == '' # no title for fit subplot
def test_priority_setting_figure_title(peakdata): """Test for setting figure title: title keyword argument has priority.""" x, y = peakdata pvmodel = PseudoVoigtModel() params = pvmodel.guess(y, x=x) result = pvmodel.fit(y, params, x=x) ax = result.plot_fit(ax_kws={'title': 'ax_kws'}, title='test') assert ax.axes.get_title() == 'test' ax = result.plot_residuals(ax_kws={'title': 'ax_kws'}, title='test') assert ax.axes.get_title() == 'test' fig, _ = result.plot(ax_res_kws={'title': 'ax_res_kws'}, title='test') assert fig.axes[0].get_title() == 'test' assert fig.axes[1].get_title() == '' fig, _ = result.plot(ax_fit_kws={'title': 'ax_fit_kws'}, title='test') assert fig.axes[0].get_title() == 'test' assert fig.axes[1].get_title() == ''
def double_voigt(self, sepPt=None, pars: list = None, bounds: list = None): if sepPt == None: sepPt = find_separation_point(self.x, self.y) x1 = self.x[self.x < sepPt] x2 = self.x[self.x > sepPt] y1 = self.y[self.x < sepPt] y2 = self.y[self.x > sepPt] mod1 = PseudoVoigtModel(prefix='v1_') mod2 = PseudoVoigtModel(prefix='v2_') if pars == None: pars1 = self.guess_pars(self, mod1, x1, y1, prefix='v1_') pars2 = self.guess_pars(self, mod2, x2, y2, prefix='v2_') mod = mod1 + mod2 pars = mod.make_params() pars.update(pars1) pars.update(pars2) return self.finish_fit(self, mod, pars)
def test_model_nan_policy(self): """Tests for nan_policy with NaN values in the input data.""" x = np.linspace(0, 10, 201) np.random.seed(0) y = gaussian(x, 10.0, 6.15, 0.8) y += gaussian(x, 8.0, 6.35, 1.1) y += gaussian(x, 0.25, 6.00, 7.5) y += np.random.normal(size=len(x), scale=0.5) # with NaN values in the input data y[55] = y[91] = np.nan mod = PseudoVoigtModel() params = mod.make_params(amplitude=20, center=5.5, sigma=1, fraction=0.25) params['fraction'].vary = False # with raise, should get a ValueError result = lambda: mod.fit(y, params, x=x, nan_policy='raise') msg = ( 'NaN values detected in your input data or the output of your ' 'objective/model function - fitting algorithms cannot handle this!' ) self.assertRaisesRegex(ValueError, msg, result) # with propagate, should get no error, but bad results result = mod.fit(y, params, x=x, nan_policy='propagate') self.assertTrue(result.success) self.assertTrue(np.isnan(result.chisqr)) self.assertTrue(np.isnan(result.aic)) self.assertFalse(result.errorbars) self.assertTrue(result.params['amplitude'].stderr is None) self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 0.001) # with omit, should get good results result = mod.fit(y, params, x=x, nan_policy='omit') self.assertTrue(result.success) self.assertTrue(result.chisqr > 2.0) self.assertTrue(result.aic < -100) self.assertTrue(result.errorbars) self.assertTrue(result.params['amplitude'].stderr > 0.1) self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 5.0) self.assertTrue(abs(result.params['center'].value - 6.0) < 0.5) # with 'wrong_argument', should get a ValueError err_msg = r"nan_policy must be 'propagate', 'omit', or 'raise'." with pytest.raises(ValueError, match=err_msg): mod.fit(y, params, x=x, nan_policy='wrong_argument')
def fitXY(self): #self.analyze () #self.amplitude1 = self.maxval - self.minval #self.amplitude2 = .6 * self.amplitude1 peak1 = PseudoVoigtModel(prefix='p1_') peak2 = PseudoVoigtModel(prefix='p2_') basef = LinearModel() model = peak1 + peak2 + basef params = model.make_params(p1_center=self.sample_position, p2_center=self.sample_position - 1.5, p1_amplitude=self.amplitude1, p2_amplitude=self.amplitude1 * 0.6, p1_sigma=0.25, p2_sigma=0.25, p1_fraction=0.8, p2_fraction=0.8, slope=0, intercept=self.minval) result = model.fit(self.ydata, x=self.wave, **params) self.modelFit = result.best_fit bfd = result.best_values self.fitParams = [ bfd['p1_amplitude'], bfd['p1_center'], bfd['p1_sigma'], bfd['p1_fraction'], bfd['p2_amplitude'], bfd['p2_center'], bfd['p2_sigma'], bfd['p2_fraction'], bfd['slope'], bfd['intercept'] ] self.oparams1[0] = bfd['p1_amplitude'] self.oparams1[1] = bfd['p1_center'] self.oparams1[2] = bfd['p1_sigma'] self.oparams1[3] = bfd['p1_fraction'] self.oparams2[0] = bfd['p2_amplitude'] self.oparams2[1] = bfd['p2_center'] self.oparams2[2] = bfd['p2_sigma'] self.oparams2[3] = bfd['p2_fraction'] self.oparams3[0] = bfd['slope'] self.oparams3[1] = bfd['intercept'] self.fitDone.emit()
def add_peak(self, peak): """Adds a new Peak to the Model list.""" if peak.region is not self._region: logger.error("peak with ID {} does not belong to region ID {}" "".format(peak.ID, self._region.ID)) raise ValueError("Peak does not belong to Region") if peak.model_name == "PseudoVoigt": model = PseudoVoigtModel(prefix=peak.prefix) model.set_param_hint("fraction", vary=False, value=0.2) params = model.make_params() else: raise NotImplementedError("Only PseudoVoigt models supported") self._params += params fwhmname = "{}fwhm".format(peak.prefix) sigmaname = "{}sigma".format(peak.prefix) ampname = "{}amplitude".format(peak.prefix) centername = "{}center".format(peak.prefix) params[fwhmname].set(value=params[fwhmname].value, vary=True, min=0) params[sigmaname].set(expr="{}/2".format(fwhmname)) params[ampname].set(min=0) params[centername].set(min=0) self._single_models[peak.prefix] = model
def fit_voigt(xp: XPS_experiment, region: str, prefix: str = 'v_', pars: list = None, bounds: list = None, ax=None, flag_plot: bool = True): """General method for fitting voigt model Input ---------- xp : class XPS_experiment XPS data region : str core level name pars, bounds : list initial guess of the fit parameters and bounds. If unspecified, guessed automatically Returns ----------- fitv : lmfit.model fit result to Voigt model """ from lmfit.models import PseudoVoigtModel, GaussianModel x = xp.dfx[region].dropna().energy y = xp.dfx[region].dropna().counts mod = PseudoVoigtModel(prefix=prefix) if pars == None: pars = mod.guess(y, x=x) pars[prefix + 'sigma'].set(value=1) # Usually guessed wrong anyway pars[prefix + 'fraction'].set(value=0.2, min=0.15, max=0.20) fitv = mod.fit(y, pars, x=x) xp.fit.update({region: fitv}) if flag_plot: hatchplot_fit(xp, region, fitv, ax=ax, plot_comps=True) return fitv
def prepareFittingModels(roiCoordsList, modelType): modelList = [] paramList = [] index = 1 for region in roiCoordsList: individualModelsList = [] individualParamsList = [] if isinstance(region, dict): # If the region is just a single region, make it a list so the for loops pulls a dict rather than a dict entry region = [region] for entry in region: prefixName = 'v' + str(index) + '_' index += 1 # pull info out of region dict selectedXVals = entry['x'] selectedYVals = entry['y'] mod = None if modelType.lower() == 'voigt': mod = VoigtModel(prefix=prefixName) elif modelType.lower() == 'psuedovoigt': mod = PseudoVoigtModel(prefix=prefixName) elif modelType.lower() == 'lorentzian': mod = LorentzianModel(prefix=prefixName) elif modelType.lower() == 'gaussian': mod = GaussianModel(prefix=prefixName) elif modelType.lower() == 'pearsonvii': mod = Pearson7Model(prefix=prefixName) assert mod, "Entered model type is not supported" individualModelsList.append(mod) pars = mod.guess(selectedYVals, x=selectedXVals, negative=False) pars[prefixName + 'center'].set(min=min(selectedXVals), max=max(selectedXVals)) pars[prefixName + 'amplitude'].set(min=0) pars[prefixName + 'sigma'].set(min=0) if modelType.lower() == 'voigt': pars[prefixName + 'gamma'].set(value=0.3, vary=True, expr='', min=0) individualParamsList.append(pars) combinedModel = individualModelsList[0] combinedParams = individualParamsList[0] if len(individualModelsList) > 1: for model, params in zip(individualModelsList[1:], individualParamsList[1:]): combinedModel += model combinedParams += params modelList.append(combinedModel) paramList.append(combinedParams) return modelList, paramList
def test_param_hint_explicit_value(self): # tests Github Issue 384 pmod = PseudoVoigtModel() params = pmod.make_params(sigma=2, fraction=0.77) assert_allclose(params['fraction'].value, 0.77, rtol=0.01)
def fitMo3d(filename, groupNo, regionNo, E_b_min, E_b_max, doplot=False): """Fit a Moly 3d signal using elemental and oxide components (two for each peak, 3/2 and 5/2). A shirley background is subtracted first. The elemental Mo components are fittet to the PseudoVoigt line shape. Oxide components are fitted to a Gaussian line shape. Parameters ---------- filename : string location of the data file, expects an SpecsLab xml file. groupNo : int index of the group of spectra (from 0) regionNo : int index of the spectrum that should be fitted (from 0) E_b_min : float region boundary E_b_max : float region boundary doplot : bool wether or not to plot the fit and components (default False) """ #load data from SpecsLab xml file and select region region = get_region(filename, groupNo, regionNo) # determine index of left and right boundary index1 = np.where(abs(region.x_be - (E_b_min)) < 1e-10) index2 = np.where(abs(region.x_be - (E_b_max)) < 1e-10) E = region.x_be[index1[0][0]:index2[0][0]] #binding energy scale s = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]] #signal #signal with background substacted epsilon, sb, B = remove_shirley_background(s, E, 3, 1e-6) ###################### # set up model components and set constraints for the fit ###################### pre11 = 'Mo3d_32_1_' mod11 = PseudoVoigtModel(prefix=pre11) pars = mod11.make_params() pars.add('delta', value=3, min=2, max=4) pars[pre11 + 'amplitude'].set(9000, min=500, max=100000) pars[pre11 + 'center'].set(-231.2, min=-231.4, max=-231) pars[pre11 + 'sigma'].set(0.3, min=0.1, max=0.4) pars[pre11 + 'fraction'].set(0.5, min=0.1, max=1) mod21 = PseudoVoigtModel(prefix='Mo3d_52_1_') pars.update(mod21.make_params()) pars['Mo3d_52_1_amplitude'].set( expr='3/2*Mo3d_32_1_amplitude') #(12000, min=500, max=100000) pars['Mo3d_52_1_center'].set(expr='Mo3d_32_1_center+delta' ) #same distance between 3/2 and 5/2 peaks #pars['Mo3d_52_1_sigma' ].set(expr='1.0*Mo3d_32_1_sigma') #all have same sigma value pars['Mo3d_52_1_fraction'].set( expr='1.0*Mo3d_32_1_fraction') #(0.5, min=0.2, max=1) pre12 = 'MoO23d_32_2_' mod12 = GaussianModel(prefix=pre12) pars.update(mod12.make_params()) pars[pre12 + 'amplitude'].set(2050, min=500, max=100000) pars[pre12 + 'center'].set(-234.5, min=-236, max=-234) pars[pre12 + 'sigma'].set(0.3, min=0.2, max=2) pre22 = 'MoO23d_52_2_' mod22 = GaussianModel(prefix=pre22) pars.update(mod22.make_params()) pars[pre22 + 'amplitude'].set( expr='3/2*MoO23d_32_2_amplitude') #(1050, min=500, max=100000) pars[pre22 + 'center'].set(expr='MoO23d_32_2_center+delta' ) #same distance between 3/2 and 5/2 peaks pars[pre22 + 'sigma'].set( expr='1.0*MoO23d_32_2_sigma') #all have same sigma value pre13 = 'MoO23d_32_3_' mod13 = GaussianModel(prefix=pre13) pars.update(mod13.make_params()) pars[pre13 + 'amplitude'].set(2050, min=500, max=100000) pars[pre13 + 'center'].set(-235.5, min=-237, max=-234) pars[pre13 + 'sigma'].set(0.3, min=0.2, max=2) pre23 = 'MoO23d_52_3_' mod23 = GaussianModel(prefix=pre23) pars.update(mod23.make_params()) pars[pre23 + 'amplitude'].set( expr='3/2*MoO23d_32_3_amplitude') #(1050, min=500, max=100000) pars[pre23 + 'center'].set(expr='MoO23d_32_3_center+delta' ) #same distance between 3/2 and 5/2 peaks pars[pre23 + 'sigma'].set( expr='1.0*MoO23d_32_3_sigma') #all have same sigma value #composite model is a sum of the components mod = mod11 + mod21 + mod12 + mod22 + mod13 + mod23 #perform the fit out = mod.fit(sb, pars, x=E) print(out.fit_report(min_correl=0.25)) if (doplot): fig = plt.figure(None, figsize=(6, 4)) #plot individual components comps = mod.eval_components(params=out.params, x=E) labels = [ 'Mo', 'Mo', 'MoO$_3$', 'MoO$_3$', 'MoO$_2$', 'MoO$_2$', 'MoO$_2$', 'MoO$_2$' ] for n, key in enumerate(comps): if ((n % 2) == 0): plt.plot(-E, comps[key] / 10**3, color=colors[n], label=labels[n]) else: plt.plot(-E, comps[key] / 10**3, color=colors[n - 1]) #plt.fill_between(-E, 0.0, comps[key]/10**3, facecolor=colors[int(i/2)], alpha=0.5) #plot measured signal (dots) and best fit (sum of components) plt.plot(-E, sb / 10**3, 'k.', label='measurement') #plt.plot( -E , s/10**3 , 'k-') #plt.plot( -E , B/10**3 , 'k--') plt.plot(-E, out.best_fit / 10**3, color=colors[1], label='total fit') ax = mpl.pyplot.gca() ax.set_xlim(241, -E_b_max) plt.xlabel('binding energy (eV)') plt.ylabel('counts (arb. u.)') print(out.best_values['MoO23d_32_2_center'] - out.best_values['Mo3d_32_1_center']) print(out.best_values['MoO23d_32_3_center'] - out.best_values['Mo3d_32_1_center']) ## reference : also plot pure Mo signal #load data from SpecsLab xml file and select region region = get_region(filename, 3, 2) # determine index of left and right boundary index1 = np.where(abs(region.x_be - (-235)) < 1e-10) index2 = np.where(abs(region.x_be - (E_b_max)) < 1e-10) E = region.x_be[index1[0][0]:index2[0][0]] #binding energy scale s = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]] #signal epsilon, sb, B = remove_shirley_background(s, E, 3, 1e-6) plt.plot(-E, sb / (6 * 10**3), 'k--', label='pure Mo') plt.legend() fig.tight_layout()
def fitpureMo3d(filename, groupNo, regionNo, E_b_min, E_b_max, doplot=False): """Fit a Moly 3d signal using elemental and oxide components (two for each peak, 3/2 and 5/2). A shirley background is subtracted first. The elemental Mo components are fittet to the PseudoVoigt line shape. Oxide components are fitted to a Gaussian line shape. Parameters ---------- filename : string location of the data file, expects an SpecsLab xml file. groupNo : int index of the group of spectra (from 0) regionNo : int index of the spectrum that should be fitted (from 0) E_b_min : float region boundary E_b_max : float region boundary doplot : bool wether or not to plot the fit and components (default False) """ #load data from SpecsLab xml file and select region region = get_region(filename, groupNo, regionNo) #Sb = VAMAS.VAMASExperiment('P006_Sb_surveyf.vms'); #regions = [data[0][2], data[1][1], data[2][1]] print(region.x_be) # determine index of left and right boundary index1 = np.where(abs(region.x_be - (E_b_min)) < 1e-10) index2 = np.where(abs(region.x_be - (E_b_max)) < 1e-10) E = region.x_be[index1[0][0]:index2[0][0]] #binding energy scale s = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]] #signal #signal with background substacted epsilon, sb, B = remove_shirley_background(s, E, 3, 1e-6) ###################### # set up model components and set constraints for the fit ###################### pre11 = 'Mo3d_32_1_' mod11 = PseudoVoigtModel(prefix=pre11) pars = mod11.make_params() pars.add('delta', value=3, min=2, max=5) pars[pre11 + 'amplitude'].set(9000, min=500, max=100000) pars[pre11 + 'center'].set(-231, min=-231, max=-230) pars[pre11 + 'sigma'].set(0.3, min=0.2, max=1) pars[pre11 + 'fraction'].set(0.5, min=0.2, max=1) mod21 = PseudoVoigtModel(prefix='Mo3d_52_1_') pars.update(mod21.make_params()) pars['Mo3d_52_1_amplitude'].set( expr='3/2*Mo3d_32_1_amplitude') #(12000, min=500, max=100000) pars['Mo3d_52_1_center'].set(expr='Mo3d_32_1_center+delta' ) #same distance between 3/2 and 5/2 peaks #pars['Mo3d_52_1_sigma' ].set(expr='1.0*Mo3d_32_1_sigma') #all have same sigma value pars['Mo3d_52_1_fraction'].set( expr='1.0*Mo3d_32_1_fraction') #(0.5, min=0.2, max=1) #composite model is a sum of the components mod = mod11 + mod21 #perform the fit out = mod.fit(sb, pars, x=E) if (doplot): plt.figure() #plot individual components comps = mod.eval_components(params=out.params, x=E) i = 0 for key in comps: plt.plot(E, comps[key] / 10**3, color=colors[i], label=key) plt.fill_between(E, 0.0, comps[key] / 10**3, facecolor=colors[i], alpha=0.5) i = i + 1 #plot measured signal (dots) and best fit (sum of components) plt.plot(E, sb / 10**3, 'k.') plt.plot(E, s / 10**3, 'k-') plt.plot(E, B / 10**3, 'k--') plt.plot(E, out.best_fit / 10**3, color=colors[1]) plt.legend() ax = mpl.pyplot.gca() ax.set_xlim(E_b_min, E_b_max) plt.xlabel('binding energy (eV)') plt.ylabel('cps ($10^3$)') print(out.fit_report(min_correl=0.25))