def plot_background(bg_rate, rad_max, ax=None, label=None): # pyirf data reco_bins = np.append(bg_rate["ENERG_LO"], bg_rate["ENERG_HI"][-1]) # first fov bin, [0, 1] deg fov_bin = 0 rate_bin = bg_rate["BKG"].T[:, fov_bin] # interpolate theta cut for given e reco bin e_center_bg = 0.5 * (bg_rate["ENERG_LO"] + bg_rate["ENERG_HI"]) e_center_theta = 0.5 * (rad_max["ENERG_LO"] + rad_max["ENERG_HI"]) theta_cut = np.interp(e_center_bg, e_center_theta, rad_max["RAD_MAX"].T[:, 0]) # undo normalization rate_bin *= cone_solid_angle(theta_cut) rate_bin *= np.diff(reco_bins) ax.errorbar( 0.5 * (bg_rate["ENERG_LO"] + bg_rate["ENERG_HI"]).to_value(u.TeV)[1:-1], rate_bin.to_value(1 / u.s)[1:-1], xerr=np.diff(reco_bins).to_value(u.TeV)[1:-1] / 2, ls="", label=label, ) ax.set_xscale("log") ax.set_yscale("log") ax.set_xlabel("True energy / TeV") ax.set_ylabel("Background rate [1/s]") return 0
def test_background(): from pyirf.irf import background_2d from pyirf.utils import cone_solid_angle np.random.seed(0) N1 = 1000 N2 = 100 N = N1 + N2 # toy event data set with just two energies # and a psf per energy bin, point-like events = QTable( { "reco_energy": np.append(np.full(N1, 1), np.full(N2, 2)) * u.TeV, "source_fov_offset": np.zeros(N) * u.deg, "weight": np.ones(N), } ) energy_bins = [0, 1.5, 3] * u.TeV fov_bins = [0, 1] * u.deg # We return a table with one row as needed for gadf bg = background_2d(events, energy_bins, fov_bins, t_obs=1 * u.s) # 2 energy bins, 1 fov bin, 200 source distance bins assert bg.shape == (2, 1) assert bg.unit == u.Unit("TeV-1 s-1 sr-1") # check that psf is normalized bin_solid_angle = np.diff(cone_solid_angle(fov_bins)) e_width = np.diff(energy_bins) assert np.allclose(np.sum((bg.T * e_width).T * bin_solid_angle, axis=1), [1000, 100] / u.s)
def test_psf(): from pyirf.irf import psf_table from pyirf.utils import cone_solid_angle np.random.seed(0) N = 1000 TRUE_SIGMA_1 = 0.2 TRUE_SIGMA_2 = 0.1 TRUE_SIGMA = np.append(np.full(N, TRUE_SIGMA_1), np.full(N, TRUE_SIGMA_2)) # toy event data set with just two energies # and a psf per energy bin, point-like events = QTable({ "true_energy": np.append(np.full(N, 1), np.full(N, 2)) * u.TeV, "source_fov_offset": np.zeros(2 * N) * u.deg, "theta": np.random.normal(0, TRUE_SIGMA) * u.deg, }) energy_bins = [0, 1.5, 3] * u.TeV fov_bins = [0, 1] * u.deg source_bins = np.linspace(0, 1, 201) * u.deg # We return a table with one row as needed for gadf psf = psf_table(events, energy_bins, source_bins, fov_bins) # 2 energy bins, 1 fov bin, 200 source distance bins assert psf.shape == (2, 1, 200) assert psf.unit == u.Unit("sr-1") # check that psf is normalized bin_solid_angle = np.diff(cone_solid_angle(source_bins)) assert np.allclose(np.sum(psf * bin_solid_angle, axis=2), 1.0) cumulated = np.cumsum(psf * bin_solid_angle, axis=2) # first energy and only fov bin bin_centers = 0.5 * (source_bins[1:] + source_bins[:-1]) assert u.isclose( bin_centers[np.where(cumulated[0, 0, :] >= 0.68)[0][0]], TRUE_SIGMA_1 * u.deg, rtol=0.1, ) # second energy and only fov bin assert u.isclose( bin_centers[np.where(cumulated[1, 0, :] >= 0.68)[0][0]], TRUE_SIGMA_2 * u.deg, rtol=0.1, )
def psf_hdu(): from pyirf.io import create_psf_table_hdu from pyirf.utils import cone_solid_angle psf = np.zeros((len(e_bins) - 1, len(source_bins) - 1, len(fov_bins) - 1)) psf[:, 0, :] = 1 psf = psf / cone_solid_angle(source_bins[1]) hdu = create_psf_table_hdu(psf, e_bins, source_bins, fov_bins, point_like=False) return psf, hdu
def plot_background_rate_from_file(filename, ax=None, **kwargs): ax = plt.gca() if ax is None else ax rad_max = QTable.read(filename, hdu="RAD_MAX")[0] bg_rate = QTable.read(filename, hdu="BACKGROUND")[0] reco_bins = np.append(bg_rate["ENERG_LO"], bg_rate["ENERG_HI"][-1]) # first fov bin, [0, 1] deg fov_bin = 0 rate_bin = bg_rate["BKG"].T[:, fov_bin] # interpolate theta cut for given e reco bin e_center_bg = 0.5 * (bg_rate["ENERG_LO"] + bg_rate["ENERG_HI"]) e_center_theta = 0.5 * (rad_max["ENERG_LO"] + rad_max["ENERG_HI"]) theta_cut = np.interp(e_center_bg, e_center_theta, rad_max["RAD_MAX"].T[:, 0]) # undo normalization rate_bin *= cone_solid_angle(theta_cut) rate_bin *= np.diff(reco_bins) if "ls" not in kwargs and "linestyle" not in kwargs: kwargs["ls"] = "" ax.errorbar( e_center_bg.to_value(u.TeV)[1:-1], rate_bin.to_value(1 / u.s)[1:-1], xerr=np.diff(reco_bins).to_value(u.TeV)[1:-1] / 2, **kwargs, ) # Style settings ax.set_xscale("log") ax.set_xlabel(r"$E_\mathrm{Reco} [\mathrm{TeV}]$") ax.set_ylabel("Background rate [Hz]") ax.grid(True, which="both") ax.legend() ax.set_yscale("log") return ax