def __init__(self, filename, col_altitude=0, col_thickness=2): """ small class that calculates the height of the shower maximum given a parametrisation of the atmosphere and certain parameters of the shower itself Parameters: ----------- filename : string path to text file that contains a table of the atmosphere parameters col_altitude : int column in the text file that contains the altitude/height col_thickness : int column in the text file that contains the thickness """ altitude = [] thickness = [] atm_file = open(filename, "r") for line in atm_file: if line.startswith("#"): continue altitude.append(float(line.split()[col_altitude])) thickness.append(float(line.split()[col_thickness])) self.atmosphere = Histogram(axisNames=["altitude"]) self.atmosphere.hist = thickness * u.g * u.cm ** -2 self.atmosphere.bin_lower_edges = [np.array(altitude) * u.km]
def test_outliers(): """ Check that out-of-range values work as expected """ H = Histogram(nbins=[5, 10], ranges=[[-2.5, 2.5], [-1, 1]]) H.fill(np.array([[1, 1], ])) val1 = H.get_value((100, 100), outlier_value=-10000) val2 = H.get_value((-100, 0), outlier_value=None) assert val1 == -10000 assert val2 == 0
def test_histogram_resample_inplace(): hist = Histogram(nbins=[5, 11], ranges=[[-2.5, 2.5], [-1, 1]]) hist.fill(np.array([[0, 0], [0, 0.5]])) for testpoint in [(0, 0), (0, 1), (1, 0), (3, 3)]: val0 = hist.get_value(testpoint) hist.resample_inplace((10, 22)) hist.resample_inplace((5, 11)) val2 = hist.get_value(testpoint) # at least check the resampling is undoable assert np.isclose(val0[0], val2[0])
def test_histogram_resample_inplace(): hist = Histogram(nbins=[5, 11], ranges=[[-2.5, 2.5], [-1, 1]]) hist.fill(np.array([[0, 0], [0,0.5]])) for testpoint in [(0,0), (0,1), (1,0), (3,3)]: val0 = hist.get_value(testpoint) hist.resample_inplace((10, 22)) val1 = hist.get_value(testpoint) hist.resample_inplace((5, 11)) val2 = hist.get_value(testpoint) # at least check the resampling is undoable assert np.isclose(val0[0], val2[0])
def test_histogram_str(): hist = Histogram(nbins=[5, 10], ranges=[[-2.5, 2.5], [-1, 1]], name="testhisto") expected = ("Histogram(name='testhisto', axes=['axis0', 'axis1'], " "nbins=[ 5 10], ranges=[[-2.5 2.5]\n [-1. 1. ]])") assert str(hist) == expected
def test_histogram_range_fill_and_read(): """ Check that the correct bin is read and written for multiple binnings and fill positions """ num = 100 for nxbins in np.arange(1, 50, 1): for xx in np.arange(-2.0, 2.0, 0.1): pp = (xx + 0.01829384, 0.1) coords = np.ones((num, 2)) * np.array(pp) hist = Histogram(nbins=[nxbins, 10], ranges=[[-2.5, 2.5], [-1, 1]]) hist.fill(coords) val = hist.get_value(pp)[0] assert val == num del hist
def test_histogram_fill_and_read(): hist = Histogram(nbins=[5, 10], ranges=[[-2.5, 2.5], [-1, 1]]) pa = (0.1, 0.1) pb = (-0.55, 0.55) a = np.ones((100, 2)) * pa # point at 0.1,0.1 b = np.ones((10, 2)) * pb # 10 points at -0.5,0.5 hist.fill(a) hist.fill(b) va = hist.get_value(pa)[0] vb = hist.get_value(pb)[0] assert va == 100 assert vb == 10
def test_histogram_fits(histogram_file): """ Write to fits,read back, and check """ hist = Histogram(nbins=[5, 11], ranges=[[-2.5, 2.5], [-1, 1]]) hist.fill(np.array([[0, 0], [0, 0.5]])) hist.to_fits().writeto(histogram_file, overwrite=True) newhist = Histogram.from_fits(histogram_file) # check that the values are the same compare_histograms(hist, newhist)
def test_outliers(): """ Check that out-of-range values work as expected """ H = Histogram(nbins=[5, 10], ranges=[[-2.5, 2.5], [-1, 1]]) H.fill(np.array([ [1, 1], ])) val1 = H.get_value((100, 100), outlier_value=-10000) val2 = H.get_value((-100, 0), outlier_value=None) assert val1 == -10000 assert val2 == 0
def plot_muon_efficiency(outputpath): """ Plot the muon efficiencies """ fig, ax = plt.subplots(1, 1, figsize=(10, 10)) figip, axip = plt.subplots(1, 1, figsize=(10, 10)) figrw, axrw = plt.subplots(1, 1, figsize=(10, 10)) nbins = 16 t = Table.read(str(outputpath) + '_muontable.fits') print('Reading muon efficiency from table', outputpath, t['MuonEff']) if len(t['MuonEff']) < 1: print("No muon events to plot") return else: print("Found", len(t['MuonEff']), "muon events") (mu, sigma) = norm.fit(t['MuonEff']) print('Gaussian fit with mu=', mu, 'sigma=', sigma) conteff = ax.hist(t['MuonEff'], nbins) ax.set_xlim(0.2 * min(t['MuonEff']), 1.2 * max(t['MuonEff'])) xtest = np.linspace(min(t['MuonEff']), max(t['MuonEff']), nbins) yg = mlab.normpdf(xtest, mu, sigma) print('mu', mu, 'sigma', sigma, 'yg', yg) ax.plot(xtest, yg, 'r', linewidth=2) ax.set_ylim(0., 1.2 * max(conteff[0])) ax.set_xlabel('Muon Efficiency') plt.draw() yimp = np.linspace(min(t['ImpactP']), max(t['ImpactP']), nbins) contimp = axip.hist(t['ImpactP'], nbins) axip.set_xlim(0.2 * min(t['ImpactP']), 1.2 * max(t['ImpactP'])) axip.set_ylim(0., 1.2 * max(contimp[0])) axip.set_xlabel('Impact Parameter (m)') plt.draw() heffimp = Histogram(nbins=[16, 16], ranges=[ (min(t['MuonEff']), max(t['MuonEff'])), (min(t['ImpactP']), max(t['ImpactP'])) ]) # ,axisNames=["MuonEfficiency","ImpactParameter"]) heffimp.fill([t['MuonEff'], t['ImpactP']]) heffimp.draw_2d() yrw = np.linspace(min(t['RingWidth']), max(t['RingWidth']), nbins) contrw = axrw.hist(t['RingWidth'], nbins) axrw.set_xlim(0.2 * min(t['RingWidth']), 1.2 * max(t['RingWidth'])) axrw.set_ylim(0., 1.2 * max(contrw[0])) axrw.set_xlabel('Ring Width ($^\circ$)') plt.draw() if outputpath is not None: print("saving figure at", outputpath) fig.savefig(str(outputpath) + '_MuonEff.png') figip.savefig(str(outputpath) + '_ImpactParameter.png') figrw.savefig(str(outputpath) + '_RingWidth.png') else: print("Not saving figure, no outputpath") plt.show()
def plot_muon_efficiency(outputpath): """ Plot the muon efficiencies """ fig, ax = plt.subplots(1, 1, figsize=(10, 10)) figip, axip = plt.subplots(1, 1, figsize=(10, 10)) figrw, axrw = plt.subplots(1, 1, figsize=(10, 10)) nbins = 16 t = Table.read(str(outputpath) + '_muontable.fits') print('Reading muon efficiency from table', outputpath, t['MuonEff']) if len(t['MuonEff']) < 1: print("No muon events to plot") return else: print("Found", len(t['MuonEff']), "muon events") (mu, sigma) = norm.fit(t['MuonEff']) print('Gaussian fit with mu=', mu, 'sigma=', sigma) #ax = figeff.add_subplot(1,3,1) conteff = ax.hist(t['MuonEff'], nbins) ax.set_xlim(0.2 * min(t['MuonEff']), 1.2 * max(t['MuonEff'])) xtest = np.linspace(min(t['MuonEff']), max(t['MuonEff']), nbins) yg = mlab.normpdf(xtest, mu, sigma) print('mu', mu, 'sigma', sigma, 'yg', yg) ax.plot(xtest, yg, 'r', linewidth=2) ax.set_ylim(0., 1.2 * max(conteff[0])) ax.set_xlabel('Muon Efficiency') # plt.figure(fig.number) plt.draw() #axip = figeff.add_subplot(1,3,1) yimp = np.linspace(min(t['ImpactP']), max(t['ImpactP']), nbins) contimp = axip.hist(t['ImpactP'], nbins) axip.set_xlim(0.2 * min(t['ImpactP']), 1.2 * max(t['ImpactP'])) axip.set_ylim(0., 1.2 * max(contimp[0])) axip.set_xlabel('Impact Parameter (m)') # plt.figure(figip.number) plt.draw() heffimp = Histogram(nbins=[16, 16], ranges=[(min(t['MuonEff']), max(t['MuonEff'])), (min(t['ImpactP']), max(t['ImpactP']))]) # ,axisNames=["MuonEfficiency","ImpactParameter"]) # embed() # heffimp.bin_lower_edges([xtest,yimp]) heffimp.fill([t['MuonEff'], t['ImpactP']]) heffimp.draw_2d() #axrw = figeff.add_subplot(1,3,1) yrw = np.linspace(min(t['RingWidth']), max(t['RingWidth']), nbins) contrw = axrw.hist(t['RingWidth'], nbins) axrw.set_xlim(0.2 * min(t['RingWidth']), 1.2 * max(t['RingWidth'])) axrw.set_ylim(0., 1.2 * max(contrw[0])) axrw.set_xlabel('Ring Width ($^\circ$)') # plt.figure(figrw.number) plt.draw() # plt.show() if outputpath is not None: print("saving figure at", outputpath) fig.savefig(str(outputpath) + '_MuonEff.png') figip.savefig(str(outputpath) + '_ImpactParameter.png') figrw.savefig(str(outputpath) + '_RingWidth.png') else: print("Not saving figure, no outputpath") plt.show()
def plot_muon_efficiency(outputpath): """ Plot the muon efficiencies """ fig, ax = plt.subplots(1, 1, figsize=(10, 10)) figip, axip = plt.subplots(1, 1, figsize=(10, 10)) figrw, axrw = plt.subplots(1, 1, figsize=(10, 10)) nbins = 16 t = Table.read(str(outputpath) + '_muontable.fits') logger.info('Reading muon efficiency from table "%s"', outputpath) if len(t['MuonEff']) < 1: logger.warning("No muon events to plot") return else: logger.info("Found %d muon events", len(t['MuonEff'])) (mu, sigma) = norm.fit(t['MuonEff']) logger.debug('Gaussian fit with mu=%f, sigma=%f', mu, sigma) conteff = ax.hist(t['MuonEff'], nbins) ax.set_xlim(0.2 * min(t['MuonEff']), 1.2 * max(t['MuonEff'])) xtest = np.linspace(min(t['MuonEff']), max(t['MuonEff']), nbins) yg = mlab.normpdf(xtest, mu, sigma) logger.debug('mu=%f sigma=%f yg=%f', mu, sigma, yg) ax.plot(xtest, yg, 'r', linewidth=2) ax.set_ylim(0., 1.2 * max(conteff[0])) ax.set_xlabel('Muon Efficiency') plt.draw() contimp = axip.hist(t['ImpactP'], nbins) axip.set_xlim(0.2 * min(t['ImpactP']), 1.2 * max(t['ImpactP'])) axip.set_ylim(0., 1.2 * max(contimp[0])) axip.set_xlabel('Impact Parameter (m)') plt.draw() heffimp = Histogram(nbins=[16, 16], ranges=[(min(t['MuonEff']), max(t['MuonEff'])), (min(t['ImpactP']), max(t[ 'ImpactP']))]) # # ,axisNames=["MuonEfficiency","ImpactParameter"]) heffimp.fill([t['MuonEff'], t['ImpactP']]) heffimp.draw_2d() contrw = axrw.hist(t['RingWidth'], nbins) axrw.set_xlim(0.2 * min(t['RingWidth']), 1.2 * max(t['RingWidth'])) axrw.set_ylim(0., 1.2 * max(contrw[0])) axrw.set_xlabel('Ring Width ($^\circ$)') plt.draw() if outputpath is not None: logger.info("saving figure to '%s'", outputpath) fig.savefig(str(outputpath) + '_MuonEff.png') figip.savefig(str(outputpath) + '_ImpactParameter.png') figrw.savefig(str(outputpath) + '_RingWidth.png') else: logger.info("Not saving figure, no outputpath") plt.show()
class ShowerMaxEstimator: def __init__(self, filename, col_altitude=0, col_thickness=2): """ small class that calculates the height of the shower maximum given a parametrisation of the atmosphere and certain parameters of the shower itself Parameters: ----------- filename : string path to text file that contains a table of the atmosphere parameters col_altitude : int column in the text file that contains the altitude/height col_thickness : int column in the text file that contains the thickness """ altitude = [] thickness = [] atm_file = open(filename, "r") for line in atm_file: if line.startswith("#"): continue altitude.append(float(line.split()[col_altitude])) thickness.append(float(line.split()[col_thickness])) self.atmosphere = Histogram(axisNames=["altitude"]) self.atmosphere.hist = thickness * u.g * u.cm ** -2 self.atmosphere.bin_lower_edges = [np.array(altitude) * u.km] def interpolate(self, arg, outlierValue=0., order=3): axis = self.atmosphere._binLowerEdges[0] bin_u = np.digitize(arg.to(axis.unit), axis) bin_l = bin_u - 1 unit = arg.unit argv = arg.value bin_u_edge = axis[bin_u].to(unit).value bin_l_edge = axis[bin_l].to(unit).value coordinate = (argv - bin_u_edge) / (bin_u_edge - bin_l_edge) \ * (bin_u - bin_l) + bin_u return ndimage.map_coordinates(self.atmosphere.hist, [[coordinate]], order=order, cval=outlierValue)[0]\ * self.atmosphere.hist.unit def find_shower_max_height(self, energy, h_first_int, gamma_alt): """ estimates the height of the shower maximum in the atmosphere according to equation (3) in [arXiv:0907.2610v3] Parameters: ----------- energy : astropy.Quantity energy of the parent gamma photon h_first_int : astropy.Quantity hight of the first interaction gamma_alt : astropy.Quantity or float altitude / pi-minus-zenith (in radians in case of float) of the parent gamma photon Returns: -------- shower_max_height : astropy.Quantity height of the shower maximum """ # offset of the shower-maximum in radiation lengths c = 0.97 * log(energy / (83 * u.MeV)) - 1.32 # radiation length in dry air at 1 atm = 36,62 g / cm**2 [PDG] c *= 36.62 * u.g * u.cm ** -2 # showers with a more horizontal direction spend more path # length in each atm. layer the "effective transverse # thickness" they have to pass is reduced c *= np.sin(gamma_alt) # find the thickness at the height of the first interaction t_first_int = self.interpolate(h_first_int) # total thickness at shower maximum = thickness at first # interaction + thickness traversed to shower maximum t_shower_max = t_first_int + c # now find the height with the wanted thickness for ii, thick1 in enumerate(self.atmosphere.hist): if t_shower_max > thick1: height1 = self.atmosphere.bin_lower_edges[0][ii] height2 = self.atmosphere.bin_lower_edges[0][ii - 1] val = [height2.to( self.atmosphere._binLowerEdges[0].unit).value] thick2 = self.atmosphere.get_value(val)[0] return (height2 - height1) / (thick2 - thick1) \ * (t_shower_max - thick1) + height1