def voigt_response(self, sigma=None, gamma=None, weights=True): ''' Fit the background with a Voigt profile to determine the response of the spectrometer If you have a good, clear signal, set sigma and gamma to None (done by default) If your signal is poor, set sigma and gamma using a fit to a good signal, and then only the position of the central wavelength will be altered. ''' vm = VoigtModel() par_v = vm.guess(self.bkgd, x=self.lamb) par_v['center'].set(value=532e-9, vary=True) if sigma is not None: #if a width is provided, fix it. par_v['sigma'].set(value=sigma, vary=False) if gamma is not None: #if a width is provided, fix it. par_v['gamma'].set(value=gamma, vary=False, expr='') elif gamma is None: #vary gamma for better fit - this is not done by default par_v['gamma'].set(value=par_v['sigma'].value, vary=True, expr='') ##Fit the Voigt Model to the data if weights is True: weights = self.bkgd / self.bkgd_err if weights is False: weights = np.ones_like(self.bkgd) self.vm_fit = vm.fit(self.bkgd, par_v, x=self.lamb, weights=weights) self.l0 = self.vm_fit.best_values['center'] self.sigma = self.vm_fit.best_values['sigma']
def test_param_set(): np.random.seed(2015) x = np.arange(0, 20, 0.05) y = gaussian(x, amplitude=15.43, center=4.5, sigma=2.13) y = y + 0.05 - 0.01*x + np.random.normal(scale=0.03, size=len(x)) model = VoigtModel() params = model.guess(y, x=x) # test #1: gamma is constrained to equal sigma sigval = params['gamma'].value assert(params['gamma'].expr == 'sigma') assert_allclose(params['gamma'].value, sigval, 1e-4, 1e-4, '', True) # test #2: explicitly setting a param value should work, even when # it had been an expression. The value will be left as fixed gamval = 0.87543 params['gamma'].set(value=gamval) assert(params['gamma'].expr is None) assert(not params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True) # test #3: explicitly setting an expression should work params['gamma'].set(expr='sigma/2.0') assert(params['gamma'].expr is not None) assert(not params['gamma'].vary) assert_allclose(params['gamma'].value, sigval/2.0, 1e-4, 1e-4, '', True) # test #4: explicitly setting a param value WITH vary=True # will set it to be variable gamval = 0.7777 params['gamma'].set(value=gamval, vary=True) assert(params['gamma'].expr is None) assert(params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True)
def test_numdifftools_calc_covar_false(): pytest.importorskip("numdifftools") # load data to be fitted data = np.loadtxt( os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['sigma'].set(min=-np.inf) # do fit, with leastsq and nelder result = mod.fit(y, params, x=x, method='leastsq') result_ndt = mod.fit(y, params, x=x, method='nelder', calc_covar=False) # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_ndt = [ result_ndt.params[p].value for p in result_ndt.params.valuesdict() ] assert_allclose(vals_ndt, vals, rtol=5e-3) assert_allclose(result_ndt.chisqr, result.chisqr) assert result_ndt.covar is None assert result_ndt.errorbars is False
def test_bounds_expression(): # load data to be fitted data = np.loadtxt(os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['amplitude'].set(min=0, max=100) params['center'].set(min=5, max=10) # do fit, here with leastsq model result = mod.fit(y, params, x=x) # assert that stderr and correlations are correct [cf. lmfit v0.9.10] assert_almost_equal(result.params['sigma'].stderr, 0.00368468, decimal=6) assert_almost_equal(result.params['center'].stderr, 0.00505496, decimal=6) assert_almost_equal(result.params['amplitude'].stderr, 0.13861506, decimal=6) assert_almost_equal(result.params['gamma'].stderr, 0.00368468, decimal=6) assert_almost_equal(result.params['fwhm'].stderr, 0.00806917, decimal=6) assert_almost_equal(result.params['height'].stderr, 0.03009459, decimal=6) assert_almost_equal(result.params['sigma'].correl['center'], -4.6623973788006615e-05, decimal=6) assert_almost_equal(result.params['sigma'].correl['amplitude'], 0.651304091954038, decimal=6) assert_almost_equal(result.params['center'].correl['amplitude'], -4.390334984618851e-05, decimal=6)
def get_model(self): self.x = np.array([0, 1, 2, 6, 12, 24]) self.y = np.array(self.norm_vals) # Compound model with Voigt curve. self.background = ExponentialModel(prefix='b_') self.pars = self.background.guess(self.y, x=self.x) self.peak = VoigtModel(prefix='p_') self.pars += self.peak.guess(self.y, x=self.x) self.comp_mod = self.peak + self.background self.init = self.comp_mod.eval(self.pars, x=self.x) self.comp_out = self.comp_mod.fit( self.y, x=self.x, fit_kws={'nan_policy': 'propagate' }) # instead of 'omit', it keeps up the zero vals. self.comp_list = self.comp_out.fit_report().split('\n') self.comp_chisq = float(self.comp_list[6][-5:]) self.out = self.comp_out self.chisq = float(self.comp_list[6][-5:]) self.usedmod = self.comp_mod self.model_flag = "composite (exponential+Voigt)" return self.comp_out, self.comp_chisq, self.out, self.chisq, self.usedmod, self.model_flag
def test_numdifftools_calc_covar_false(): pytest.importorskip("numdifftools") # load data to be fitted data = np.loadtxt(os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['sigma'].set(min=-np.inf) # do fit, with leastsq and nelder result = mod.fit(y, params, x=x, method='leastsq') result_ndt = mod.fit(y, params, x=x, method='nelder', calc_covar=False) # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_ndt = [result_ndt.params[p].value for p in result_ndt.params.valuesdict()] assert_allclose(vals_ndt, vals, rtol=5e-3) assert_allclose(result_ndt.chisqr, result.chisqr) assert result_ndt.covar is None assert result_ndt.errorbars is False
def test_saveload_usersyms(): """Test save/load of modelresult with non-trivial user symbols, this example uses a VoigtModel, wheree `wofz()` is used in a constraint expression""" x = np.linspace(0, 20, 501) y = gaussian(x, 1.1, 8.5, 2) + lorentzian(x, 1.7, 8.5, 1.5) np.random.seed(20) y = y + np.random.normal(size=len(x), scale=0.025) model = VoigtModel() pars = model.guess(y, x=x) result = model.fit(y, pars, x=x) savefile = 'tmpvoigt_modelresult.sav' save_modelresult(result, savefile) assert_param_between(result.params['sigma'], 0.7, 2.1) assert_param_between(result.params['center'], 8.4, 8.6) assert_param_between(result.params['height'], 0.2, 1.0) time.sleep(0.25) result2 = load_modelresult(savefile) assert_param_between(result2.params['sigma'], 0.7, 2.1) assert_param_between(result2.params['center'], 8.4, 8.6) assert_param_between(result2.params['height'], 0.2, 1.0)
def call_voigt(x, y, cen, count, pars): label='v'+str(count)+'_' voigt = VoigtModel(prefix=label) pars.update(voigt.make_params()) pars[label+'center'].set(cen, min=cen-0.01, max=cen+0.01) pars[label+'amplitude'].set(-0.5, min=-10., max=0.0001) pars[label+'sigma'].set(0.1, min=0.005, max=0.25) pars[label+'gamma'].set(value=0.7, vary=True, expr='') return voigt
def call_voigt(x, y, cen, count, pars): label='v'+str(count)+'_' voigt = VoigtModel(prefix=label) pars.update(voigt.make_params()) pars[label+'center'].set(cen, min=cen-0.01, max=cen+0.01) pars[label+'amplitude'].set(0, min=-(max(y)-min(y))*1.5, max=0.0001) pars[label+'sigma'].set(fw_set/4, min=0.005, max=fw_set/2.3548) pars[label+'gamma'].set(value=fw_set/4, vary=True, expr='') return voigt
def test_numdifftools_with_bounds(fit_method): pytest.importorskip("numdifftools") if fit_method in ['shgo', 'dual_annealing']: pytest.importorskip("scipy", minversion="1.2") # load data to be fitted data = np.loadtxt( os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['amplitude'].set(min=25, max=70) params['sigma'].set(max=1) params['center'].set(min=5, max=15) # do fit, here with leastsq model result = mod.fit(y, params, x=x, method='leastsq') result_ndt = mod.fit(y, params, x=x, method=fit_method) # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_ndt = [ result_ndt.params[p].value for p in result_ndt.params.valuesdict() ] assert_allclose(vals_ndt, vals, rtol=0.1) assert_allclose(result_ndt.chisqr, result.chisqr, rtol=1e-5) # assert that parameter uncertaintes from leastsq and calculated from # the covariance matrix using numdifftools are very similar stderr = [result.params[p].stderr for p in result.params.valuesdict()] stderr_ndt = [ result_ndt.params[p].stderr for p in result_ndt.params.valuesdict() ] perr = np.array(stderr) / np.array(vals) perr_ndt = np.array(stderr_ndt) / np.array(vals_ndt) assert_almost_equal(perr_ndt, perr, decimal=3) # assert that parameter correlatations from leastsq and calculated from # the covariance matrix using numdifftools are very similar for par1 in result.var_names: cor = [ result.params[par1].correl[par2] for par2 in result.params[par1].correl.keys() ] cor_ndt = [ result_ndt.params[par1].correl[par2] for par2 in result_ndt.params[par1].correl.keys() ] assert_almost_equal(cor_ndt, cor, decimal=2)
def curve_fitting_voigt(dref, pars=None): xdata = dref.index.to_numpy() ydata = dref.to_numpy() mod = VoigtModel() if pars is None: pars = mod.guess(ydata, x=xdata) out = mod.fit(ydata, pars, x=xdata) return out
def test_least_squares_solver_options(peakdata, capsys): """Test least_squares algorithm, pass options to solver.""" x = peakdata[0] y = peakdata[1] mod = VoigtModel() params = mod.guess(y, x=x) solver_kws = {'verbose': 2} mod.fit(y, params, x=x, method='least_squares', fit_kws=solver_kws) captured = capsys.readouterr() assert 'Iteration' in captured.out assert 'final cost' in captured.out
def singleline(x, y, tipo, arquivo): ## pdb.set_trace() mod = VoigtModel() pars = mod.guess(y, x=x) pars['gamma'].set(value=0.7, vary=True, expr='') ## pars['sigma'].set(value=0.7, vary=True, expr='') out = mod.fit(y, pars, x=x) gamma = out.best_values['gamma'] sigma = out.best_values['sigma'] center = out.best_values['center'] calcsingleline(gamma, sigma, center, tipo, arquivo)
def test_numdifftools_no_bounds(): numdifftools = pytest.importorskip("numdifftools") # load data to be fitted data = np.loadtxt( os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['sigma'].set(min=-np.inf) # do fit, here with leastsq model result = mod.fit(y, params, x=x, method='leastsq') for fit_method in ['nelder', 'basinhopping', 'ampgo']: result_ndt = mod.fit(y, params, x=x, method=fit_method) # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_ndt = [ result_ndt.params[p].value for p in result_ndt.params.valuesdict() ] assert_allclose(vals_ndt, vals, rtol=5e-3) assert_allclose(result_ndt.chisqr, result.chisqr) # assert that parameter uncertaintes from leastsq and calculated from # the covariance matrix using numdifftools are very similar stderr = [result.params[p].stderr for p in result.params.valuesdict()] stderr_ndt = [ result_ndt.params[p].stderr for p in result_ndt.params.valuesdict() ] perr = np.array(stderr) / np.array(vals) perr_ndt = np.array(stderr_ndt) / np.array(vals_ndt) assert_almost_equal(perr_ndt, perr, decimal=4) # assert that parameter correlatations from leastsq and calculated from # the covariance matrix using numdifftools are very similar for par1 in result.var_names: cor = [ result.params[par1].correl[par2] for par2 in result.params[par1].correl.keys() ] cor_ndt = [ result_ndt.params[par1].correl[par2] for par2 in result_ndt.params[par1].correl.keys() ] assert_almost_equal(cor_ndt, cor, decimal=2)
def correlate_spectra(obs_flx, obs_wvl, ref_flx, ref_wvl): # convert spectra sampling to logspace obs_flux_res_log, _ = spectra_logspace(obs_flx, obs_wvl) ref_flux_sub_log, wvl_log = spectra_logspace(ref_flx, ref_wvl) wvl_step = ref_wvl[1] - ref_wvl[0] # correlate the two spectra min_flux = 0.95 ref_flux_sub_log[ref_flux_sub_log > min_flux] = 0. obs_flux_res_log[obs_flux_res_log > min_flux] = 0. corr_res = correlate(ref_flux_sub_log, obs_flux_res_log, mode='same', method='fft') # plt.plot(corr_res) # plt.show() # plt.close() # create a correlation subset that will actually be analysed corr_w_size = 100 corr_c_off = np.int64(len(corr_res) / 2.) corr_pos_min = corr_c_off - corr_w_size corr_pos_max = corr_c_off + corr_w_size # print corr_pos_min, corr_pos_max corr_res_sub = corr_res[corr_pos_min:corr_pos_max] corr_res_sub -= np.median(corr_res_sub) corr_res_sub_x = np.arange(len(corr_res_sub)) # analyze correlation function by fitting gaussian/voigt/lorentzian distribution to it fit_model = VoigtModel() parameters = fit_model.guess(corr_res_sub, x=corr_res_sub_x) corr_fit_res = fit_model.fit(corr_res_sub, parameters, x=corr_res_sub_x) corr_center = corr_fit_res.params['center'].value # plt.plot(corr_res_sub) # plt.axvline(corr_center) # plt.show() # plt.close() # determine the actual shift idx_no_shift = np.int32(len(corr_res) / 2.) idx_center = corr_c_off - corr_w_size + corr_center log_shift_px = idx_no_shift - idx_center log_shift_wvl = log_shift_px * wvl_step wvl_log_new = wvl_log - log_shift_wvl rv_shifts = (wvl_log_new[1:] - wvl_log_new[:-1]) / wvl_log_new[:-1] * 299792.458 * log_shift_px if log_shift_wvl < 2: return np.nanmedian(rv_shifts) else: # something went wrong return np.nan
def fit_peak_1d( xdata: np.ndarray, ydata: np.ndarray, engine: str = 'lmfit', ) -> np.ndarray: """ Description ----------- Perform 1D peak fitting using Voigt function Parameters ---------- xdata: np.ndarray independent var array ydata: np.ndarray dependent var array engien: str engine name, [lmfit, tomoproc] Returns ------- dict dictionary of peak parameters NOTE ---- Return dictionary have different entries. """ if engine.lower() in ['lmfit', 'external']: mod = VoigtModel() pars = mod.guess(ydata, x=xdata) out = mod.fit(ydata, pars, x=xdata) return out.best_values else: popt, pcov = curve_fit( voigt1d, xdata, ydata, maxfev=int(1e6), p0=[ydata.max(), xdata.mean(), 1, 1], bounds=([0, xdata.min(), 0, 0], [ ydata.max() * 10, xdata.max(), xdata.max() - xdata.min(), np.inf ]), ) return { 'amplitude': popt[0], 'center': popt[1], 'fwhm': popt[2], 'shape': popt[3], }
def fitsample(data, theta_initial, theta_final): x = data[:,0] y = data[:,1] m = (x > theta_initial) & (x < theta_final) x_fit = x[m] y_fit = y[m] pseudovoigt1 = VoigtModel(prefix = 'pv1_') pars= pseudovoigt1.make_params() pars['pv1_center'].set(13.5, min = 13.4, max = 13.6) pars['pv1_sigma'].set(0.05, min= 0.01, max = 0.1) pars['pv1_amplitude'].set(70, min = 1, max = 100) #pars['pv1_fraction'].set(0.5) lorentz2 = LorentzianModel(prefix = 'lor2_') pars.update(lorentz2.make_params()) pars['lor2_center'].set(13.60, min = 13.4, max = 13.9) pars['lor2_sigma'].set(0.1, min= 0.01) pars['lor2_amplitude'].set(10, min = 1, max = 50 ) #pars['lor2_fraction'].set(0.5) line1 = LinearModel(prefix ='l1_') pars.update(line1.make_params()) pars['l1_slope'].set(0) pars['l1_intercept'].set(240, min = 200, max = 280) mod = pseudovoigt1 + lorentz2 + line1 v = pars.valuesdict() result = mod.fit(y_fit, pars, x=x_fit) #print(result.fit_report()) pv1_pos = result.params['pv1_center'].value pv1_height = result.params['pv1_height'].value lor2_pos = result.params['lor2_center'].value lor2_height = result.params['lor2_height'].value #peak_area = pars['gau1_fwhm'].value*peak_amp #plt.xlim([theta_initial, theta_final]) #plt.ylim([100, 500]) #plt.semilogy(x_fit, y_fit, 'bo') #plt.semilogy (x_fit, result.init_fit, 'k--') #plt.semilogy(x_fit, result.best_fit, 'r-') #plt.show() return pv1_pos, pv1_height, lor2_pos, lor2_height
def test_least_squares_cov_x(peakdata, bounds): """Test calculation of cov. matrix from Jacobian, with/without bounds.""" x = peakdata[0] y = peakdata[1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) if bounds: params['amplitude'].set(min=25, max=70) params['sigma'].set(min=0, max=1) params['center'].set(min=5, max=15) else: params['sigma'].set(min=-np.inf) # do fit with least_squares and leastsq algorithm result = mod.fit(y, params, x=x, method='least_squares') result_lsq = mod.fit(y, params, x=x, method='leastsq') # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_lsq = [ result_lsq.params[p].value for p in result_lsq.params.valuesdict() ] assert_allclose(vals, vals_lsq, rtol=1e-5) assert_allclose(result.chisqr, result_lsq.chisqr) # assert that parameter uncertaintes obtained from the leastsq method and # those from the covariance matrix estimated from the Jacbian matrix in # least_squares are similar stderr = [result.params[p].stderr for p in result.params.valuesdict()] stderr_lsq = [ result_lsq.params[p].stderr for p in result_lsq.params.valuesdict() ] assert_allclose(stderr, stderr_lsq, rtol=1e-4) # assert that parameter correlations obtained from the leastsq method and # those from the covariance matrix estimated from the Jacbian matrix in # least_squares are similar for par1 in result.var_names: cor = [ result.params[par1].correl[par2] for par2 in result.params[par1].correl.keys() ] cor_lsq = [ result_lsq.params[par1].correl[par2] for par2 in result_lsq.params[par1].correl.keys() ] assert_allclose(cor, cor_lsq, rtol=1e-2)
def voigtFit(filename, xloc=0, yloc=1, stats=False, plot=False): # Read Data df = pd.read_csv(filename, header=None) # Remove bad pixel df.drop(df.index[446], inplace=True) df.fillna(method='bfill', inplace=True) # Narrow region for later delays if 'd5' in filename: df = df[(df.iloc[:, xloc] > 287.75) & (df.iloc[:, xloc] < 288.6)] if 'd4' in filename and ('m1r' or 'm2' in filename): df = df[(df.iloc[:, xloc] > 287.75) & (df.iloc[:, xloc] < 288.6)] x = np.array(df.iloc[:, xloc]) y = np.array(df.iloc[:, yloc]) # Set Voigt fit parameters mod = VoigtModel() pars = mod.guess(y, x=x) pars['gamma'].set(value=0.7, vary=True, expr='') # Perform Voigt fit out = mod.fit(y, pars, x=x) # Print fit statistics if stats: print(out.fit_report(min_correl=0.25, show_correl=False)) # Plot Voigt fit if plot: plt.plot(x, y, 'o', markersize=2.0, c='blue') plt.plot(x, out.best_fit, 'r-') dely = out.eval_uncertainty(sigma=5) plt.fill_between(x, out.best_fit - dely, out.best_fit + dely, color="#bc8f8f") plt.xlabel = 'Wavelength (nm)' plt.ylabel = 'Intensity (a.u.)' plt.xlim((287, 289.5)) plt.show() # Save fit statistics for par_name, param in out.params.items(): if par_name == 'gamma': return pd.DataFrame({ 'fid': [filename], 'fwhm_L': [2 * param.value], 'error': [2 * param.stderr], 'R^2': [out.redchi] })
def xrdCalculationProcessing(spectrumData, centerXValsList, heightList, axs, setupOptions): proposedUserSubstrateTwoTheta = centerXValsList[heightList.index(max(heightList))] substrateModel = VoigtModel() params = substrateModel.guess(spectrumData.bgSubIntensity, x=spectrumData.xVals, negative=False) out = substrateModel.fit(spectrumData.bgSubIntensity, params, x=spectrumData.xVals) fullModelSubstrateTwoTheta = out.best_values['center'] if abs(fullModelSubstrateTwoTheta - proposedUserSubstrateTwoTheta) <= 0.1: # looks like the user selected the substrate as a peak, use their value substrateTwoTheta = proposedUserSubstrateTwoTheta else: # Looks like the user did not select the substrate as a peak, use a global value from fitting all data substrateTwoTheta = fullModelSubstrateTwoTheta literatureSubstrateTwoTheta = calculateTwoTheta(snContentPercent=0) # Reusing Sn content to 2theta equation twoThetaOffset = substrateTwoTheta - literatureSubstrateTwoTheta offsetCorrectedCenterTwoThetaList = np.asarray(centerXValsList) - twoThetaOffset for centerTwoTheta in offsetCorrectedCenterTwoThetaList: michaelSnContent = round(calculateXRDSnContent(centerTwoTheta), 1) print("Michael Comp:", michaelSnContent) print("Zach Comp:", round(calculateXRDSnContent_Zach(centerTwoTheta), 1)) if abs(centerTwoTheta - literatureSubstrateTwoTheta) > 0.05: # Don't draw one for the substrate _, centerIndex = closestNumAndIndex(spectrumData.xVals, centerTwoTheta + twoThetaOffset) if setupOptions.isLogPlot: basePlot = spectrumData.lnIntensity subtractedPlot = spectrumData.lnBgSubIntensity else: basePlot = spectrumData.intensity subtractedPlot = spectrumData.bgSubIntensity if setupOptions.doBackgroundSubtraction: an0 = axs[0].annotate(str(abs(michaelSnContent)), xy=(centerTwoTheta + twoThetaOffset, basePlot[centerIndex]), xycoords='data', xytext=(0, 72), textcoords='offset points', arrowprops=dict(arrowstyle="->", shrinkA=10, shrinkB=5, patchA=None, patchB=None)) an0.draggable() an1 = axs[1].annotate(str(abs(michaelSnContent)), xy=( centerTwoTheta + twoThetaOffset, subtractedPlot[centerIndex]), xycoords='data', xytext=(0, 72), textcoords='offset points', arrowprops=dict(arrowstyle="->", shrinkA=10, shrinkB=5, patchA=None, patchB=None)) an1.draggable() else: an0 = axs.annotate(str(abs(michaelSnContent)), xy=(centerTwoTheta + twoThetaOffset, subtractedPlot[centerIndex]), xycoords='data', xytext=(0, 72), textcoords='offset points', arrowprops=dict(arrowstyle="->", shrinkA=10, shrinkB=5, patchA=None, patchB=None)) an0.draggable()
def test_cov_x_with_bounds(): # load data to be fitted data = np.loadtxt( os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['amplitude'].set(min=25, max=70) params['sigma'].set(min=0, max=1) params['center'].set(min=5, max=15) # do fit, here with leastsq model result = mod.fit(y, params, x=x, method='least_squares') result_lsq = mod.fit(y, params, x=x, method='leastsq') # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_lsq = [ result_lsq.params[p].value for p in result_lsq.params.valuesdict() ] assert_allclose(vals_lsq, vals, rtol=1e-5) assert_allclose(result_lsq.chisqr, result.chisqr) # assert that parameter uncertaintes obtained from the leastsq method and # those from the covariance matrix estimated from the Jacbian matrix in # least_squares are similar stderr = [result.params[p].stderr for p in result.params.valuesdict()] stderr_lsq = [ result_lsq.params[p].stderr for p in result_lsq.params.valuesdict() ] assert_almost_equal(stderr_lsq, stderr, decimal=6) # assert that parameter correlations obtained from the leastsq method and # those from the covariance matrix estimated from the Jacbian matrix in # least_squares are similar for par1 in result.var_names: cor = [ result.params[par1].correl[par2] for par2 in result.params[par1].correl.keys() ] cor_lsq = [ result_lsq.params[par1].correl[par2] for par2 in result_lsq.params[par1].correl.keys() ] assert_almost_equal(cor_lsq, cor, decimal=6)
def ChoosePeakType(self, peaktype, i): """ This function helps to create the `CompositeModel() <https://lmfit.github.io/lmfit-py/model.html#lmfit.model.CompositeModel>`_ . Implemented models are: `GaussianModel() <https://lmfit.github.io/lmfit-py/builtin_models.html#lmfit.models.GaussianModel>`_ , `LorentzianModel() <https://lmfit.github.io/lmfit-py/builtin_models.html#lmfit.models.LorentzianModel>`_ , `VoigtModel() <https://lmfit.github.io/lmfit-py/builtin_models.html#lmfit.models.VoigtModel>`_ , `BreitWignerModel() <https://lmfit.github.io/lmfit-py/builtin_models.html#lmfit.models.BreitWignerModel>`_ . Parameters ---------- peaktype : string Possible line shapes of the peaks to fit are 'breit_wigner', 'lorentzian', 'gaussian', and 'voigt'. i : int Integer between 0 and (N-1) to distinguish between N peaks of the same peaktype. It is used in the prefix. Returns ------- lmfit.models.* : class Returns either VoigtModel(), BreitWignerModel(), LorentzianModel(), or GaussianModel() depending on the peaktype with *Model(prefix = prefix, nan_policy = 'omit'). The prefix contains the peaktype and i. """ prefix = peaktype + '_p' + str(i + 1) + '_' if peaktype == 'voigt': return VoigtModel(prefix=prefix, nan_policy='omit') elif peaktype == 'breit_wigner': return BreitWignerModel(prefix=prefix, nan_policy='omit') elif peaktype == 'lorentzian': return LorentzianModel(prefix=prefix, nan_policy='omit') elif peaktype == 'gaussian': return GaussianModel(prefix=prefix, nan_policy='omit')
def fit_voigt_over_linear(q, I, cen=1, sig=0.002, sigmin=1e-4, sigmax=0.01, amplmin=0, amplmax=500, trim=0.06, plot=False): trim = logical_and(q < cen + trim, q > cen - trim) q = q[trim] I = I[trim] mod = LinearModel() mod.set_param_hint('slope', value=-20) mod.set_param_hint('intercept', value=10) lineout = mod.fit(I, x=q) pars = lineout.params mod += VoigtModel() pars.add('center', value=cen) pars.add('sigma', value=sig, max=sigmax, min=sigmin) pars.add('amplitude', value=amplmin / 2 + amplmax / 2, min=amplmin, max=amplmax) out = mod.fit(I, pars, x=q) return out
def fit(self, xx, yy, fitType): xx = np.asarray(xx) yy = np.asarray(yy) print("XX", xx) print("YY", yy) print(len(xx)) print(len(yy)) print("XX", xx) x1 = xx[0] x2 = xx[-1] y1 = yy[0] y2 = yy[-1] m = (y2 - y1) / (x2 - x1) b = y2 - m * x2 if fitType == "Gaussian": mod = GaussianModel() elif fitType == "Lorentzian": mod = LorentzianModel() else: mod = VoigtModel() pars = mod.guess(yy, x=xx, slope=m) print(pars) mod = mod + LinearModel() pars.add('intercept', value=b, vary=True) pars.add('slope', value=m, vary=True) out = mod.fit(yy, pars, x=xx) return out.best_fit
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 methodfunciont(key): dic = { "VoigtModel": VoigtModel(), "PseudoVoigtModel": PseudoVoigtModel(), "GaussianModel": GaussianModel() } return dic[key]
def fit(self): x = self.energies_eV y = self.intensities model = VoigtModel() init_parameters = model.guess(y, x=x) self.fit_results = model.fit(y, init_parameters, x=x) values = self.fit_results.params.valuesdict() self.fit_results_position_eV = values['center'] self.fit_results_fwhm_eV = values['fwhm'] self.fit_results_sigma_eV = values['sigma'] self.fit_results_gamma_eV = values['gamma'] self.fit_results_area = values['amplitude'] self.fit_results_height = values['height']
def test_numdifftools_with_bounds(fit_method): pytest.importorskip("numdifftools") if fit_method == 'shgo': pytest.importorskip("scipy", minversion="1.2") # load data to be fitted data = np.loadtxt(os.path.join(os.path.dirname(__file__), '..', 'examples', 'test_peak.dat')) x = data[:, 0] y = data[:, 1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) params['amplitude'].set(min=25, max=70) params['sigma'].set(max=1) params['center'].set(min=5, max=15) # do fit, here with leastsq model result = mod.fit(y, params, x=x, method='leastsq') result_ndt = mod.fit(y, params, x=x, method=fit_method) # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_ndt = [result_ndt.params[p].value for p in result_ndt.params.valuesdict()] assert_allclose(vals_ndt, vals, rtol=0.1) assert_allclose(result_ndt.chisqr, result.chisqr, rtol=1e-5) # assert that parameter uncertaintes from leastsq and calculated from # the covariance matrix using numdifftools are very similar stderr = [result.params[p].stderr for p in result.params.valuesdict()] stderr_ndt = [result_ndt.params[p].stderr for p in result_ndt.params.valuesdict()] perr = np.array(stderr) / np.array(vals) perr_ndt = np.array(stderr_ndt) / np.array(vals_ndt) assert_almost_equal(perr_ndt, perr, decimal=3) # assert that parameter correlatations from leastsq and calculated from # the covariance matrix using numdifftools are very similar for par1 in result.var_names: cor = [result.params[par1].correl[par2] for par2 in result.params[par1].correl.keys()] cor_ndt = [result_ndt.params[par1].correl[par2] for par2 in result_ndt.params[par1].correl.keys()] assert_almost_equal(cor_ndt, cor, decimal=2)
def peakfit(xvals, yvals, yerrors=None): """ Fit peak to scans """ peak_mod = VoigtModel() # peak_mod = GaussianModel() bkg_mod = LinearModel() pars = peak_mod.guess(yvals, x=xvals) pars += bkg_mod.make_params(intercept=np.min(yvals), slope=0) # pars['gamma'].set(value=0.7, vary=True, expr='') # don't fix gamma mod = peak_mod + bkg_mod out = mod.fit(yvals, pars, x=xvals, weights=yerrors) return out
def test_least_squares_cov_x(peakdata, bounds): """Test calculation of cov. matrix from Jacobian, with/without bounds.""" x = peakdata[0] y = peakdata[1] # define the model and initialize parameters mod = VoigtModel() params = mod.guess(y, x=x) if bounds: params['amplitude'].set(min=25, max=70) params['sigma'].set(min=0, max=1) params['center'].set(min=5, max=15) else: params['sigma'].set(min=-np.inf) # do fit with least_squares and leastsq algorithm result = mod.fit(y, params, x=x, method='least_squares') result_lsq = mod.fit(y, params, x=x, method='leastsq') # assert that fit converged to the same result vals = [result.params[p].value for p in result.params.valuesdict()] vals_lsq = [result_lsq.params[p].value for p in result_lsq.params.valuesdict()] assert_allclose(vals, vals_lsq, rtol=1e-5) assert_allclose(result.chisqr, result_lsq.chisqr) # assert that parameter uncertaintes obtained from the leastsq method and # those from the covariance matrix estimated from the Jacbian matrix in # least_squares are similar stderr = [result.params[p].stderr for p in result.params.valuesdict()] stderr_lsq = [result_lsq.params[p].stderr for p in result_lsq.params.valuesdict()] assert_allclose(stderr, stderr_lsq, rtol=1e-4) # assert that parameter correlations obtained from the leastsq method and # those from the covariance matrix estimated from the Jacbian matrix in # least_squares are similar for par1 in result.var_names: cor = [result.params[par1].correl[par2] for par2 in result.params[par1].correl.keys()] cor_lsq = [result_lsq.params[par1].correl[par2] for par2 in result_lsq.params[par1].correl.keys()] assert_allclose(cor, cor_lsq, rtol=1e-2)
def voigt_response(self, sigma=None, gamma=None): ''' Fit the background with a Voigt profile to determine the response of the spectrometer If you have a good, clear signal, set sigma and gamma to None (done by default) If your signal is poor, set sigma and gamma using a fit to a good signal, and then only the position of the central wavelength will be altered. ''' vm = VoigtModel() par_v = vm.guess(self.bkgd, x=self.lamb) par_v['center'].set(value=532e-9, vary=True) err = (self.bkgd_ferr * self.shot) if sigma is not None: #if a width is provided, fix it. par_v['sigma'].set(value=sigma, vary=False) if gamma is not None: #if a width is provided, fix it. par_v['gamma'].set(value=gamma, vary=False, expr='') elif gamma is None: #vary gamma for better fit - this is not done by default par_v['gamma'].set(value=par_v['sigma'].value, vary=True, expr='') ##Fit the Voigt Model to the data vm_fit = vm.fit(self.bkgd, par_v, x=self.lamb) self.vm_fit = vm_fit #now crop the data so that the response is symmetric for the convolution to work l0 = vm_fit.best_values['center'] self.sigma = vm_fit.best_values['sigma'] self.l0 = l0 l0_i = find_nearest(self.lamb, l0) l_size = self.lamb.size take_l = min( l0_i, l_size - l0_i) #trim the shortest distance from the central wavelength low_i = l0_i - take_l high_i = l0_i + take_l self.lamb = self.lamb[low_i:high_i] self.bkgd = self.bkgd[low_i:high_i] self.shot = self.shot[low_i:high_i] self.shot_ferr = self.shot_ferr[low_i:high_i] self.bkgd_ferr = self.bkgd_ferr[low_i:high_i] #the response is taken from the model so it is nice and smooth self.response = vm_fit.best_fit[low_i:high_i] self.shift = self.lamb - l0 #this is useful for plotting data
def fit_one_Voigt(x_lst,y_lst, pre): ''' Fits one Pseudo Voigt returns the results object ''' x_lst = np.asarray(x_lst) y_lst = np.asarray(y_lst) mod = VoigtModel(prefix = pre, independent_vars=['x'],nan_policy='raise') # here we set up the peak fitting guess. Then the peak fitter will make a parameter object out of them mod.set_param_hint(pre+'amplitude', value = 4 * np.max(y_lst), min = 3*np.max(y_lst), max = 7*np.max(y_lst), vary=True) # mod.set_param_hint(prefp+'center', value = x_max, min = x_max*(1-wiggle_room), max = x_max*(1+wiggle_room),vary=True) mod.set_param_hint(pre+'center', value = x_lst[np.argmax(y_lst)], vary=True) # Basically FWHM/3.6 w_guess = 2 mod.set_param_hint(pre+'sigma', value = w_guess, min = 0, max = 5*w_guess,vary=True) result = mod.fit(y_lst, x = x_lst, params = mod.make_params()) return result
def test_param_set(): np.random.seed(2015) x = np.arange(0, 20, 0.05) y = gaussian(x, amplitude=15.43, center=4.5, sigma=2.13) y = y + 0.05 - 0.01 * x + np.random.normal(scale=0.03, size=len(x)) model = VoigtModel() params = model.guess(y, x=x) # test #1: gamma is constrained to equal sigma assert (params['gamma'].expr == 'sigma') params.update_constraints() sigval = params['gamma'].value assert_allclose(params['gamma'].value, sigval, 1e-4, 1e-4, '', True) # test #2: explicitly setting a param value should work, even when # it had been an expression. The value will be left as fixed gamval = 0.87543 params['gamma'].set(value=gamval) assert (params['gamma'].expr is None) assert (not params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True) # test #3: explicitly setting an expression should work # Note, the only way to ensure that **ALL** constraints are up to date # is to call params.update_constraints(). This is because the constraint # may have multiple dependencies. params['gamma'].set(expr='sigma/2.0') assert (params['gamma'].expr is not None) assert (not params['gamma'].vary) params.update_constraints() assert_allclose(params['gamma'].value, sigval / 2.0, 1e-4, 1e-4, '', True) # test #4: explicitly setting a param value WITH vary=True # will set it to be variable gamval = 0.7777 params['gamma'].set(value=gamval, vary=True) assert (params['gamma'].expr is None) assert (params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True)
def __init__(self, ModelString): self.NumModels = {} self.NumModels['Total'] = len(ModelString) self.NumModels['Linear'] = len(re.findall('L', ModelString)) self.NumModels['Gaussian'] = len(re.findall('G', ModelString)) self.NumModels['Voigt'] = len(re.findall('V', ModelString)) if self.NumModels['Total'] != self.NumModels['Linear'] + self.NumModels[ 'Gaussian'] + self.NumModels['Voigt']: print( 'Warning: Number of total functions does not equal number of summed functions' ) ModelCounter = 0 i = 0 while i < self.NumModels['Linear']: if ModelCounter == 0: self.Model = LinearModel(prefix='L' + str(i + 1) + '_') else: self.Model = self.Model + LinearModel(prefix='L' + str(i + 1) + '_') ModelCounter = ModelCounter + 1 i += 1 i = 0 while i < self.NumModels['Gaussian']: if ModelCounter == 0: self.Model = GaussianModel(prefix='G' + str(i + 1) + '_') else: self.Model = self.Model + GaussianModel(prefix='G' + str(i + 1) + '_') ModelCounter = ModelCounter + 1 i += 1 i = 0 while i < self.NumModels['Voigt']: if ModelCounter == 0: self.Model = VoigtModel(prefix='V' + str(i + 1) + '_') else: self.Model = self.Model + VoigtModel(prefix='V' + str(i + 1) + '_') ModelCounter = ModelCounter + 1 i += 1
def test_param_set(): np.random.seed(2015) x = np.arange(0, 20, 0.05) y = gaussian(x, amplitude=15.43, center=4.5, sigma=2.13) y = y + 0.05 - 0.01*x + np.random.normal(scale=0.03, size=len(x)) model = VoigtModel() params = model.guess(y, x=x) # test #1: gamma is constrained to equal sigma assert(params['gamma'].expr == 'sigma') params.update_constraints() sigval = params['gamma'].value assert_allclose(params['gamma'].value, sigval, 1e-4, 1e-4, '', True) # test #2: explicitly setting a param value should work, even when # it had been an expression. The value will be left as fixed gamval = 0.87543 params['gamma'].set(value=gamval) assert(params['gamma'].expr is None) assert(not params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True) # test #3: explicitly setting an expression should work # Note, the only way to ensure that **ALL** constraints are up to date # is to call params.update_constraints(). This is because the constraint # may have multiple dependencies. params['gamma'].set(expr='sigma/2.0') assert(params['gamma'].expr is not None) assert(not params['gamma'].vary) params.update_constraints() assert_allclose(params['gamma'].value, sigval/2.0, 1e-4, 1e-4, '', True) # test #4: explicitly setting a param value WITH vary=True # will set it to be variable gamval = 0.7777 params['gamma'].set(value=gamval, vary=True) assert(params['gamma'].expr is None) assert(params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True)
def fit_voigt(ax, spectra, args): fit_range = args.pl_range wl = spectra[:, 0] sp = spectra[:, 2] wl_fit = wl[(wl > fit_range[0]) & (wl < fit_range[1])] sp_fit = sp[(wl > fit_range[0]) & (wl < fit_range[1])] mod = VoigtModel() + ConstantModel() pars = mod.make_params(amplitude=np.max(sp_fit), center=wl_fit[np.argmax(sp_fit)], sigma=10, gamma=10, c=0) out = mod.fit(sp_fit, pars, x=wl_fit) ax.plot(wl_fit, out.best_fit, 'k--', alpha=0.8) print(f'Peak center = {out.params["center"].value:.2f} nm \n' f'FWHM = {out.params["fwhm"].value:.2f} nm') return out.params['center'].value, out.params['fwhm'].value,
data = loadtxt('test_peak.dat') x = data[:, 0] y = data[:, 1] gmodel = GaussianModel() gmodel.guess_starting_values(y, x=x) gresult = gmodel.fit(y, x=x) print 'With Gaussian: ' print fit_report(gresult.params, min_correl=0.25) print 'Chi-square = %.3f, Reduced Chi-square = %.3f' % (gresult.chisqr, gresult.redchi) plt.plot(x, y, 'k') plt.plot(x, 10*(y-gresult.best_fit), 'r-') vmodel = VoigtModel() vmodel.guess_starting_values(y, x=x) vresult = vmodel.fit(y, x=x) print 'With Voigt: ' print fit_report(vresult.params, min_correl=0.25) print 'Chi-square = %.3f, Reduced Chi-square = %.3f' % (vresult.chisqr, vresult.redchi) plt.plot(x, 10*(y-vresult.best_fit), 'b-') vmodel.params['gamma'].vary = True vmodel.params['gamma'].expr = None vresult2 = vmodel.fit(y, x=x)
def pre_edge_baseline(energy, norm=None, group=None, form='lorentzian', emin=None, emax=None, elo=None, ehi=None, with_line=True, _larch=None): """remove baseline from main edge over pre edge peak region This assumes that pre_edge() has been run successfully on the spectra and that the spectra has decent pre-edge subtraction and normalization. Arguments ---------- energy: array of x-ray energies, in eV, or group (see note 1) norm: array of normalized mu(E) group: output group elo: low energy of pre-edge peak region to not fit baseline [e0-20] ehi: high energy of pre-edge peak region ot not fit baseline [e0-10] emax: max energy (eV) to use for baesline fit [e0-5] emin: min energy (eV) to use for baesline fit [e0-40] form: form used for baseline (see note 2) ['lorentzian'] with_line: whether to include linear component in baseline ['True'] Returns ------- None A group named 'prepeaks' will be created in the output group, with the following attributes: energy energy array for pre-edge peaks = energy[emin:emax] baseline fitted baseline array over pre-edge peak energies norm spectrum over pre-edge peak energies peaks baseline-subtraced spectrum over pre-edge peak energies centroid estimated centroid of pre-edge peaks (see note 3) peak_energies list of predicted peak energies (see note 4) fit_details details of fit to extract pre-edge peaks. (if the output group is None, _sys.xafsGroup will be written to) Notes ----- 1 If the first argument is a Group, it must contain 'energy' and 'norm'. See First Argrument Group in Documentation 2 A function will be fit to the input mu(E) data over the range between [emin:elo] and [ehi:emax], ignorng the pre-edge peaks in the region [elo:ehi]. The baseline function is specified with the `form` keyword argument, which can be one of 'lorentzian', 'gaussian', or 'voigt', with 'lorentzian' the default. In addition, the `with_line` keyword argument can be used to add a line to this baseline function. 3 The value calculated for `prepeaks.centroid` will be found as (prepeaks.energy*prepeaks.peaks).sum() / prepeaks.peaks.sum() 4 The values in the `peak_energies` list will be predicted energies of the peaks in `prepeaks.peaks` as found by peakutils. """ energy, norm, group = parse_group_args(energy, members=('energy', 'norm'), defaults=(norm,), group=group, fcn_name='pre_edge_baseline') prepeaks_setup(energy, norm=norm, group=group, emin=emin, emax=emax, elo=elo, ehi=ehi, _larch=_larch) emin = group.prepeaks.emin emax = group.prepeaks.emax elo = group.prepeaks.elo ehi = group.prepeaks.ehi dele = 1.e-13 + min(np.diff(energy))/5.0 imin = index_of(energy, emin+dele) ilo = index_of(energy, elo+dele) ihi = index_of(energy, ehi+dele) imax = index_of(energy, emax+dele) # build xdat, ydat: dat to fit (skipping pre-edge peaks) xdat = np.concatenate((energy[imin:ilo+1], energy[ihi:imax+1])) ydat = np.concatenate((norm[imin:ilo+1], norm[ihi:imax+1])) # build fitting model: note that we always include # a LinearModel but may fix slope and intercept form = form.lower() if form.startswith('voig'): model = VoigtModel() elif form.startswith('gaus'): model = GaussianModel() else: model = LorentzianModel() model += LinearModel() params = model.make_params(amplitude=1.0, sigma=2.0, center=emax, intercept=0, slope=0) params['amplitude'].min = 0.0 params['sigma'].min = 0.25 params['sigma'].max = 50.0 params['center'].max = emax + 25.0 params['center'].min = emax - 25.0 if not with_line: params['slope'].vary = False params['intercept'].vary = False result = model.fit(ydat, params, x=xdat) cen = dcen = 0. peak_energies = [] # energy including pre-edge peaks, for output edat = energy[imin: imax+1] norm = norm[imin:imax+1] bline = peaks = dpeaks = norm*0.0 # get baseline and resulting norm over edat range if result is not None: bline = result.eval(result.params, x=edat) peaks = norm-bline # estimate centroid cen = (edat*peaks).sum() / peaks.sum() # uncertainty in norm includes only uncertainties in baseline fit # and uncertainty in centroid: try: dpeaks = result.eval_uncertainty(result.params, x=edat) except: dbpeaks = 0.0 cen_plus = (edat*(peaks+dpeaks)).sum()/ (peaks+dpeaks).sum() cen_minus = (edat*(peaks-dpeaks)).sum()/ (peaks-dpeaks).sum() dcen = abs(cen_minus - cen_plus) / 2.0 # locate peak positions if HAS_PEAKUTILS: peak_ids = peakutils.peak.indexes(peaks, thres=0.05, min_dist=2) peak_energies = [edat[pid] for pid in peak_ids] group = set_xafsGroup(group, _larch=_larch) group.prepeaks = Group(energy=edat, norm=norm, baseline=bline, peaks=peaks, delta_peaks=dpeaks, centroid=cen, delta_centroid=dcen, peak_energies=peak_energies, fit_details=result, emin=emin, emax=emax, elo=elo, ehi=ehi, form=form, with_line=with_line) return
def test_param_set(): np.random.seed(2015) x = np.arange(0, 20, 0.05) y = gaussian(x, amplitude=15.43, center=4.5, sigma=2.13) y = y + 0.05 - 0.01*x + np.random.normal(scale=0.03, size=len(x)) model = VoigtModel() params = model.guess(y, x=x) # test #1: gamma is constrained to equal sigma assert(params['gamma'].expr == 'sigma') params.update_constraints() sigval = params['sigma'].value assert_allclose(params['gamma'].value, sigval, 1e-4, 1e-4, '', True) # test #2: explicitly setting a param value should work, even when # it had been an expression. The value will be left as fixed gamval = 0.87543 params['gamma'].set(value=gamval) assert(params['gamma'].expr is None) assert(not params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True) # test #3: explicitly setting an expression should work # Note, the only way to ensure that **ALL** constraints are up to date # is to call params.update_constraints(). This is because the constraint # may have multiple dependencies. params['gamma'].set(expr='sigma/2.0') assert(params['gamma'].expr is not None) assert(not params['gamma'].vary) params.update_constraints() assert_allclose(params['gamma'].value, sigval/2.0, 1e-4, 1e-4, '', True) # test #4: explicitly setting a param value WITH vary=True # will set it to be variable gamval = 0.7777 params['gamma'].set(value=gamval, vary=True) assert(params['gamma'].expr is None) assert(params['gamma'].vary) assert_allclose(params['gamma'].value, gamval, 1e-4, 1e-4, '', True) # test 5: make sure issue #389 is fixed: set boundaries and make sure # they are kept when changing the value amplitude_vary = params['amplitude'].vary amplitude_expr = params['amplitude'].expr params['amplitude'].set(min=0.0, max=100.0) params.update_constraints() assert_allclose(params['amplitude'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 100.0, 1e-4, 1e-4, '', True) params['amplitude'].set(value=40.0) params.update_constraints() assert_allclose(params['amplitude'].value, 40.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 100.0, 1e-4, 1e-4, '', True) assert(params['amplitude'].expr == amplitude_expr) assert(params['amplitude'].vary == amplitude_vary) assert(not params['amplitude'].brute_step) # test for possible regressions of this fix (without 'expr'): # the set function should only change the requested attribute(s) params['amplitude'].set(value=35.0) params.update_constraints() assert_allclose(params['amplitude'].value, 35.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 100.0, 1e-4, 1e-4, '', True) assert(params['amplitude'].vary == amplitude_vary) assert(params['amplitude'].expr == amplitude_expr) assert(not params['amplitude'].brute_step) # set minimum params['amplitude'].set(min=10.0) params.update_constraints() assert_allclose(params['amplitude'].value, 35.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].min, 10.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 100.0, 1e-4, 1e-4, '', True) assert(params['amplitude'].vary == amplitude_vary) assert(params['amplitude'].expr == amplitude_expr) assert(not params['amplitude'].brute_step) # set maximum params['amplitude'].set(max=110.0) params.update_constraints() assert_allclose(params['amplitude'].value, 35.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].min, 10.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 110.0, 1e-4, 1e-4, '', True) assert(params['amplitude'].vary == amplitude_vary) assert(params['amplitude'].expr == amplitude_expr) assert(not params['amplitude'].brute_step) # set vary params['amplitude'].set(vary=False) params.update_constraints() assert_allclose(params['amplitude'].value, 35.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].min, 10.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 110.0, 1e-4, 1e-4, '', True) assert(params['amplitude'].vary == False) assert(params['amplitude'].expr == amplitude_expr) assert(not params['amplitude'].brute_step) # set brute_step params['amplitude'].set(brute_step=0.1) params.update_constraints() assert_allclose(params['amplitude'].value, 35.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].min, 10.0, 1e-4, 1e-4, '', True) assert_allclose(params['amplitude'].max, 110.0, 1e-4, 1e-4, '', True) assert(params['amplitude'].vary == False) assert(params['amplitude'].expr == amplitude_expr) assert_allclose(params['amplitude'].brute_step, 0.1, 1e-4, 1e-4, '', True) # test for possible regressions of this fix for variables WITH 'expr': height_value = params['height'].value height_min = params['height'].min height_max = params['height'].max height_vary = params['height'].vary height_expr = params['height'].expr height_brute_step = params['height'].brute_step # set vary=True should remove expression params['height'].set(vary=True) params.update_constraints() assert_allclose(params['height'].value, height_value, 1e-4, 1e-4, '', True) assert_allclose(params['height'].min, height_min, 1e-4, 1e-4, '', True) assert_allclose(params['height'].max, height_max, 1e-4, 1e-4, '', True) assert(params['height'].vary == True) assert(params['height'].expr == None) assert(params['height'].brute_step == height_brute_step) # setting an expression should set vary=False params['height'].set(expr=height_expr) params.update_constraints() assert_allclose(params['height'].value, height_value, 1e-4, 1e-4, '', True) assert_allclose(params['height'].min, height_min, 1e-4, 1e-4, '', True) assert_allclose(params['height'].max, height_max, 1e-4, 1e-4, '', True) assert(params['height'].vary == False) assert(params['height'].expr == height_expr) assert(params['height'].brute_step == height_brute_step) # changing min/max should not remove expression params['height'].set(min=0) params.update_constraints() assert_allclose(params['height'].value, height_value, 1e-4, 1e-4, '', True) assert_allclose(params['height'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['height'].max, height_max, 1e-4, 1e-4, '', True) assert(params['height'].vary == height_vary) assert(params['height'].expr == height_expr) assert(params['height'].brute_step == height_brute_step) # changing brute_step should not remove expression params['height'].set(brute_step=0.1) params.update_constraints() assert_allclose(params['height'].value, height_value, 1e-4, 1e-4, '', True) assert_allclose(params['height'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['height'].max, height_max, 1e-4, 1e-4, '', True) assert(params['height'].vary == height_vary) assert(params['height'].expr == height_expr) assert_allclose(params['amplitude'].brute_step, 0.1, 1e-4, 1e-4, '', True) # changing the value should remove expression and keep vary=False params['height'].set(brute_step=0) params['height'].set(value=10.0) params.update_constraints() assert_allclose(params['height'].value, 10.0, 1e-4, 1e-4, '', True) assert_allclose(params['height'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['height'].max, height_max, 1e-4, 1e-4, '', True) assert(params['height'].vary == False) assert(params['height'].expr == None) assert(params['height'].brute_step == height_brute_step) # passing expr='' should only remove the expression params['height'].set(expr=height_expr) # first restore the original expr params.update_constraints() params['height'].set(expr='') params.update_constraints() assert_allclose(params['height'].value, height_value, 1e-4, 1e-4, '', True) assert_allclose(params['height'].min, 0.0, 1e-4, 1e-4, '', True) assert_allclose(params['height'].max, height_max, 1e-4, 1e-4, '', True) assert(params['height'].vary == False) assert(params['height'].expr == None) assert(params['height'].brute_step == height_brute_step)