''' Simple fit without fit error estimation. For correct fit errors check the advanced example. Bound should be defined. Eta has to be > 1. ''' from scipy.optimize import curve_fit import matplotlib.pyplot as plt import numpy as np import pylandau # Create fake data with possion error mpv, eta, sigma, A = 30, 5, 4, 1000 x = np.arange(0, 100, 0.5) y = pylandau.langau(x, mpv, eta, sigma, A) yerr = np.random.normal(np.zeros_like(x), np.sqrt(y)) yerr[y < 1] = 1 y += yerr # Fit with constrains coeff, pcov = curve_fit(pylandau.langau, x, y, sigma=yerr, absolute_sigma=True, p0=(mpv, eta, sigma, A), bounds=(1, 10000)) # Plot plt.errorbar(x, y, np.sqrt(pylandau.langau(x, *coeff)), fmt=".") plt.plot(x, pylandau.langau(x, *coeff), "-") plt.show()
# Plot the Landau Gauss convolution (Langau) and cross check with convolution with scipy import numpy as np import pylandau import matplotlib.pyplot as plt from scipy.ndimage.filters import gaussian_filter1d mpv, eta, sigma, A = 10, 1, 3, 1 x = np.arange(0, 50, 0.01) y = pylandau.landau(x, mpv=mpv, eta=eta, A=A) y_gconv = gaussian_filter1d(y, sigma=sigma / 0.01) # Scaled means that the resulting Landau*Gauss function maximum is A at mpv # Otherwise the underlying Landau function before convolution has the # function maximum A at mpv y_gconv_2 = pylandau.langau(x, mpv, eta, sigma, A, scale_langau=False) y_gconv_3 = pylandau.langau(x, mpv, eta, sigma, A, scale_langau=True) plt.plot(x, y, label='Landau') plt.plot(x, y_gconv_2, label='Langau') plt.plot(x, y_gconv, '--', label='Langau Scipy') plt.plot(x, y_gconv_3, label='Langau scaled') plt.legend(loc=0) plt.show()
# Plot the functions using matplotlib import numpy as np import pylandau import matplotlib.pyplot as plt x = np.arange(0, 100, 0.01) for A, eta, mpv in ((1, 1, 10), (1, 2, 30), (0.5, 5, 50)): # Use the function that calculates y values when given array plt.plot(x, pylandau.landau(x, mpv, eta, A), '-', label='mu=%1.1f, eta=%1.1f, A=%1.1f' % (mpv, eta, A)) # Use the function that calculates the y value given a x value, (e.g. needed for minimizers) y = np.array([pylandau.get_landau(x_value, mpv, eta, A) for x_value in x]) plt.plot(x, y, 'r--', label='mu=%1.1f, eta=%1.1f, A=%1.1f' % (mpv, eta, A)) # Use the function that calculates the y value given a x value, (e.g. needed for minimizers) sigma = 10 plt.plot(x, pylandau.langau(x, mpv, eta, sigma, A), '--', label='mu=%1.1f, eta=%1.1f, sigma=%1.1f, A=%1.1f' % (A, eta, sigma, mpv)) plt.legend(loc=0) plt.show()
def test_negative_amplitude(self): ''' Check exception for negative amplitude ''' x = np.linspace(0, 100) with self.assertRaises(ValueError): pylandau.landau(x, A=-1) x = np.linspace(0, 100) with self.assertRaises(ValueError): pylandau.langau(x, A=-1)
def test_langau_stability(self, mpv, eta, sigma, A): ''' Check Langau outputs for same input parameters ''' # Correct input to avoid oscillations if sigma > 100 * eta: sigma = eta assume(sigma * eta < constrains.LANGAU_MAX_ETA_SIGMA) x = np.linspace(mpv - 5 * sigma * eta, mpv + 5 * sigma * eta, 1000) y_1 = pylandau.langau(x, mpv=mpv, eta=eta, sigma=sigma, A=A) y_2 = pylandau.langau(x, mpv=mpv, eta=eta, sigma=sigma, A=A) self.assertTrue(np.all(y_1 == y_2))
def test_langau_stability(self, mpv, eta, sigma, A): ''' Check Langau outputs for same input parameters ''' # Correct input to avoid oscillations if sigma > 100 * eta: sigma = eta assume(sigma * eta < constraints.LANGAU_MAX_ETA_SIGMA) x = np.linspace(mpv - 5 * sigma * eta, mpv + 5 * sigma * eta, 1000) y_1 = pylandau.langau(x, mpv=mpv, eta=eta, sigma=sigma, A=A) y_2 = pylandau.langau(x, mpv=mpv, eta=eta, sigma=sigma, A=A) self.assertTrue(np.all(y_1 == y_2))
def test_landau_fallback(self, pars): ''' Check if langau is landau for sigma = 0 ''' (mpv, eta, A) = pars x = np.linspace(mpv - 5 * eta, mpv + 5 * eta, 1000) y_1 = pylandau.landau(x, mpv=mpv, eta=eta, A=A) y_2 = pylandau.langau(x, mpv=mpv, eta=eta, sigma=0., A=A) self.assertTrue(np.all(y_1 == y_2))
def _calc_landau(x): if self.simulate_data.dut_noise[dut_index]: # PyLandau Langau MPV parameter is the MPV of the Langau not the Landau only! y = pylandau.langau( x, mpv=mpv_charge, eta=mpv_charge / 10., sigma=self.simulate_data.dut_noise[dut_index], scale_langau=False) else: y = pylandau.landau(x, mpv=mpv_charge, eta=mpv_charge / 10) return y
def fitfunction_testbeam_array(x, mu, eta, sigma, A, p4, p5): global c c+=1 return pylandau.langau(x, mu, eta, sigma, A) + p4 * np.exp(-x / p5)
def run(self): """Runs the routines to generate all langau specific data""" # Here events with only one cluster are choosen or two, you decide indNumClus = self.get_num_clusters(self.data, self.numClusters) indizes = np.concatenate(indNumClus) # Slice the data from the bas analysis for further calculations valid_events_clustersize = np.take(self.data["base"]["Clustersize"], indizes) valid_events_clusters = np.take(self.data["base"]["Clusters"], indizes) valid_events_Signal = np.take(self.data["base"]["Signal"],indizes) self.results_dict["Clustersize"] = [] # TODO: here a non numba optimized version is used. We should use numba here! # Calculate the energy deposition PER Clustersize and add it to self.results_dict["Clustersize"] self.cluster_analysis(valid_events_Signal, valid_events_clusters, valid_events_clustersize) # With all the data from every clustersize add all together and fit the main langau to it finalE = np.zeros(0) finalNoise = np.zeros(0) for cluster in self.results_dict["Clustersize"]: # Clean up and extra energy cut indi = np.nonzero(cluster["signal"] > 0)[0] nogarbage = cluster["signal"][indi] # ultra_high_energy_cut indi = np.nonzero(nogarbage < self.Ecut)[0] cluster["signal"] = cluster["signal"][indi] finalE = np.append(finalE, cluster["signal"]) finalNoise = np.append(finalNoise, cluster["noise"]) # Fit the langau to the summ of all individual clusters coeff, _, _, error_bins, edges= self.fit_langau(finalE, finalNoise, bins=self.results_dict["bins"], cut = self.ClusterCut) self.results_dict["signal"] = finalE self.results_dict["noise"] = finalNoise self.results_dict["langau_coeff"] = coeff plotxrange = np.arange(0., edges[-1], edges[-1] / 1000.) self.results_dict["langau_data"] = [ plotxrange,pylandau.langau(plotxrange,*coeff) ] # aka x and y data self.results_dict["data_error"] = error_bins # Seed cut langau, taking only the bare hit channels which are above seed cut levels if self.seed_cut_langau: seed_cut_channels = self.data["base"]["Channel_hit"] signals = self.data["base"]["Signal"] seedcutADC = [] seedcutChannels = [] for i, signal in enumerate(tqdm(signals, desc="(langau SC) Processing events")): if signal[seed_cut_channels[i]].any(): seedcutADC.append(signal[seed_cut_channels[i]]) seedcutChannels.append(seed_cut_channels[i]) if self.Charge_scale: self.log.info("Converting ADC to electrons for SC Langau...") converted = self.main.calibration.convert_ADC_to_e(np.concatenate(seedcutADC), np.concatenate(seedcutChannels)) else: converted = np.absolute(np.concatenate(seedcutADC)) finalE = np.array(converted, dtype=np.float32) # get rid of 0 events indizes = np.nonzero(finalE > 0)[0] nogarbage = finalE[indizes] indizes = np.nonzero(nogarbage < self.Ecut)[0] # ultra_high_energy_cut coeff, _, _, error_bins, edges = self.fit_langau( nogarbage[indizes], bins=self.results_dict["bins"], cut = self.SCCut) self.results_dict["signal_SC"] = nogarbage[indizes] self.results_dict["langau_coeff_SC"] = coeff plotxrange = np.arange(0., edges[-1], edges[-1]/1000.) self.results_dict["langau_data_SC"] = [ plotxrange,pylandau.langau(plotxrange,*coeff) ] # aka x and y data # Old attempts for multiprocessing, no speed up seen here # # Try joblib # #start = time() # #arg_instances = [(size, valid_events_clustersize, # # valid_events_Signal, valid_events_clusters, # # noise, charge_cal) for size in clustersize_list] # #results = Parallel(n_jobs=4, backend="threading")(map(delayed(self.process_cluster_size), # # arg_instances)) # #for res in results: # # self.results_dict[data]["Clustersize"].append(res) # # !!!!!!!!!!!!!!! NO SPEED BOOST HERE!!!!!!!!!!!!!!!!!!!! # General langau, where all clustersizes are considered # if self.poolsize > 1: # paramslist = [] # for size in self.cluster_size_list: # cls_ind = np.nonzero(valid_events_clustersize == size)[0] # paramslist.append((cls_ind, valid_events_Signal, # valid_events_clusters, # self.main.calibration.convert_ADC_to_e, # self.main.noise)) # COMMENT: lagau_cluster not defined!!!! # Here multiple cpu calculate the energy of the events per clustersize # results = self.pool.starmap(self.langau_cluster, paramslist, # chunksize=1) # self.results_dict["Clustersize"] = results return self.results_dict.copy()
def minimizeMe(mpv, eta, sigma, A): chi2 = np.sum( np.square(y - langau(x, mpv, eta, sigma, A).astype(float)) / np.square(yerr.astype(float))) return chi2 / (x.shape[0] - 5) # devide by NDF
def fit_landau_migrad(x, y, p0, limit_mpv, limit_eta, limit_sigma, limit_A): def minimizeMe(mpv, eta, sigma, A): chi2 = np.sum( np.square(y - langau(x, mpv, eta, sigma, A).astype(float)) / np.square(yerr.astype(float))) return chi2 / (x.shape[0] - 5) # devide by NDF # Prefit to get correct errors yerr = np.sqrt(y) # Assume error from measured data yerr[y < 1] = 1 m = iminuit.Minuit(minimizeMe, mpv=p0[0], limit_mpv=limit_mpv, error_mpv=1, eta=p0[1], error_eta=0.1, limit_eta=limit_eta, sigma=p0[2], error_sigma=0.1, limit_sigma=limit_sigma, A=p0[3], error_A=1, limit_A=limit_A, errordef=1, print_level=2) m.migrad() if not m.get_fmin().is_valid: raise RuntimeError('Fit did not converge') # Main fit with model errors yerr = np.sqrt( langau(x, mpv=m.values['mpv'], eta=m.values['eta'], sigma=m.values['sigma'], A=m.values['A'])) # Assume error from measured data yerr[y < 1] = 1 m = iminuit.Minuit(minimizeMe, mpv=m.values['mpv'], limit_mpv=limit_mpv, error_mpv=1, eta=m.values['eta'], error_eta=0.1, limit_eta=limit_eta, sigma=m.values['sigma'], error_sigma=0.1, limit_sigma=limit_sigma, A=m.values['A'], error_A=1, limit_A=limit_A, errordef=1, print_level=2) m.migrad() fit_values = m.values values = np.array([ fit_values['mpv'], fit_values['eta'], fit_values['sigma'], fit_values['A'] ]) m.hesse() m.minos() minos_errors = m.get_merrors() if not minos_errors['mpv'].is_valid: print( 'Warning: MPV error determination with Minos failed! You can still use Hesse errors.' ) errors = np.array([(minos_errors['mpv'].lower, minos_errors['mpv'].upper), (minos_errors['eta'].lower, minos_errors['eta'].upper), (minos_errors['sigma'].lower, minos_errors['sigma'].upper), (minos_errors['A'].lower, minos_errors['A'].upper)]) return values, errors, m
def test_langau_mpv(self, mpv): ''' Check Langau MPV position ''' x = np.linspace(mpv - 10, mpv + 10, 1000) y = pylandau.langau(x, mpv=mpv, eta=1., sigma=1., A=1.) delta = x[1] - x[0] self.assertAlmostEqual(x[np.argmax(y)], mpv, delta=delta)
def _fitfunction_testbeam(self, x, *p): ''' Custom fit function for testbeam histograms. ''' mu, eta, sigma, A, p4, p5 = p return landau.langau(x, mu, eta, sigma, A) + p4*np.exp(-x/p5)
if not minos_errors['mpv'].is_valid: print('Warning: MPV error determination with Minos failed! You can still use Hesse errors.') errors = np.array([(minos_errors['mpv'].lower, minos_errors['mpv'].upper), (minos_errors['eta'].lower, minos_errors['eta'].upper), (minos_errors['sigma'].lower, minos_errors['sigma'].upper), (minos_errors['A'].lower, minos_errors['A'].upper)]) return values, errors, m if __name__ == '__main__': # Fake counting experiment with Landgaus distribution x = np.arange(100).astype(np.float) y = langau(x, mpv=30., eta=5., sigma=4., A=1000.) # Add poisson error y += np.random.normal(np.zeros_like(y), np.sqrt(y)) # Fit the data with numerical exact error estimation # Taking into account correlations values, errors, m = fit_landau_migrad(x, y, p0=[x.shape[0] / 2., 10., 10., np.max(y)], limit_mpv=(10., 100.), limit_eta=(2., 20.), limit_sigma=(2., 20.), limit_A=(500., 1500.))
def fitfunction(x, *p): ''' Fitfunction used for testbeam data ''' mu, eta, sigma, A, p4, p5 = p return landau.langau(x, mu, eta, sigma, A) + p4*np.exp(-x/p5)
def fit_testbeam_spectrum(self, x, y, p0, fit_range=None, ymax=None, debug=False, detail=False): ''' Fit custom fitfunction to spectrum from testbeam and plot both data and fitted function. Parameters: x:np.ndarray - x-values of histogrammed data. y:np.ndarray - y-values of histogrammed data. p0:tuple - Set of startparameters for fit. Format: (mu, eta, sigma, A). fit_range:tuple - [Optional] Region where the fit should be performed. Obtained automatically if None. ymax:int - [Optional] Maximum of y-axis of plot. Obtained automatically if None. debug:boolean - [Optional] If set, the fitfunction is plotted with startparameters for debugging. ''' self.error = False threshold = self.find_threshold(y) if not fit_range or fit_range == 'auto': fit_range = (threshold, np.round(np.amax(x))) if ymax is None: ymax = 1.1*np.amax(y) plot_range = (threshold, np.round(np.amax(x))) try: p, _ = curve_fit(self._fitfunction_testbeam, x[(x > fit_range[0]) & (x < fit_range[1])], y[(x > fit_range[0]) & (x < fit_range[1])], p0=p0) mpv = float(x[np.argmax(landau.langau(x, p[0], p[1], p[2], p[3]))]) if debug: p = p0 if p[0] <= threshold: logging.error('Encountered an error during analysis of file %s: Peak is below threshold!' % (self.f)) self.errorfiles.append([os.path.join(self.dirpath, self.f), 'Fit: Peak below threshold!']) self.error = True elif p[3] <= 0: logging.error('Encountered an error during analysis of file %s: Amplitude is negative!' % (self.f)) self.errorfiles.append([os.path.join(self.dirpath, self.f), 'Fit: Amplitude is negative!']) self.error = True elif (10. * float(p[1]) < float(p[2])): logging.error('Encountered an error during analysis of file %s: Oscillation occurred!' % (self.f)) self.errorfiles.append([os.path.join(self.dirpath, self.f), 'Fit: Oscillation occurred!']) #self.error = True except RuntimeError as e: logging.error('Encountered an error during analysis of file %s: %s' % (self.f, e)) self.errorfiles.append([os.path.join(self.dirpath, self.f), e]) self.error = True if self.error: p = p0 mpv = p0[0] else: p = tuple([abs(e) for e in p]) logging.debug('Fitparameters:\nMPV = %1.2f\nmu = %1.3f\neta = %1.3f\nsigma = %1.3f\nA = %1.3f\np4 = %1.3f\np5 = %1.3f' % ((mpv,) + p)) plt.cla() plt.plot(x[(x > plot_range[0]) & (x < plot_range[1])], y[(x > plot_range[0]) & (x < plot_range[1])], label='Data', color='blue', zorder=0, linewidth=1) plt.fill_between(x[(x > plot_range[0]) & (x < plot_range[1])], y[(x > plot_range[0]) & (x < plot_range[1])], facecolor='blue', alpha=0.3, zorder=0) plt.plot(x[(x > fit_range[0]) & (x < fit_range[1])], self._fitfunction_testbeam(x, *p)[(x > fit_range[0]) & (x < fit_range[1])], label='Fit', color='red', zorder=2, linewidth=1) if detail: plt.plot(x[(x > fit_range[0]) & (x < fit_range[1])], landau.langau(x, *p[:4])[(x > fit_range[0]) & (x < fit_range[1])], label='Langau', color='purple', linestyle='--', zorder=2, linewidth=1) plt.plot(x[(x > fit_range[0]) & (x < fit_range[1])], p[4]*np.exp(-x/p[5])[(x > fit_range[0]) & (x < fit_range[1])], label='Exponential', color='yellow', linestyle='--', zorder=2, linewidth=1) plt.plot([mpv, mpv], [0., ymax], label='MPV', color='green', linestyle='--') if self._energy_calibrated: plt.xlim(self._energy_intercept, np.round(np.amax(x))) else: plt.xlim(0, np.round(np.amax(x))) plt.ylim(0, ymax) plt.legend(loc=1, fontsize=12) plt.grid() plt.title(self.title) plt.xlabel('Energy [%s]' % self._energy_unit) plt.ylabel('Count') ax = plt.axes() textstr = '$N = %i$' % self.event_count if self._darkframe_corrected: textstr += '\nDarkframe-cor.' if self.error: textstr += '\nFit failed!' else: textstr += '\nFit parameters:\n$MPV = %1.2f$\n$\mu=%.2f$\n$\eta=%.2f$\n$\sigma=%.2f$\n$A=%.2f$\n$p_4=%.2f$\n$p_5=%.2f$' % ((mpv,) + p) box = AnchoredText(textstr, loc=5) ax.add_artist(box) ax.annotate('Threshold', xy=(threshold,0), xytext=(threshold,-0.1*ymax), horizontalalignment='center', arrowprops=dict(facecolor='black', shrink=0.05, width=0, headwidth=0)) plt.savefig(self.outfile + '.' + self.outformat)
def minimizeMe(mpv, eta, sigma, A): chi2 = np.sum(np.square(y - langau(x, mpv, eta, sigma, A).astype(float)) / np.square(yerr.astype(float))) return chi2 / (x.shape[0] - 5) # devide by NDF
def lnlike(theta): #parameters.set_params(pd.pyListToVec(theta)) #params = pd.vector() #params[:] = theta #parameters.set_params(params) #f = pd.fit(parameters) #chisq = f.generateChiSquare() #print "chisq = " + str(chisq) #below is OK for traditional chisquared calculation #should find a way to do this dynamically at some point, based on input parameters. #y = pylandau.langau(E, float(theta[0]), 64, 45, float(theta[1])) #units in keV, mpv, width, sigma, scale #y2 = pylandau.langau(E,float(theta[2]), 64, 45, float(theta[3])) #y3 = pylandau.langau(E,float(theta[4]), 64, 45, float(theta[5])) #y4 = y+y2#+y3 chisq = 0.0 #for value1,value2 in zip(C,y4): # chisq += math.pow((value1-value2),2)/np.sqrt(value1) #I have to figure out ssh for this computer, or put all of this on the server at UML!(DUH OF COURSE) #At least for the doublet,I have to reduce scale parameter to relative ratio #For triplet, I could proceed with same scale parameter and normalize using 3 parameter #could reduce parameters by turning scale between 2 low energy peaks into a ratio #ratioLowE = theta[1]/theta[5] #changing to correlation between scale factors (ratio dependence) #could also introduce scale factor #below is for "unbinned" log(likelihood) calculation #for doublet #fractionTot = float(theta[2]+theta[3]) #ratio is better constrained y = pylandau.langau(x1, float(theta[0]), 64, 45, float(theta[2])) #units in keV, mpv, width, sigma, scale y2 = pylandau.langau(x1,float(theta[1]), 64, 45, 1.-float(theta[2])) #if prior between 0 and 1 this should converge to fractional distribution # #remember to redo params and priors dan! y4 = y+y2 #for triplet # fractionTot = float(theta[3]+theta[4]+theta[5]) # #fractionTot = float(theta[5]) #going to make theta[3] the log of the intensity ratio between low energy peak1 to peak2 #theta[4] is then fraction that goes to high energy peak (I hope the math below works out for the individual fractions) # fraction12 = theta[3] # # fraction12 = np.exp(theta[3]) # y = pylandau.langau(x1, float(theta[0]), 64, 45, float(1.-theta[4])/float(1+fraction12)) #units in keV, mpv, width, sigma, scale # y2 = pylandau.langau(x1,float(theta[1]), 64, 45, float(theta[4])) #if prior between 0 and 1 this should converge to fractional distribution # y3 = pylandau.langau(x1,float(theta[2]), 64, 45, float(fraction12)*float(1.-theta[4])/float(1+fraction12)) # #remember to redo params and priors dan! # y4 = y+y2+y3 #original #y = pylandau.langau(x1, float(theta[0]), 64, 45, float(theta[1])) #units in keV, mpv, width, sigma, scale #y2 = pylandau.langau(x1,float(theta[2]), 64, 45, float(theta[3])) #y3 = pylandau.langau(x1,float(theta[4]), 64, 45, float(theta[5])) #if I get rid of normalize perhaps I will recover absolute heigh information #of course it collapses to largest values since this "maximizes" probability normalize = np.trapz(y4) #normalize = 1. for energy in E: #because of binning of x1, energy is mapped -0.5 from the entry chisq += np.log(y4[int(energy-0.5)]/normalize) #for likelihood analysis the the probability that a model is correct for n points is by comparing the model distribution, P, at points, p_n, and multiplying them all P(p_1)*P(p_2)*...*P(p_n). Thus when taking the log these can all be added together. This is the natural way to do these kind of fits, though time consuming. #I'm appending the temperature parameter beta to the end of the return chisq
def test_langau_A(self, A): ''' Check Langau amplitude ''' mpv = 1. x = np.linspace(mpv - 10, mpv + 10, 1000) y = pylandau.langau(x, mpv=mpv, eta=1., sigma=1., A=A) self.assertAlmostEqual(y.max(), A, delta=1e-4 * A)
def fit_landau_migrad(x, y, p0, limit_mpv, limit_eta, limit_sigma, limit_A): def minimizeMe(mpv, eta, sigma, A): chi2 = np.sum(np.square(y - langau(x, mpv, eta, sigma, A).astype(float)) / np.square(yerr.astype(float))) return chi2 / (x.shape[0] - 5) # devide by NDF # Prefit to get correct errors yerr = np.sqrt(y) # Assume error from measured data yerr[y < 1] = 1 m = iminuit.Minuit(minimizeMe, mpv=p0[0], limit_mpv=limit_mpv, error_mpv=1, eta=p0[1], error_eta=0.1, limit_eta=limit_eta, sigma=p0[2], error_sigma=0.1, limit_sigma=limit_sigma, A=p0[3], error_A=1, limit_A=limit_A, errordef=1, print_level=2) m.migrad() if not m.get_fmin().is_valid: raise RuntimeError('Fit did not converge') # Main fit with model errors yerr = np.sqrt(langau(x, mpv=m.values['mpv'], eta=m.values['eta'], sigma=m.values['sigma'], A=m.values['A'])) # Assume error from measured data yerr[y < 1] = 1 m = iminuit.Minuit(minimizeMe, mpv=m.values['mpv'], limit_mpv=limit_mpv, error_mpv=1, eta=m.values['eta'], error_eta=0.1, limit_eta=limit_eta, sigma=m.values['sigma'], error_sigma=0.1, limit_sigma=limit_sigma, A=m.values['A'], error_A=1, limit_A=limit_A, errordef=1, print_level=2) m.migrad() fit_values = m.values values = np.array([fit_values['mpv'], fit_values['eta'], fit_values['sigma'], fit_values['A']]) m.hesse() m.minos() minos_errors = m.get_merrors() if not minos_errors['mpv'].is_valid: print('Warning: MPV error determination with Minos failed! You can still use Hesse errors.') errors = np.array([(minos_errors['mpv'].lower, minos_errors['mpv'].upper), (minos_errors['eta'].lower, minos_errors['eta'].upper), (minos_errors['sigma'].lower, minos_errors['sigma'].upper), (minos_errors['A'].lower, minos_errors['A'].upper)]) return values, errors, m
from scipy.optimize import curve_fit import matplotlib.pyplot as plt import numpy as np import pylandau import math import csv # Create fake data with possion error mpv, eta, sigma, A = 30, 5, 4, 1000 x = np.arange(0, 100, 0.5) y = pylandau.langau(x, mpv, eta, sigma, A) yerr = np.random.normal(np.zeros_like(x), np.sqrt(y)) yerr[y < 1] = 1 y += yerr #read in tab delimited data file energies = [] counts = [] countsErr = np.array([]) with open('decayEnergy_100kevBins_filter2.txt', 'r') as f: next(f) # skip headings reader = csv.reader(f, delimiter='\t') for energy, count in reader: energies.append(energy) counts.append(count) E = np.array(energies, dtype=float) C = np.array(counts, dtype=float) #np.sqrt(E)
'Warning: MPV error determination with Minos failed! You can still use Hesse errors.' ) errors = np.array([(minos_errors['mpv'].lower, minos_errors['mpv'].upper), (minos_errors['eta'].lower, minos_errors['eta'].upper), (minos_errors['sigma'].lower, minos_errors['sigma'].upper), (minos_errors['A'].lower, minos_errors['A'].upper)]) return values, errors, m if __name__ == '__main__': # Fake counting experiment with Landgaus distribution x = np.arange(100).astype(np.float) y = langau(x, mpv=30., eta=5., sigma=4., A=1000.) # Add poisson error y += np.random.normal(np.zeros_like(y), np.sqrt(y)) # Fit the data with numerical exact error estimation # Taking into account correlations values, errors, m = fit_landau_migrad( x, y, p0=[x.shape[0] / 2., 10., 10., np.max(y)], limit_mpv=(10., 100.), limit_eta=(2., 20.), limit_sigma=(2., 20.), limit_A=(500., 1500.)) # Plot fit result
def fit_langau(self, x, y, p0, fit_range, ymax=None, debug=False): ''' Fit a simple langau function only to peak of arbitrary spectrum and plot both data and fitted function. Parameters: x:np.ndarray - x-values of histogrammed data. y:np.ndarray - y-values of histogrammed data. p0:tuple - Set of startparameters for fit. Format: (mu, eta, sigma, A). fit_range:tuple - Approximate region of peak where the fit should be performed. ymax:int - [Optional] Maximum of y-axis of plot. Obtained automatically if None. debug:boolean - [Optional] If set, the fitfunction is plotted with startparameters for debugging. ''' self.error = False threshold = self.find_threshold(y) plot_range = (threshold, np.round(np.amax(x))) if fit_range is None: fit_range = (threshold, np.round(np.amax(x))) if ymax is None: ymax = 1.1*np.amax(y) try: p, _ = curve_fit(landau.langau, x[(x > fit_range[0]) & (x < fit_range[1])], y[(x > fit_range[0]) & (x < fit_range[1])], p0=p0) if debug: p = p0 except RuntimeError as e: logging.error('Encountered an error during analysis of file %s: %s' % (os.path.join(self.dirpath, self.f), e)) self.errorfiles.append([os.path.join(self.dirpath, self.f), e]) self.error = True p = p0 if p[3] <= 0: logging.error('Encountered an error during analysis of file %s: Amplitude is negative!' % (os.path.join(self.dirpath, self.f))) self.errorfiles.append([os.path.join(self.dirpath, self.f), 'Fit: Amplitude is negative!']) self.error = True p = p0 p = tuple([abs(e) for e in p]) logging.debug('Fitparameters:\nmu = %1.3f\neta = %1.3f\nsigma = %1.3f\nA = %1.3f' % tuple(p)) plt.cla() plt.plot(x[(x > plot_range[0]) & (x < plot_range[1])], y[(x > plot_range[0]) & (x < plot_range[1])], label='Data', color='blue', zorder=0, linewidth=1) plt.fill_between(x[(x > plot_range[0]) & (x < plot_range[1])], y[(x > plot_range[0]) & (x < plot_range[1])], facecolor='blue', alpha=0.3, zorder=0) plt.plot(x[(x > fit_range[0]) & (x < fit_range[1])], landau.langau(x, *p)[(x > fit_range[0]) & (x < fit_range[1])], label='Fit', color='red', zorder=2, linewidth=1) if self._energy_calibrated: plt.xlim(self._energy_intercept,np.round(np.amax(x))) else: plt.xlim(0,np.round(np.amax(x))) plt.ylim(0,ymax) plt.legend(loc=1, fontsize=12) plt.grid() plt.title(self.title) plt.xlabel('Energy [%s]' % self._energy_unit) plt.ylabel('Count') ax = plt.axes() textstr = '$N = %i$' % self.event_count if self._darkframe_corrected: textstr += '\nDarkframe-cor.' if self.error: textstr += '\nFit failed!' else: textstr += '\nFit parameters:\n$\mu=%.2f$\n$\eta=%.2f$\n$\sigma=%.2f$\n$A=%.2f$' % (p) box = AnchoredText(textstr, loc=5) ax.add_artist(box) ax.annotate('Threshold', xy=(threshold,0), xytext=(threshold,-0.1*ymax), horizontalalignment='center', arrowprops=dict(facecolor='black', shrink=0.05, width=0, headwidth=0)) plt.savefig(self.outfile + '.' + self.outformat)
''' Simple fit without fit error estimation. For correct fit errors check the advanced example. Bound should be defined. Eta has to be > 1. ''' from scipy.optimize import curve_fit import matplotlib.pyplot as plt import numpy as np import pylandau # Create fake data with possion error mpv, eta, sigma, A = 30, 5, 4, 1000 x = np.arange(0, 100, 0.5) y = pylandau.langau(x, mpv, eta, sigma, A) yerr = np.random.normal(np.zeros_like(x), np.sqrt(y)) yerr[y < 1] = 1 y += yerr # Fit with constrains coeff, pcov = curve_fit(pylandau.langau, x, y, sigma=yerr, absolute_sigma=True, p0=(mpv, eta, sigma, A), bounds=(1, 10000)) # Plot plt.errorbar(x, y, np.sqrt(pylandau.langau(x, *coeff)), fmt=".") print coeff plt.plot(x, pylandau.langau(x, *coeff), "-")
def run(self): """Calculates the langau for the specified data""" # Which clusters need to be considered clustersize_list = self.main.kwargs["configs"].get("langau", {}).get( "clustersize", [-1]) if type(clustersize_list) != list: clustersize_list = list(clustersize_list) if clustersize_list[0] == -1: clustersize_list = list( range(1, self.main.kwargs["configs"]["max_cluster_size"] + 1)) # If nothing is specified # Go over all datafiles for data in tqdm(self.data, desc="(langau) Processing file:"): self.results_dict[data] = {} indNumClus = self.get_num_clusters( self.data[data], self.numClusters ) # Here events with only one cluster are choosen indizes = np.concatenate(indNumClus) valid_events_clustersize = np.take( self.data[data]["base"]["Clustersize"], indizes) valid_events_clusters = np.take( self.data[data]["base"]["Clusters"], indizes) valid_events_Signal = np.take( self.data[data]["base"]["Signal"], indizes) # Get the clustersizes of valid events # Get events which show only cluster in its data charge_cal, noise = self.main.calibration.charge_cal, self.main.noise self.results_dict[data]["Clustersize"] = [] # General langau, where all clustersizes are considered if self.main.usejit and self.poolsize > 1: paramslist = [] for size in clustersize_list: cls_ind = np.nonzero(valid_events_clustersize == size)[0] paramslist.append( (cls_ind, valid_events_Signal, valid_events_clusters, self.main.calibration.charge_cal, self.main.noise)) # Here multiple cpu calculate the energy of the events per clustersize results = self.pool.starmap(langau_cluster, paramslist, chunksize=1) self.results_dict[data]["Clustersize"] = results else: for size in tqdm(clustersize_list, desc="(langau) Processing clustersize"): # get the events with the different clustersizes ClusInd = [[], []] for i, event in enumerate(valid_events_clustersize): # cls_ind = np.nonzero(valid_events_clustersize == size)[0] for j, clus in enumerate(event): if clus == size: ClusInd[0].extend([i]) ClusInd[1].extend([j]) signal_clst_event = [] noise_clst_event = [] for i, ind in tqdm(enumerate(ClusInd[0]), desc="(langau) Processing event"): y = ClusInd[1][i] # Signal calculations signal_clst_event.append( np.take(valid_events_Signal[ind], valid_events_clusters[ind][y])) # Noise Calculations noise_clst_event.append( np.take(noise, valid_events_clusters[ind] [y])) # Get the Noise of an event totalE = np.sum(convert_ADC_to_e(signal_clst_event, charge_cal), axis=1) totalNoise = np.sqrt( np.sum(convert_ADC_to_e(noise_clst_event, charge_cal), axis=1) ) # eError is a list containing electron signal noise # incrementor += 1 preresults = {"signal": totalE, "noise": totalNoise} self.results_dict[data]["Clustersize"].append(preresults) # With all the data from every clustersize add all together and fit the langau to it finalE = np.zeros(0) finalNoise = np.zeros(0) for cluster in self.results_dict[data]["Clustersize"]: indi = np.nonzero( cluster["signal"] > 0)[0] # Clean up and extra energy cut nogarbage = cluster["signal"][indi] indi = np.nonzero( nogarbage < self.Ecut)[0] # ultra_high_energy_cut cluster["signal"] = cluster["signal"][indi] finalE = np.append(finalE, cluster["signal"]) finalNoise = np.append(finalNoise, cluster["noise"]) # Fit the langau to it coeff, pcov, hist, error_bins = self.fit_langau(finalE, finalNoise, bins=self.bins) self.results_dict[data]["signal"] = finalE self.results_dict[data]["noise"] = finalNoise self.results_dict[data]["langau_coeff"] = coeff self.results_dict[data]["langau_data"] = [ np.arange(1., 100000., 1000.), pylandau.langau(np.arange(1., 100000., 1000.), *coeff) ] # aka x and y data self.results_dict[data]["data_error"] = error_bins # Consider now only the seedcut hits for the langau, if self.main.kwargs["configs"].get("langau", {}).get("seed_cut_langau", False): seed_cut_channels = self.data[data]["base"]["Channel_hit"] signals = self.data[data]["base"]["Signal"] finalE = [] seedcutADC = [] for i, signal in enumerate( tqdm(signals, desc="(langau SC) Processing events")): if signal[seed_cut_channels[i]].any(): seedcutADC.append(signal[seed_cut_channels[i]]) self.log.info("Converting ADC to electrons...") converted = convert_ADC_to_e(seedcutADC, charge_cal) for conv in converted: finalE.append(sum(conv)) finalE = np.array(finalE, dtype=np.float32) # get rid of 0 events indizes = np.nonzero(finalE > 0)[0] nogarbage = finalE[indizes] indizes = np.nonzero( nogarbage < self.Ecut)[0] # ultra_high_energy_cut coeff, pcov, hist, error_bins = self.fit_langau( nogarbage[indizes], bins=self.bins) # coeff, pcov, hist, error_bins = self.fit_langau(nogarbage, bins=500) self.results_dict[data]["signal_SC"] = nogarbage[indizes] self.results_dict[data]["langau_coeff_SC"] = coeff self.results_dict[data]["langau_data_SC"] = [ np.arange(1., 100000., 1000.), pylandau.langau(np.arange(1., 100000., 1000.), *coeff) ] # aka x and y data return self.results_dict.copy()