def incorporate_recon(event_edges, cascade_edges, nuflux, angle_edges): """ This takes in the results from `generate_singly_diff_fluxes` and incorporates reconstruction uncertainties Should take in a list or array of energies (true, deposited), in units of eV And also take in a list of true cos(zenith) edges """ e_min = min(cascade_edges) e_max = max(cascade_edges) z_min = min(angle_edges) z_max = max(angle_edges) # we need to get all the centers for these bins with the given edges. # these will be associeed with each of the bins in nuflux cascade_centers = bhist([cascade_edges]).centers true_e_centers = bhist([event_edges]).centers true_ang_centers = bhist([angle_edges]).centers # these are reconstruction objects r_energy = bhist([ np.logspace(np.log10(e_min), np.log10(e_max), int(len(cascade_edges)/2)) ]) r_angle = bhist([ np.linspace( z_min, z_max, int(len(angle_edges)/2))]) print("Reconstruction Parameters") print(" Energy: {} to {} GeV".format(sci(e_min), sci(e_max))) print(" cos(t): {} to {} ".format(z_min, z_max)) r_energy_centers = r_energy.centers r_energy_widths = r_energy.widths r_angle_centers = r_angle.centers r_angle_widths = r_angle.widths #build the data object # this thing take in those edges and centers and correctly builds normalized probabilities for the given bins dataobj = DataReco(r_energy.edges, r_angle.edges, cascade_edges, angle_edges) # may god have mercy on our souls recoflux = {} for key in nuflux.keys(): print("Reconstructing {} Flux".format(key)) # energy x, angle y recoflux[key] = np.zeros(shape=(len(r_energy_centers),len(true_e_centers), len(r_angle_centers),len(true_ang_centers))) for i_e_reco in range(len(r_energy_centers)): for i_e_depo in range(len(cascade_centers)): depo_odds = dataobj.get_energy_reco_odds(i_e_depo, i_e_reco) #per if depo_odds<=0.: continue for i_a_true in range(len(true_ang_centers)): for i_a_reco in range(len(r_angle_centers)): ang_odds = dataobj.get_czenith_reco_odds(i_a_true, i_a_reco,0) #per sr if ang_odds<=0.: continue for i_e_true in range(len(true_e_centers)): amt = nuflux[key][i_e_depo][i_e_true][i_a_true]# *depo_odds*ang_odds #per angle per gev depo if amt>=0: recoflux[key][i_e_reco][i_e_true][i_a_reco][i_a_true] += amt _save_data(r_energy.edges, event_edges, r_angle.edges, angle_edges, recoflux)
def build_contours(obs_e, obs_angle): """ Presuming we're given some observed angle and observed energy... We first figure out which event bin we want """ obs_energy = obs_e * (1e9) # these are bin edges e_reco, e_true, a_reco, a_true, probs = load_file() # let's get the centers we want e_true_centers = bhist([e_true]).centers a_true_centers = bhist([a_true]).centers e_reco_centers = bhist([e_reco]).centers a_reco_centers = bhist([a_reco]).centers e_left, e_right = get_loc(obs_energy, e_reco_centers) a_left, a_right = get_loc(obs_angle, a_reco_centers) # so this is a bit wild. The reco-space entries are coarse. So, we grab neighboring 2D reconstruction arrays and do a bilinear interpolation of 2D arrays around our given point in reco-space key = list(probs.keys())[6] p0 = (obs_energy, obs_angle) p1 = (e_reco_centers[e_left], a_reco_centers[a_left]) p2 = (e_reco_centers[e_right], a_reco_centers[a_right]) print(np.shape(probs[key])) #q11=probs[key][e_left,:,a_left,:] #q21=probs[key][e_right,:,a_left,:] #q12=probs[key][e_left,:,a_right,:] #q22=probs[key][e_right,:,a_right,:] q11 = slicer(probs[key], e_left, a_left) q21 = slicer(probs[key], e_right, a_left) q12 = slicer(probs[key], e_left, a_right) q22 = slicer(probs[key], e_right, a_right) focus_prob = bilinear_interp(p0, p1, p2, q11, q12, q21, q22) del probs # clear this out of memory. It's really big... # pcolormesh focus_prob = np.ma.masked_where(focus_prob <= 0, focus_prob) plt.pcolormesh( np.array(e_true_centers) / (1e9), a_true_centers, np.transpose(focus_prob)) plt.xlabel("True Energy [GeV]", size=14) plt.xscale('log') plt.ylabel("Cos Zenith", size=14) plt.title(r"Expected Truth for E={} GeV, $\cos\theta$={}".format( sci(obs_e), obs_angle)) plt.savefig("contour.png", dpi=400) plt.show()
def __init__(self, reco_energy_edges, reco_czenith_edges, depo_energy_edges, true_czenith_edges): """ Expects the energies in eV, but works in GeV """ self._ereco = bhist([np.array(reco_energy_edges) * (1e-9)]) self._edepo = bhist([np.array(depo_energy_edges) * (1e-9)]) self._zreco = bhist([reco_czenith_edges ]) # these two are in cos(Zenith) self._ztrue = bhist([true_czenith_edges]) # these are now filled with the values of the probability DENSITY functions for each angle/energy combo # TODO right now there is no assumed covariance ... this should be improved self._energy_odds_array = np.array([[ get_odds_energy(deposited, reconstructed) for reconstructed in self.reco_energy_centers ] for deposited in self.depo_energy_centers]) self._angle_odds_array = np.array([[ get_odds_angle(true, reconstructed) for reconstructed in self.reco_czenith_centers ] for true in self.true_czenith_centers]) # Avoid equating two floats. Look for a sufficiently small difference! max_diff = 1e-12 # normalize these things! # so for each energy deposited... the sum of (PDF*width evaluated at each reco bin) should add up to 1. for depo in range(len(self._energy_odds_array)): self._energy_odds_array[depo] *= 1. / sum( self._energy_odds_array[depo] * self.reco_energy_widths) assert (abs(1 - sum(self._energy_odds_array[depo] * self.reco_energy_widths)) <= max_diff) for depo in range(len(self._angle_odds_array)): self._angle_odds_array[depo] *= 1. / sum( self._angle_odds_array[depo] * self.reco_czenith_widths) assert (abs(1 - sum(self._angle_odds_array[depo] * self.reco_czenith_widths)) <= max_diff)
def _load_flux(filename): f = open(filename, 'rb') all_data = pickle.load(f) f.close() e_reco = all_data["e_reco"] a_reco = all_data["a_reco"] flux = all_data["flux"] return (e_reco, a_reco, flux) if True: e_reco, a_reco, flux = _load_flux(filename) energies = np.array(bhist([e_reco]).centers) czeniths = np.array(bhist([a_reco]).centers) from_muon, from_not = sep_by_flavor(flux) cf = plt.pcolormesh(czeniths, energies / (1e9), np.log10(from_not), cmap=cm.coolwarm) #, locator=ticker.LogLocator()) plt.yscale('log') plt.ylabel("Event Energy [GeV]", size=14) plt.xlabel(r"Reconstructed $\cos\theta$", size=14) plt.title("Not Muons", size=14) cbar = plt.colorbar() #cf,ticks=ticker.LogLocator()) cbar.set_label(r"$\log\Phi$ [GeV sr s cm$^{2}$]$^{-1}$") plt.savefig("reco_flux_not_muon.png", dpi=400)
def do_for_key(event_edges, cascade_edges, key, angles=None): """ This function takes the desired bin edges for the event energies and deposited energies along with the dictionary key corresponding to a specific combination of falvor, current, and neutrino type. It builds up the 2D flux array (singly differential), which it then returns """ evt = bhist([event_edges]) cas = bhist([cascade_edges]) reco = bhist([cascade_edges]) event_energies = evt.centers event_widths = evt.widths cascade_energies = cas.centers cascade_widths = cas.widths reco_energies = reco.centers reco_widths = reco.widths flav = key.split("_")[0] curr = key.split("_")[2] if angles is None: ang_list = [None] else: ang_list = angles if angles is None: flux = bhist((cascade_edges, event_edges)) else: flux = bhist((cascade_edges, event_edges, angles)) for angle in ang_list: # need special Tau treatment if curr == "CC": # deposit all the energy. Hadronic and Leptonic (event) contribute # Since the energy always all gets deposited, we only need to do one loop! # So, for a given "deposited energy" (cascade_energy), we already know the total energy. # Therefore we just get the total cross section * flux there... the units end up as [s GeV in sr]^-1 for cas_bin in range(len(cascade_energies)): deposited_energy = cascade_energies[ cas_bin] # energy going to the leptonic component! if flav.lower() == 'tau': # Etau is cascade_energies[cas_bin] # How much energy is visible in the various tau decays though? # going from zero->deposited energy deposited_energy = 0.5 * ( tauData(deposited_energy / const.GeV, 1) + tauData(deposited_energy / const.GeV, -1)) amount = data.get_flux(deposited_energy, key, angle=angle) amount *= get_diff_xs(deposited_energy, get_flavor(key), get_neut(key), get_curr(key)) try: if angle is None: flux.register(amount, cascade_energies[cas_bin], deposited_energy) # add it in! else: flux.register(amount, cascade_energies[cas_bin], deposited_energy, angle) except ValueError: if flav.lower() != 'tau': raise Exception("It wasn't tau. Something's wrong") else: # in this case, knowing the cascade doesn't tell us anything about the event energy. # so we loop over both, get the flux*differential_xs at each bin combination, and multiply by the widths of deposited-energy-bin to get the same units as in the CC case for evt_bin in range(len(event_energies)): for cas_bin in range(len(cascade_energies)): lepton_energy = event_energies[evt_bin] - cascade_energies[ cas_bin] # we'll have nowhere to put these, so let's just skip this if lepton_energy < min(cascade_energies): continue if lepton_energy > max(cascade_energies): continue amount = data.get_flux(event_energies[evt_bin], key, angle=angle) amount *= get_diff_xs(event_energies[evt_bin], get_flavor(key), get_neut(key), get_curr(key), lepton_energy, 0.0) * cascade_widths[cas_bin] if angle is None: flux.register(amount, cascade_energies[cas_bin], event_energies[evt_bin]) else: flux.register(amount, cascade_energies[cas_bin], event_energies[evt_bin], angle) # build a new bhist in reconstruction space (Still with event energy too) # then scan through deposited-true angle space # and use the PDFS to smear the true values into reconstructed values, depositing them into the reconstruction bhist return (flux.fill)
plt.legend() print("saving 'wow.png'") plt.savefig("wow_{:.2f}.png".format(glob_angle),dpi=400) savefile = ".analysis_level.dat" if mode==8 or do_all: if load_stored and os.path.exists(savefile): event, cascade, nuflux, angle_edges = _load_data(glob_angle) else: event, cascade, nuflux, angle_edges = generate_singly_diff_fluxes(n_bins) from_muon, from_not = sep_by_flavor(nuflux) event_energies = np.array(bhist([event]).centers) cascade_energies = np.array(bhist([cascade]).centers) from_muon = np.ma.masked_where(from_muon<=0, from_muon) from_not = np.ma.masked_where(from_not<=0, from_not) plt.figure() levels = np.logspace(-50,-33,10) print("Max of muon: {}".format(np.max(from_muon))) cf = plt.contourf(event_energies/const.GeV, cascade_energies/const.GeV, from_muon,cmap=cm.coolwarm, locator=ticker.LogLocator(), levels=levels) plt.xscale('log') plt.yscale('log') plt.xlabel('Parent Energy [GeV]', size=14) plt.ylabel('Cascade Energy [GeV]', size=14) plt.grid(which='major',alpha=0.7) cbar = plt.colorbar(cf,ticks=ticker.LogLocator())