예제 #1
0
def calculate_sensitivities(events, energy_bin_edges, alpha, n_draws=1):
    SensCalc = SensitivityPointSource(
        reco_energies={'g': events['g']['MC_Energy'].values * u.TeV,
                       'p': events['p']['MC_Energy'].values * u.TeV,
                       'e': events['e']['MC_Energy'].values * u.TeV},
        mc_energies={'g': events['g']['MC_Energy'].values * u.TeV,
                     'p': events['p']['MC_Energy'].values * u.TeV,
                     'e': events['e']['MC_Energy'].values * u.TeV},
        flux_unit=flux_unit)

    SensCalc.generate_event_weights(
        n_simulated_events={'g': meta_gammas["n_simulated"],
                            'p': meta_proton["n_simulated"],
                            'e': meta_electr["n_simulated"]},
        generator_areas={'g': np.pi * (meta_gammas["gen_radius"] * u.m)**2,
                         'p': np.pi * (meta_proton["gen_radius"] * u.m)**2,
                         'e': np.pi * (meta_electr["gen_radius"] * u.m)**2},
        observation_time=observation_time,
        spectra={'g': crab_source_rate,
                 'p': cr_background_rate,
                 'e': electron_spectrum},
        e_min_max={'g': (meta_gammas["e_min"], meta_gammas["e_max"]) * u.TeV,
                   'p': (meta_proton["e_min"], meta_proton["e_max"]) * u.TeV,
                   'e': (meta_electr["e_min"], meta_electr["e_max"]) * u.TeV},
        extensions={'p': meta_proton["diff_cone"] * u.deg,
                    'e': meta_electr["diff_cone"] * u.deg},
        generator_gamma={'g': meta_gammas["gen_gamma"],
                         'p': meta_proton["gen_gamma"],
                         'e': meta_electr["gen_gamma"]})

    SensCalc.get_sensitivity(
        alpha=alpha, n_draws=n_draws, max_background_ratio=.05,
        sensitivity_energy_bin_edges=sensitivity_energy_bin_edges)

    return SensCalc
예제 #2
0
def test_SensitivityPointSource_sensitivity_MC():

    np.random.seed(314159)

    alpha = 1.
    n_sig = 20
    n_bgr = 10
    # one bin with the bin-centre at 1 TeV
    energy_bin_edges = np.array([.1, 10]) * u.TeV
    energies_sig = np.ones(n_sig)
    energies_bgr = np.ones(n_bgr)

    sens = SensitivityPointSource(reco_energies={
        's': energies_sig * u.TeV,
        'b': energies_bgr * u.TeV
    })

    # sens.event_weights = {'s': energies_sig, 'b': energies_bgr}
    sensitivity = sens.get_sensitivity(
        alpha=alpha,
        signal_list=("s"),
        mode="MC",
        sensitivity_source_flux=crab_source_rate,
        sensitivity_energy_bin_edges=energy_bin_edges,
        min_n=0,
        max_background_ratio=1)

    # the ratio between sensitivity and reference flux (i.e. from the crab nebula) should
    # be the scaling factor that needs to be applied to n_sig to produce a 5 sigma result
    # in the lima formula
    ratio = sensitivity["Sensitivity"][0] / (crab_source_rate(1 * u.TeV)).value
    np.testing.assert_allclose(
        [sigma_lima(ratio * n_sig + alpha * n_bgr, n_bgr, alpha)], [5],
        atol=0.0066)
예제 #3
0
def test_SensitivityPointSource_sensitivity_MC():

    np.random.seed(314159)

    alpha = 1.
    n_sig = 20
    n_bgr = 10
    # one bin with the bin-centre at 1 TeV
    energy_bin_edges = np.array([.1, 10]) * u.TeV
    energies_sig = np.ones(n_sig)
    energies_bgr = np.ones(n_bgr)

    sens = SensitivityPointSource(reco_energies={'s': energies_sig * u.TeV,
                                                 'b': energies_bgr * u.TeV})

    # sens.event_weights = {'s': energies_sig, 'b': energies_bgr}
    sensitivity = sens.get_sensitivity(alpha=alpha, signal_list=("s"), mode="MC",
                                       sensitivity_source_flux=crab_source_rate,
                                       sensitivity_energy_bin_edges=energy_bin_edges,
                                       min_n=0, max_background_ratio=1)

    # the ratio between sensitivity and reference flux (i.e. from the crab nebula) should
    # be the scaling factor that needs to be applied to n_sig to produce a 5 sigma result
    # in the lima formula
    ratio = sensitivity["Sensitivity"][0] / (crab_source_rate(1 * u.TeV)).value
    np.testing.assert_allclose(
        [sigma_lima(ratio * n_sig + alpha * n_bgr, n_bgr, alpha)], [5], atol=0.0066)
예제 #4
0
def test_SensitivityPointSource_sensitivity_data():

    alpha = 1.
    n_on = 30
    n_off = 10
    # one bin with the bin-centre at 1 TeV
    energy_bin_edges = np.array([.1, 10]) * u.TeV
    energies_sig = np.ones(n_on)
    energies_bgr = np.ones(n_off)

    sens = SensitivityPointSource(reco_energies={
        's': energies_sig * u.TeV,
        'b': energies_bgr * u.TeV
    })

    sensitivity = sens.get_sensitivity(
        alpha=alpha,
        signal_list=("s"),
        mode="data",
        sensitivity_source_flux=crab_source_rate,
        sensitivity_energy_bin_edges=energy_bin_edges,
        min_n=0,
        max_background_ratio=1)

    # the ratio between sensitivity and reference flux (i.e. from the crab nebula) should
    # be the scaling factor that needs to be applied to n_sig=n_on-n_off*alpha to produce
    # a 5 sigma result  in the lima formula
    ratio = sensitivity["Sensitivity"][0] / (crab_source_rate(1 * u.TeV)).value
    np.testing.assert_allclose([
        sigma_lima(ratio *
                   (n_on - n_off * alpha) + n_off * alpha, n_off, alpha)
    ], [5])
예제 #5
0
def test_SensitivityPointSource_effective_area():

    # areas used in the event generator
    gen_area_g = np.pi * (1000 * u.m)**2
    gen_area_p = np.pi * (2000 * u.m)**2

    # energy list of "selected" events
    energy_sel_gammas = np.logspace(2, 6, 200, False) * u.TeV
    energy_sel_electr = np.logspace(2, 6, 200, False) * u.TeV
    energy_sel_proton = np.logspace(2, 6, 400, False) * u.TeV

    # energy list of "generated" events
    energy_sim_gammas = np.logspace(2, 6, 400, False) * u.TeV
    energy_sim_electr = np.logspace(2, 6, 400, False) * u.TeV
    energy_sim_proton = np.logspace(2, 6, 800, False) * u.TeV

    # binning for the energy histograms
    energy_edges = np.logspace(2, 6, 41) * u.TeV

    # energy histogram for the generated events
    energy_sim_hist_gamma = np.histogram(energy_sim_gammas,
                                         bins=energy_edges)[0]
    energy_sim_hist_elect = np.histogram(energy_sim_electr,
                                         bins=energy_edges)[0]
    energy_sim_hist_proton = np.histogram(energy_sim_proton,
                                          bins=energy_edges)[0]

    # constructer gets fed with energy lists and desired energy binning
    sens = SensitivityPointSource(mc_energies={
        'g': energy_sel_gammas,
        'p': energy_sel_proton,
        'e': energy_sel_electr
    },
                                  energy_bin_edges={
                                      'g': energy_edges,
                                      'p': energy_edges,
                                      'e': energy_edges
                                  })

    # effective areas
    sens.get_effective_areas(generator_energy_hists={
        'g': energy_sim_hist_gamma,
        'p': energy_sim_hist_proton,
        'e': energy_sim_hist_elect
    },
                             generator_areas={
                                 'g': gen_area_g,
                                 'p': gen_area_p,
                                 'e': gen_area_g
                             })

    # midway result are the effective areas
    eff_a = sens.effective_areas
    eff_area_g, eff_area_p, eff_area_e = eff_a['g'], eff_a['p'], eff_a['e']

    # "selected" events are half of the "generated" events
    # so effective areas should be half of generator areas, too
    np.testing.assert_allclose(eff_area_g.value, gen_area_g.value / 2)
    np.testing.assert_allclose(eff_area_p.value, gen_area_p.value / 2)
예제 #6
0
def test_SensitivityPointSource_effective_area():

    # areas used in the event generator
    gen_area_g = np.pi * (1000 * u.m)**2
    gen_area_p = np.pi * (2000 * u.m)**2

    # energy list of "selected" events
    energy_sel_gammas = np.logspace(2, 6, 200, False) * u.TeV
    energy_sel_electr = np.logspace(2, 6, 200, False) * u.TeV
    energy_sel_proton = np.logspace(2, 6, 400, False) * u.TeV

    # energy list of "generated" events
    energy_sim_gammas = np.logspace(2, 6, 400, False) * u.TeV
    energy_sim_electr = np.logspace(2, 6, 400, False) * u.TeV
    energy_sim_proton = np.logspace(2, 6, 800, False) * u.TeV

    # binning for the energy histograms
    energy_edges = np.logspace(2, 6, 41) * u.TeV

    # energy histogram for the generated events
    energy_sim_hist_gamma = np.histogram(energy_sim_gammas,
                                         bins=energy_edges)[0]
    energy_sim_hist_elect = np.histogram(energy_sim_electr,
                                         bins=energy_edges)[0]
    energy_sim_hist_proton = np.histogram(energy_sim_proton,
                                          bins=energy_edges)[0]

    # constructer gets fed with energy lists and desired energy binning
    sens = SensitivityPointSource(
        mc_energies={'g': energy_sel_gammas,
                     'p': energy_sel_proton,
                     'e': energy_sel_electr},
        energy_bin_edges={'g': energy_edges,
                          'p': energy_edges,
                          'e': energy_edges})

    # effective areas
    sens.get_effective_areas(
        generator_energy_hists={'g': energy_sim_hist_gamma,
                                'p': energy_sim_hist_proton,
                                'e': energy_sim_hist_elect},
        generator_areas={'g': gen_area_g,
                         'p': gen_area_p,
                         'e': gen_area_g})

    # midway result are the effective areas
    eff_a = sens.effective_areas
    eff_area_g, eff_area_p, eff_area_e = eff_a['g'], eff_a['p'], eff_a['e']

    # "selected" events are half of the "generated" events
    # so effective areas should be half of generator areas, too
    np.testing.assert_allclose(eff_area_g.value, gen_area_g.value / 2)
    np.testing.assert_allclose(eff_area_p.value, gen_area_p.value / 2)
예제 #7
0
def test_draw_events_from_flux_histogram():

    np.random.seed(2)

    e_min = 20
    e_max = 50
    n_bins = 30
    n_draws = 1000

    energy_edges = np.linspace(e_min, e_max, n_bins + 1, True) * u.TeV
    energy = np.random.uniform(e_min, e_max, 50000) * u.TeV

    target_distribution = make_mock_event_rate(lambda e: (e / u.TeV)**-3,
                                               energy_edges,
                                               norm=n_draws,
                                               log_e=False)

    indices = SensitivityPointSource.draw_events_from_flux_histogram(
        {'g': energy}, {'g': target_distribution}, {'g': energy_edges})

    hist, _ = np.histogram(energy[indices['g']], bins=energy_edges[::])

    # checking the χ² between `target_distribution` and the drawn one
    chisquare = scipy.stats.chisquare(target_distribution, hist)[0]
    # the test that the reduced χ² is close to 1 (tollorance of 1)
    np.testing.assert_allclose([chisquare / n_bins], [1], atol=0.1)
예제 #8
0
def test_draw_events_from_flux_histogram():

    np.random.seed(2)

    e_min = 20
    e_max = 50
    n_bins = 30
    n_draws = 1000

    energy_edges = np.linspace(e_min, e_max, n_bins + 1, True) * u.TeV
    energy = np.random.uniform(e_min, e_max, 50000) * u.TeV

    target_distribution = make_mock_event_rate(lambda e: (e / u.TeV)**-3,
                                               energy_edges,
                                               norm=n_draws, log_e=False)

    indices = SensitivityPointSource.draw_events_from_flux_histogram(
        {'g': energy},
        {'g': target_distribution},
        {'g': energy_edges})

    hist, _ = np.histogram(energy[indices['g']], bins=energy_edges[::])

    # checking the χ² between `target_distribution` and the drawn one
    chisquare = scipy.stats.chisquare(target_distribution, hist)[0]
    # the test that the reduced χ² is close to 1 (tollorance of 1)
    np.testing.assert_allclose([chisquare / n_bins], [1], atol=0.1)
예제 #9
0
def test_draw_events_with_flux_weight():

    np.random.seed(1)

    e_min = 20
    e_max = 50
    n_draws = 10000

    energy = np.random.uniform(e_min, e_max, 50000)

    gamma_old = 0  # since events are drawn from np.random.uniform
    gamma_new = 3

    # this is a simple approach on how to determine event weights (i.e. probabilities to
    # get picked in the  random draw) to generate a set of events with a different energy
    # spectrum
    #
    # Parameters
    # ----------
    # energy : numpy array
    #     the energies of the MC events
    # gamma_new : float
    #     the spectral index the drawn set of events is supposed to follow
    # gamma_old : float
    #     the spectral index the MC events have been generated with
    #     this is zero here but usually 2 in MC generators
    #
    # Returns
    # -------
    # weights : numpy array
    #     list of event weights normalised to 1
    #     to be used as PDF in `np.random.choice`
    energy_weights = energy**(gamma_old - gamma_new)
    energy_weights /= np.sum(energy_weights)

    indices = SensitivityPointSource.draw_events_from_flux_weight(
        {'g': energy},
        {'g': energy_weights},
        {'g': n_draws})

    assert len(energy[indices['g']]) == n_draws
예제 #10
0
def test_draw_events_with_flux_weight():

    np.random.seed(1)

    e_min = 20
    e_max = 50
    n_draws = 10000

    energy = np.random.uniform(e_min, e_max, 50000)

    gamma_old = 0  # since events are drawn from np.random.uniform
    gamma_new = 3

    # this is a simple approach on how to determine event weights (i.e. probabilities to
    # get picked in the  random draw) to generate a set of events with a different energy
    # spectrum
    #
    # Parameters
    # ----------
    # energy : numpy array
    #     the energies of the MC events
    # gamma_new : float
    #     the spectral index the drawn set of events is supposed to follow
    # gamma_old : float
    #     the spectral index the MC events have been generated with
    #     this is zero here but usually 2 in MC generators
    #
    # Returns
    # -------
    # weights : numpy array
    #     list of event weights normalised to 1
    #     to be used as PDF in `np.random.choice`
    energy_weights = energy**(gamma_old - gamma_new)
    energy_weights /= np.sum(energy_weights)

    indices = SensitivityPointSource.draw_events_from_flux_weight(
        {'g': energy}, {'g': energy_weights}, {'g': n_draws})

    assert len(energy[indices['g']]) == n_draws
예제 #11
0
def calculate_sensitivities(events, energy_bin_edges, alpha):
    SensCalc = SensitivityPointSource(
            reco_energies={'g': events['g']['MC_Energy'].values * u.TeV,
                           'p': events['p']['MC_Energy'].values * u.TeV,
                           'e': events['e']['MC_Energy'].values * u.TeV},
            mc_energies={'g': events['g']['MC_Energy'].values * u.TeV,
                         'p': events['p']['MC_Energy'].values * u.TeV,
                         'e': events['e']['MC_Energy'].values * u.TeV},
            flux_unit=flux_unit)

    SensCalc.generate_event_weights(
            n_simulated_events={'g': meta_gammas["n_simulated"],
                                'p': meta_proton["n_simulated"],
                                'e': meta_electr["n_simulated"]},
            generator_areas={'g': np.pi * (meta_gammas["gen_radius"] * u.m)**2,
                             'p': np.pi * (meta_proton["gen_radius"] * u.m)**2,
                             'e': np.pi * (meta_electr["gen_radius"] * u.m)**2},
            observation_time=observation_time,
            spectra={'g': crab_source_rate,
                     'p': cr_background_rate,
                     'e': electron_spectrum},
            e_min_max={'g': (meta_gammas["e_min"], meta_gammas["e_max"]) * u.TeV,
                       'p': (meta_proton["e_min"], meta_proton["e_max"]) * u.TeV,
                       'e': (meta_electr["e_min"], meta_electr["e_max"]) * u.TeV},
            extensions={'p': meta_proton["diff_cone"] * u.deg,
                        'e': meta_electr["diff_cone"] * u.deg},
            generator_gamma={'g': meta_gammas["gen_gamma"],
                             'p': meta_proton["gen_gamma"],
                             'e': meta_electr["gen_gamma"]})

    SensCalc.get_sensitivity(
            alpha=alpha,
            n_draws=1, max_background_ratio=.05,
            sensitivity_energy_bin_edges=sensitivity_energy_bin_edges)

    return SensCalc
예제 #12
0
def make_performance_plots(events_w, events_t, which=None):

    if not which or "theta_square" in which:
        fig = plt.figure()
        for channels in [events_w]:
            for events in channels:
                plt.hist(events["off_angle"]**2, weights=events["event_weights"],
                         bins=np.linspace(0, 3, 10))
            plt.xlabel("off_angle**2 / degree**2")
            plt.show()

    if (not which or "multiplicity" in which) and False:
        fig = plt.figure()
        plt.hist(events_w['g']["NTels_reco"], alpha=.5,
                 bins=np.arange(0, 51, 2))
        plt.hist(events_t['g']["NTels_reco"], alpha=.5,
                 bins=np.arange(0, 51, 2))
        if args.write:
            save_fig(args.plots_dir + "multiplicity")
        plt.pause(.1)

    if not which or "multiplicity_by_size" in which:
        fig, axs = plt.subplots(1, 3, figsize=(10, 5))
        plt.sca(axs[0])
        plt.hist(events_w['g']["NTels_reco_lst"], alpha=.5,
                 bins=np.arange(0, 5, 1))
        plt.hist(events_t['g']["NTels_reco_lst"], alpha=.5,
                 bins=np.arange(0, 5, 1))
        plt.suptitle("LST")

        plt.sca(axs[1])
        plt.hist(events_w['g']["NTels_reco_mst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.hist(events_t['g']["NTels_reco_mst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.suptitle("MST")

        plt.sca(axs[2])
        plt.hist(events_w['g']["NTels_reco_sst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.hist(events_t['g']["NTels_reco_sst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.suptitle("SST")

        if args.write:
            save_fig(args.plots_dir + "multiplicity_by_size")
        plt.pause(.1)

    if (not which or "ang_res_verbose" in which) and False:
        fig, axes = plt.subplots(1, 2)
        n_tel_max = 50  # np.max(gammas_w["NTels_reco"])
        # plt.subplots_adjust(left=0.11, right=0.97, hspace=0.39, wspace=0.29)
        plot_hex_and_violin(events_w['g']["NTels_reco"],
                            np.log10(events_w['g']["off_angle"]),
                            np.arange(0, n_tel_max + 1, 5),
                            xlabel=r"$N_\mathrm{Tels}$",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            do_hex=False, axis=axes[0],
                            extent=[0, n_tel_max, -3, 0])
        plot_hex_and_violin(np.log10(events_w['g']["reco_Energy"]),
                            np.log10(events_w['g']["off_angle"]),
                            np.linspace(-1, 3, 17),
                            xlabel=r"$\log_{10}(E_\mathrm{reco}$ / TeV)",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            v_padding=0.015, axis=axes[1], extent=[-.5, 2.5, -3, 0])
        plt.suptitle("wavelet")

        if args.write:
            save_fig(args.plots_dir + "ang_res_verbose_wave")
        plt.pause(.1)

        fig, axes = plt.subplots(1, 2)
        n_tel_max = 50  # np.max(gammas_w["NTels_reco"])
        # plt.subplots_adjust(left=0.11, right=0.97, hspace=0.39, wspace=0.29)
        plot_hex_and_violin(events_t['g']["NTels_reco"],
                            np.log10(events_t['g']["off_angle"]),
                            np.arange(0, n_tel_max + 1, 5),
                            xlabel=r"$N_\mathrm{Tels}$",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            do_hex=False, axis=axes[0],
                            extent=[0, n_tel_max, -3, 0])
        plot_hex_and_violin(np.log10(events_t['g']["reco_Energy"]),
                            np.log10(events_t['g']["off_angle"]),
                            np.linspace(-1, 3, 17),
                            xlabel=r"$\log_{10}(E_\mathrm{reco}$ / TeV)",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            v_padding=0.015, axis=axes[1], extent=[-.5, 2.5, -3, 0])
        plt.suptitle("tailcuts")

        if args.write:
            save_fig(args.plots_dir + "ang_res_verbose_tail")
        plt.pause(.1)

    # angular resolutions

    if not which or ("ang_res" in which or "xi" in which):
        plt.figure()
        for key in events_w:
            xi_68_w = percentiles(events_w[key]["off_angle"],
                                  events_w[key]["reco_Energy"],
                                  e_bin_edges.value, 68)
            xi_68_t = percentiles(events_t[key]["off_angle"],
                                  events_t[key]["reco_Energy"],
                                  e_bin_edges.value, 68)

            plt.plot(e_bin_centres.value, xi_68_t,
                     color="darkorange",
                     marker=channel_marker_map[key],
                     ls=channel_linestyle_map[key],
                     label="--".join([channel_map[key], "tail"]))
            plt.plot(e_bin_centres.value, xi_68_w,
                     color="darkred",
                     marker=channel_marker_map[key],
                     ls=channel_linestyle_map[key],
                     label="--".join([channel_map[key], "wave"]))
        plt.title("angular resolution")
        plt.xlabel(r"$E_\mathrm{reco}$ / TeV")
        plt.ylabel(r"$\xi_\mathrm{68} / ^\circ$")
        plt.gca().set_xscale("log")
        plt.gca().set_yscale("log")
        plt.grid()
        plt.legend()

        if args.write:
            save_fig(args.plots_dir + "xi")
        plt.pause(.1)

    if not which or "gammaness" in which:
        # gammaness plots
        show_gammaness(events_w, "wavelets")
        show_gammaness(events_t, "tailcuts")

    if not which or "energy_migration" in which:
        # MC Energy vs. reco Energy 2D histograms
        for events, mode in zip([events_w, events_t],
                                ["wavelets", "tailcuts"]):
            fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
            for i, key in enumerate(events):
                ax = axs[i]
                plt.sca(ax)
                counts, _, _ = np.histogram2d(events[key]["reco_Energy"],
                                              events[key]["MC_Energy"],
                                              bins=(e_bin_fine_edges, e_bin_fine_edges))
                ax.pcolormesh(e_bin_fine_edges.value, e_bin_fine_edges.value, counts.T)
                plt.plot(e_bin_fine_edges.value[[0, -1]], e_bin_fine_edges.value[[0, -1]],
                         color="darkgreen")
                plt.title(channel_map[key])
                ax.set_xlabel(r"$E_\mathrm{reco}$ / TeV")
                if i == 0:
                    ax.set_ylabel(r"$E_\mathrm{MC}$ / TeV")
                ax.set_xscale("log")
                ax.set_yscale("log")
                plt.grid()

            plt.suptitle(mode)
            plt.subplots_adjust(left=.1, wspace=.1)

            if args.write:
                save_fig(args.plots_dir + "_".join(["energy_migration", mode]))
            plt.pause(.1)

    if not which or "DeltaE" in which:
        # (reco Energy - MC Energy) 2D histograms
        for events, mode in zip([events_w, events_t],
                                ["wavelets", "tailcuts"]):

            # (reco Energy - MC Energy) vs. reco Energy 2D histograms
            fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
            for i, key in enumerate(events):
                ax = axs[i]
                plt.sca(ax)

                counts, _, _ = np.histogram2d(
                            events[key]["reco_Energy"],
                            events[key]["reco_Energy"] - events[key]["MC_Energy"],
                            bins=(e_bin_fine_edges, np.linspace(-1, 1, 100)))
                ax.pcolormesh(e_bin_fine_edges.value, np.linspace(-1, 1, 100),
                              np.sqrt(counts.T))
                plt.plot(e_bin_fine_edges.value[[0, -1]], [0, 0],
                         color="darkgreen")
                plt.title(channel_map[key])
                ax.set_xlabel(r"$E_\mathrm{reco}$ / TeV")
                if i == 0:
                    ax.set_ylabel(r"$(E_\mathrm{reco} - E_\mathrm{MC})$ / TeV")
                ax.set_xscale("log")
                plt.grid()

            plt.suptitle(mode)
            plt.subplots_adjust(left=.1, wspace=.1)
            if args.write:
                save_fig(args.plots_dir + "_".join(["DeltaE_vs_recoE", mode]))
            plt.pause(.1)

            # (reco Energy - MC Energy) vs. MC Energy 2D histograms
            fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
            for i, key in enumerate(events):
                ax = axs[i]
                plt.sca(ax)

                counts, _, _ = np.histogram2d(
                            events[key]["MC_Energy"],
                            events[key]["reco_Energy"] - events[key]["MC_Energy"],
                            bins=(e_bin_fine_edges, np.linspace(-1, 1, 100)))
                ax.pcolormesh(e_bin_fine_edges.value, np.linspace(-1, 1, 100),
                              np.sqrt(counts.T))
                plt.plot(e_bin_fine_edges.value[[0, -1]], [0, 0],
                         color="darkgreen")
                plt.title(channel_map[key])
                ax.set_xlabel(r"$E_\mathrm{MC}$ / TeV")
                if i == 0:
                    ax.set_ylabel(r"$(E_\mathrm{reco} - E_\mathrm{MC})$ / TeV")
                ax.set_xscale("log")
                plt.grid()

            plt.suptitle(mode)
            plt.subplots_adjust(left=.1, wspace=.1)
            if args.write:
                save_fig(args.plots_dir + "_".join(["DeltaE_vs_MCE", mode]))
            plt.pause(.1)

    if not which or "Energy_resolution" in which:
        # energy resolution as 68th percentile of the relative reconstructed error binned
        # in reconstructed energy
        rel_DeltaE_w = np.abs(events_w['g']["reco_Energy"] -
                              events_w['g']["MC_Energy"])/events_w['g']["reco_Energy"]
        DeltaE68_w_ebinned = percentiles(rel_DeltaE_w, events_w['g']["reco_Energy"],
                                         e_bin_edges.value, 68)

        rel_DeltaE_t = np.abs(events_t['g']["reco_Energy"] -
                              events_t['g']["MC_Energy"])/events_t['g']["reco_Energy"]
        DeltaE68_t_ebinned = percentiles(rel_DeltaE_t, events_t['g']["reco_Energy"],
                                         e_bin_edges.value, 68)

        plt.figure()
        plt.plot(e_bin_centres.value, DeltaE68_t_ebinned, label="gamma -- tail",
                 marker='v', color="darkorange")
        plt.plot(e_bin_centres.value, DeltaE68_w_ebinned, label="gamma -- wave",
                 marker='^', color="darkred")
        plt.title("Energy Resolution")
        plt.xlabel(r"$E_\mathrm{reco}$ / TeV")
        plt.ylabel(r"$(|E_\mathrm{reco} - E_\mathrm{MC}|)_{68}/E_\mathrm{reco}$")
        plt.gca().set_xscale("log")
        plt.grid()
        plt.legend()

        if args.write:
            save_fig(args.plots_dir + "Energy_resolution_vs_recoE")
        plt.pause(.1)

        # energy resolution binned in MC Energy
        for key in ['g', 'e']:
            plt.figure()
            for events, mode in zip([events_w, events_t],
                                    ["wavelets", "tailcuts"]):

                rel_DeltaE = np.abs(events[key]["reco_Energy"] -
                                    events[key]["MC_Energy"]) / events[key]["MC_Energy"]
                DeltaE68_ebinned = percentiles(rel_DeltaE, events[key]["MC_Energy"],
                                               e_bin_edges.value, 68)

                plt.plot(e_bin_centres.value, DeltaE68_ebinned,
                         label=" -- ".join([channel_map[key], mode]),
                         marker=channel_marker_map[key],
                         color="darkred" if "wave" in mode else "darkorange")
            plt.title("Energy Resolution")
            plt.xlabel(r"$E_\mathrm{MC}$ / TeV")
            plt.ylabel(r"$(|E_\mathrm{reco} - E_\mathrm{MC}|)_{68}/E_\mathrm{MC}$")
            plt.gca().set_xscale("log")
            plt.grid()
            plt.legend()

            if args.write:
                save_fig(args.plots_dir + "Energy_resolution_"
                         + channel_map[key])
            plt.pause(.1)

    if not which or "Energy_bias" in which:
        # Ebias as median of 1-E_reco/E_MC
        for key in ['g', 'e']:
            plt.figure()
            for events, mode in zip([events_w, events_t],
                                    ["wavelets", "tailcuts"]):
                Ebias = 1 - (events[key]["reco_Energy"] / events[key]["MC_Energy"])
                Ebias_medians = percentiles(Ebias, events[key]["reco_Energy"],
                                            e_bin_edges.value, 50)
                plt.plot(e_bin_centres.value, Ebias_medians,
                         label=" -- ".join([channel_map[key], mode]),
                         marker=channel_marker_map[key],
                         color="darkred" if "wave" in mode else "darkorange")
            plt.title("Energy Bias")
            plt.xlabel(r"$E_\mathrm{reco}$ / TeV")
            plt.ylabel(r"$(1 - E_\mathrm{reco}/E_\mathrm{MC})_{50}$")
            plt.ylim([-0.2, .3])
            plt.gca().set_xscale("log")
            plt.legend()
            plt.grid()

            if args.write:
                save_fig(args.plots_dir + "Energy_bias_"
                         + channel_map[key])
            plt.pause(.1)

    if not which or any(what in which for what in ["gen_spectrum", "expected_events",
                                                   "effective_areas", "event_rate"]):
        bin_centres, bin_widths = {}, {}
        bin_centres['g'] = (edges_gammas[:-1] + edges_gammas[1:]) / 2
        bin_centres['p'] = (edges_proton[:-1] + edges_proton[1:]) / 2
        bin_centres['e'] = (edges_electr[:-1] + edges_electr[1:]) / 2
        bin_widths['g'] = np.diff(edges_gammas)
        bin_widths['p'] = np.diff(edges_proton)
        bin_widths['e'] = np.diff(edges_electr)

        for events, mode in zip([events_t, events_w], ["tailcuts", "wavelets"]):
            SensCalc = SensitivityPointSource(
                    reco_energies={'g': events['g']['reco_Energy'].values * u.TeV,
                                   'p': events['p']['reco_Energy'].values * u.TeV,
                                   'e': events['e']['reco_Energy'].values * u.TeV},
                    mc_energies={'g': events['g']['MC_Energy'].values * u.TeV,
                                 'p': events['p']['MC_Energy'].values * u.TeV,
                                 'e': events['e']['MC_Energy'].values * u.TeV},
                    energy_bin_edges={'g': edges_gammas,
                                      'p': edges_proton,
                                      'e': edges_electr},
                    flux_unit=flux_unit)

            SensCalc.generate_event_weights(
                    n_simulated_events={'g': meta_gammas["n_simulated"],
                                        'p': meta_proton["n_simulated"],
                                        'e': meta_electr["n_simulated"]},
                    generator_areas={'g': np.pi *
                                     (meta_gammas["gen_radius"] * u.m)**2,
                                     'p': np.pi *
                                     (meta_proton["gen_radius"] * u.m)**2,
                                     'e': np.pi *
                                     (meta_electr["gen_radius"] * u.m)**2},
                    observation_time=observation_time,
                    spectra={'g': crab_source_rate,
                             'p': cr_background_rate,
                             'e': electron_spectrum},
                    e_min_max={'g': (meta_gammas["e_min"],
                                     meta_gammas["e_max"]) * u.TeV,
                               'p': (meta_proton["e_min"],
                                     meta_proton["e_max"]) * u.TeV,
                               'e': (meta_electr["e_min"],
                                     meta_electr["e_max"]) * u.TeV},
                    extensions={'p': meta_proton["diff_cone"] * u.deg,
                                'e': meta_electr["diff_cone"] * u.deg},
                    generator_gamma={'g': meta_gammas["gen_gamma"],
                                     'p': meta_proton["gen_gamma"],
                                     'e': meta_electr["gen_gamma"]})
            SensCalc.get_effective_areas(
                    generator_areas={'g': np.pi *
                                     (meta_gammas["gen_radius"] * u.m)**2,
                                     'p': np.pi *
                                     (meta_proton["gen_radius"] * u.m)**2,
                                     'e': np.pi *
                                     (meta_electr["gen_radius"] * u.m)**2},
                    n_simulated_events={'g': meta_gammas["n_simulated"],
                                        'p': meta_proton["n_simulated"],
                                        'e': meta_electr["n_simulated"]},
                    generator_spectra={'g': e_minus_2,
                                       'p': e_minus_2,
                                       'e': e_minus_2},
                    )
            SensCalc.get_expected_events()

            if not which or "gen_spectrum" in which:
                # plot MC generator spectrum and selected spectrum
                fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
                for i, key in enumerate(events):
                    ax = axs[i]
                    plt.sca(ax)
                    plt.plot(bin_centres[key].value,
                             SensCalc.generator_energy_hists[key], label="generated",
                             # align="center", width=bin_widths[key].value
                             )
                    plt.plot(bin_centres[key].value,
                             SensCalc.selected_events[key], label="selected",
                             # align="center", width=bin_widths[key].value
                             )
                    plt.xlabel(
                        r"$E_\mathrm{MC} / \mathrm{" + str(bin_centres[key].unit) + "}$")
                    if i == 0:
                        plt.ylabel("number of (unweighted) events")
                    plt.gca().set_xscale("log")
                    plt.gca().set_yscale("log")
                    plt.title(channel_map[key])
                    plt.legend()
                plt.suptitle(mode)
                plt.subplots_adjust(left=.1, wspace=.1)
                if args.write:
                    save_fig(args.plots_dir + "generator_events_" + mode)
                plt.pause(.1)

            if not which or "expected_events" in which:
                # plot the number of expected events in each energy bin
                plt.figure()
                for key in ['p', 'e', 'g']:
                    plt.plot(
                        bin_centres[key] / energy_unit,
                        SensCalc.exp_events_per_energy_bin[key],
                        label=channel_map[key],
                        color=channel_color_map[key],
                        # align="center", width=bin_widths[key].value, alpha=.75
                    )
                plt.gca().set_xscale("log")
                plt.gca().set_yscale("log")

                plt.xlabel(r"$E_\mathrm{MC} / \mathrm{" + str(energy_unit) + "}$")
                plt.ylabel("expected events in {}".format(observation_time))
                plt.legend()
                if args.write:
                    save_fig(args.plots_dir + "expected_events_" + mode)
                plt.pause(.1)

            if not which or "event_rate" in which:
                # plot the number of expected events in each energy bin
                plt.figure()
                for key in ['p', 'e', 'g']:
                    plt.plot(
                        bin_centres[key] / energy_unit,
                        (SensCalc.exp_events_per_energy_bin[key] /
                         observation_time).to(u.s**-1).value *
                        (1 if key == 'g' else alpha),
                        label=channel_map[key],
                        marker=channel_marker_map[key],
                        color=channel_color_map[key],
                        # align="center", width=bin_widths[key].value, alpha=.75
                    )
                plt.gca().set_xscale("log")
                plt.gca().set_yscale("log")

                plt.xlabel(r"$E_\mathrm{MC} / \mathrm{" + str(energy_unit) + "}$")
                plt.ylabel(r"event rate: $\frac{dN}{dt} / \mathrm{s}^{-1}$")
                plt.legend()
                if args.write:
                    save_fig(args.plots_dir + "event_rate_" + mode)
                plt.pause(.1)

            if not which or "effective_areas" in which:
                # plot effective area
                plt.figure()  # figsize=(16, 8))
                plt.suptitle("Effective Areas")
                for key in ['p', 'e', 'g']:
                    plt.plot(
                        bin_centres[key] / energy_unit,
                        SensCalc.effective_areas[key] / u.m**2,
                        label=channel_map[key], color=channel_color_map[key],
                        marker=channel_marker_map[key])
                plt.xlabel(r"$E_\mathrm{MC} / \mathrm{" + str(energy_unit) + "}$")
                plt.ylabel(r"$A_\mathrm{eff} / \mathrm{m}^2$")
                plt.gca().set_xscale("log")
                plt.gca().set_yscale("log")
                plt.title(mode)
                plt.legend()
                if args.write:
                    save_fig(args.plots_dir + "effective_areas_" + mode)
                plt.pause(.1)
예제 #13
0
def make_performance_plots(events_w, events_t, which=None):

    if (not which or "multiplicity" in which) and False:
        fig = plt.figure()
        plt.hist(events_w['g']["NTels_reco"], alpha=.5,
                 bins=np.arange(0, 51, 2))
        plt.hist(events_t['g']["NTels_reco"], alpha=.5,
                 bins=np.arange(0, 51, 2))
        if args.write:
            save_fig(args.plots_dir + "multiplicity")
        plt.pause(.1)

    if not which or "multiplicity_by_size" in which:
        fig, axs = plt.subplots(1, 3, figsize=(10, 5))
        plt.sca(axs[0])
        plt.hist(events_w['g']["NTels_reco_lst"], alpha=.5,
                 bins=np.arange(0, 5, 1))
        plt.hist(events_t['g']["NTels_reco_lst"], alpha=.5,
                 bins=np.arange(0, 5, 1))
        plt.suptitle("LST")

        plt.sca(axs[1])
        plt.hist(events_w['g']["NTels_reco_mst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.hist(events_t['g']["NTels_reco_mst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.suptitle("MST")

        plt.sca(axs[2])
        plt.hist(events_w['g']["NTels_reco_sst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.hist(events_t['g']["NTels_reco_sst"], alpha=.5,
                 bins=np.arange(0, 15, 1))
        plt.suptitle("SST")

        if args.write:
            save_fig(args.plots_dir + "multiplicity_by_size")
        plt.pause(.1)

    if (not which or "ang_res_verbose" in which) and False:
        fig, axes = plt.subplots(1, 2)
        n_tel_max = 50  # np.max(gammas_w["NTels_reco"])
        # plt.subplots_adjust(left=0.11, right=0.97, hspace=0.39, wspace=0.29)
        plot_hex_and_violin(events_w['g']["NTels_reco"],
                            np.log10(events_w['g']["off_angle"]),
                            np.arange(0, n_tel_max + 1, 5),
                            xlabel=r"$N_\mathrm{Tels}$",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            do_hex=False, axis=axes[0],
                            extent=[0, n_tel_max, -3, 0])
        plot_hex_and_violin(np.log10(events_w['g']["reco_Energy"]),
                            np.log10(events_w['g']["off_angle"]),
                            np.linspace(-1, 3, 17),
                            xlabel=r"$\log_{10}(E_\mathrm{reco}$ / TeV)",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            v_padding=0.015, axis=axes[1], extent=[-.5, 2.5, -3, 0])
        plt.suptitle("wavelet")

        if args.write:
            save_fig(args.plots_dir + "ang_res_verbose_wave")
        plt.pause(.1)

        fig, axes = plt.subplots(1, 2)
        n_tel_max = 50  # np.max(gammas_w["NTels_reco"])
        # plt.subplots_adjust(left=0.11, right=0.97, hspace=0.39, wspace=0.29)
        plot_hex_and_violin(events_t['g']["NTels_reco"],
                            np.log10(events_t['g']["off_angle"]),
                            np.arange(0, n_tel_max + 1, 5),
                            xlabel=r"$N_\mathrm{Tels}$",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            do_hex=False, axis=axes[0],
                            extent=[0, n_tel_max, -3, 0])
        plot_hex_and_violin(np.log10(events_t['g']["reco_Energy"]),
                            np.log10(events_t['g']["off_angle"]),
                            np.linspace(-1, 3, 17),
                            xlabel=r"$\log_{10}(E_\mathrm{reco}$ / TeV)",
                            ylabel=r"$\log_{10}(\xi / ^\circ)$",
                            v_padding=0.015, axis=axes[1], extent=[-.5, 2.5, -3, 0])
        plt.suptitle("tailcuts")

        if args.write:
            save_fig(args.plots_dir + "ang_res_verbose_tail")
        plt.pause(.1)

    # angular resolutions

    if not which or ("ang_res" in which or "xi" in which):
        plt.figure()
        for key in events_w:
            xi_68_w = percentiles(events_w[key]["off_angle"],
                                  events_w[key]["reco_Energy"],
                                  e_bin_edges.value, 68)
            xi_68_t = percentiles(events_t[key]["off_angle"],
                                  events_t[key]["reco_Energy"],
                                  e_bin_edges.value, 68)

            plt.plot(e_bin_centres.value, xi_68_t,
                     color="darkorange",
                     marker=channel_marker_map[key],
                     ls=channel_linestyle_map[key],
                     label="--".join([channel_map[key], "tail"]))
            plt.plot(e_bin_centres.value, xi_68_w,
                     color="darkred",
                     marker=channel_marker_map[key],
                     ls=channel_linestyle_map[key],
                     label="--".join([channel_map[key], "wave"]))
        plt.title("angular resolution")
        plt.xlabel(r"$E_\mathrm{reco}$ / TeV")
        plt.ylabel(r"$\xi_\mathrm{68} / ^\circ$")
        plt.gca().set_xscale("log")
        plt.gca().set_yscale("log")
        plt.grid()
        plt.legend()

        if args.write:
            save_fig(args.plots_dir + "xi")
        plt.pause(.1)

    if not which or "gammaness" in which:
        # gammaness plots
        show_gammaness(events_w, "wavelets")
        show_gammaness(events_t, "tailcuts")

    if not which or "energy_migration" in which:
        # MC Energy vs. reco Energy 2D histograms
        for events, mode in zip([events_w, events_t],
                                ["wavelets", "tailcuts"]):
            fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
            for i, key in enumerate(events):
                ax = axs[i]
                plt.sca(ax)
                counts, _, _ = np.histogram2d(events[key]["reco_Energy"],
                                              events[key]["MC_Energy"],
                                              bins=(e_bin_fine_edges, e_bin_fine_edges))
                ax.pcolormesh(e_bin_fine_edges.value, e_bin_fine_edges.value, counts.T)
                plt.plot(e_bin_fine_edges.value[[0, -1]], e_bin_fine_edges.value[[0, -1]],
                         color="darkgreen")
                plt.title(channel_map[key])
                ax.set_xlabel(r"$E_\mathrm{reco}$ / TeV")
                if i == 0:
                    ax.set_ylabel(r"$E_\mathrm{MC}$ / TeV")
                ax.set_xscale("log")
                ax.set_yscale("log")
                plt.grid()

            plt.suptitle(mode)
            plt.subplots_adjust(left=.1, wspace=.1)

            if args.write:
                save_fig(args.plots_dir + "_".join(["energy_migration", mode]))
            plt.pause(.1)

    if not which or "DeltaE" in which:
        # (reco Energy - MC Energy) 2D histograms
        for events, mode in zip([events_w, events_t],
                                ["wavelets", "tailcuts"]):

            # (reco Energy - MC Energy) vs. reco Energy 2D histograms
            fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
            for i, key in enumerate(events):
                ax = axs[i]
                plt.sca(ax)

                counts, _, _ = np.histogram2d(
                            events[key]["reco_Energy"],
                            events[key]["reco_Energy"] - events[key]["MC_Energy"],
                            bins=(e_bin_fine_edges, np.linspace(-1, 1, 100)))
                ax.pcolormesh(e_bin_fine_edges.value, np.linspace(-1, 1, 100),
                              np.sqrt(counts.T))
                plt.plot(e_bin_fine_edges.value[[0, -1]], [0, 0],
                         color="darkgreen")
                plt.title(channel_map[key])
                ax.set_xlabel(r"$E_\mathrm{reco}$ / TeV")
                if i == 0:
                    ax.set_ylabel(r"$(E_\mathrm{reco} - E_\mathrm{MC})$ / TeV")
                ax.set_xscale("log")
                plt.grid()

            plt.suptitle(mode)
            plt.subplots_adjust(left=.1, wspace=.1)
            if args.write:
                save_fig(args.plots_dir + "_".join(["DeltaE_vs_recoE", mode]))
            plt.pause(.1)

            # (reco Energy - MC Energy) vs. MC Energy 2D histograms
            fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
            for i, key in enumerate(events):
                ax = axs[i]
                plt.sca(ax)

                counts, _, _ = np.histogram2d(
                            events[key]["MC_Energy"],
                            events[key]["reco_Energy"] - events[key]["MC_Energy"],
                            bins=(e_bin_fine_edges, np.linspace(-1, 1, 100)))
                ax.pcolormesh(e_bin_fine_edges.value, np.linspace(-1, 1, 100),
                              np.sqrt(counts.T))
                plt.plot(e_bin_fine_edges.value[[0, -1]], [0, 0],
                         color="darkgreen")
                plt.title(channel_map[key])
                ax.set_xlabel(r"$E_\mathrm{MC}$ / TeV")
                if i == 0:
                    ax.set_ylabel(r"$(E_\mathrm{reco} - E_\mathrm{MC})$ / TeV")
                ax.set_xscale("log")
                plt.grid()

            plt.suptitle(mode)
            plt.subplots_adjust(left=.1, wspace=.1)
            if args.write:
                save_fig(args.plots_dir + "_".join(["DeltaE_vs_MCE", mode]))
            plt.pause(.1)

    if not which or "Energy_resolution" in which:
        # energy resolution as 68th percentile of the relative reconstructed error binned
        # in reconstructed energy
        rel_DeltaE_w = np.abs(events_w['g']["reco_Energy"] -
                              events_w['g']["MC_Energy"])/events_w['g']["reco_Energy"]
        DeltaE68_w_ebinned = percentiles(rel_DeltaE_w, events_w['g']["reco_Energy"],
                                         e_bin_edges.value, 68)

        rel_DeltaE_t = np.abs(events_t['g']["reco_Energy"] -
                              events_t['g']["MC_Energy"])/events_t['g']["reco_Energy"]
        DeltaE68_t_ebinned = percentiles(rel_DeltaE_t, events_t['g']["reco_Energy"],
                                         e_bin_edges.value, 68)

        plt.figure()
        plt.plot(e_bin_centres.value, DeltaE68_t_ebinned, label="gamma -- tail",
                 marker='v', color="darkorange")
        plt.plot(e_bin_centres.value, DeltaE68_w_ebinned, label="gamma -- wave",
                 marker='^', color="darkred")
        plt.title("Energy Resolution")
        plt.xlabel(r"$E_\mathrm{reco}$ / TeV")
        plt.ylabel(r"$(|E_\mathrm{reco} - E_\mathrm{MC}|)_{68}/E_\mathrm{reco}$")
        plt.gca().set_xscale("log")
        plt.grid()
        plt.legend()

        if args.write:
            save_fig(args.plots_dir + "Energy_resolution_vs_recoE")
        plt.pause(.1)

        # energy resolution binned in MC Energy
        for key in ['g', 'e']:
            plt.figure()
            for events, mode in zip([events_w, events_t],
                                    ["wavelets", "tailcuts"]):

                rel_DeltaE = np.abs(events[key]["reco_Energy"] -
                                    events[key]["MC_Energy"]) / events[key]["MC_Energy"]
                DeltaE68_ebinned = percentiles(rel_DeltaE, events[key]["MC_Energy"],
                                               e_bin_edges.value, 68)

                plt.plot(e_bin_centres.value, DeltaE68_ebinned,
                         label=" -- ".join([channel_map[key], mode]),
                         marker=channel_marker_map[key],
                         color="darkred" if "wave" in mode else "darkorange")
            plt.title("Energy Resolution")
            plt.xlabel(r"$E_\mathrm{MC}$ / TeV")
            plt.ylabel(r"$(|E_\mathrm{reco} - E_\mathrm{MC}|)_{68}/E_\mathrm{MC}$")
            plt.gca().set_xscale("log")
            plt.grid()
            plt.legend()

            if args.write:
                save_fig(args.plots_dir + "Energy_resolution_"
                         + channel_map[key])
            plt.pause(.1)

    if not which or "Energy_bias" in which:
        # Ebias as median of 1-E_reco/E_MC
        for key in ['g', 'e']:
            plt.figure()
            for events, mode in zip([events_w, events_t],
                                    ["wavelets", "tailcuts"]):
                Ebias = 1 - (events[key]["reco_Energy"] / events[key]["MC_Energy"])
                Ebias_medians = percentiles(Ebias, events[key]["reco_Energy"],
                                            e_bin_edges.value, 50)
                plt.plot(e_bin_centres.value, Ebias_medians,
                         label=" -- ".join([channel_map[key], mode]),
                         marker=channel_marker_map[key],
                         color="darkred" if "wave" in mode else "darkorange")
            plt.title("Energy Bias")
            plt.xlabel(r"$E_\mathrm{reco}$ / TeV")
            plt.ylabel(r"$(1 - E_\mathrm{reco}/E_\mathrm{MC})_{50}$")
            plt.ylim([-0.2, .3])
            plt.gca().set_xscale("log")
            plt.legend()
            plt.grid()

            if args.write:
                save_fig(args.plots_dir + "Energy_bias_"
                         + channel_map[key])
            plt.pause(.1)

    if not which or any(what in which for what in ["gen_spectrum", "expected_events",
                                                   "effective_areas", "event_rate"]):
        bin_centres, bin_widths = {}, {}
        bin_centres['g'] = (edges_gammas[:-1] + edges_gammas[1:]) / 2
        bin_centres['p'] = (edges_proton[:-1] + edges_proton[1:]) / 2
        bin_centres['e'] = (edges_electr[:-1] + edges_electr[1:]) / 2
        bin_widths['g'] = np.diff(edges_gammas)
        bin_widths['p'] = np.diff(edges_proton)
        bin_widths['e'] = np.diff(edges_electr)

        # for events, mode in zip([events_t, events_w], ["tailcuts", "wavelets"]):
        for events, mode in zip([events_w], ["wavelets"]):
            SensCalc = SensitivityPointSource(
                reco_energies={'g': events['g']['reco_Energy'].values * u.TeV,
                               'p': events['p']['reco_Energy'].values * u.TeV,
                               'e': events['e']['reco_Energy'].values * u.TeV},
                mc_energies={'g': events['g']['MC_Energy'].values * u.TeV,
                             'p': events['p']['MC_Energy'].values * u.TeV,
                             'e': events['e']['MC_Energy'].values * u.TeV},
                energy_bin_edges={'g': edges_gammas,
                                  'p': edges_proton,
                                  'e': edges_electr},
                flux_unit=flux_unit)

            SensCalc.generate_event_weights(
                n_simulated_events={'g': meta_gammas["n_simulated"],
                                    'p': meta_proton["n_simulated"],
                                    'e': meta_electr["n_simulated"]},
                generator_areas={'g': np.pi *
                                 (meta_gammas["gen_radius"] * u.m)**2,
                                 'p': np.pi *
                                 (meta_proton["gen_radius"] * u.m)**2,
                                 'e': np.pi *
                                 (meta_electr["gen_radius"] * u.m)**2},
                observation_time=observation_time,
                spectra={'g': crab_source_rate,
                         'p': cr_background_rate,
                         'e': electron_spectrum},
                e_min_max={'g': (meta_gammas["e_min"],
                                 meta_gammas["e_max"]) * u.TeV,
                           'p': (meta_proton["e_min"],
                                 meta_proton["e_max"]) * u.TeV,
                           'e': (meta_electr["e_min"],
                                 meta_electr["e_max"]) * u.TeV},
                extensions={'p': meta_proton["diff_cone"] * u.deg,
                            'e': meta_electr["diff_cone"] * u.deg},
                generator_gamma={'g': meta_gammas["gen_gamma"],
                                 'p': meta_proton["gen_gamma"],
                                 'e': meta_electr["gen_gamma"]})
            SensCalc.get_effective_areas(
                generator_areas={'g': np.pi *
                                 (meta_gammas["gen_radius"] * u.m)**2,
                                 'p': np.pi *
                                 (meta_proton["gen_radius"] * u.m)**2,
                                 'e': np.pi *
                                 (meta_electr["gen_radius"] * u.m)**2},
                n_simulated_events={'g': meta_gammas["n_simulated"],
                                    'p': meta_proton["n_simulated"],
                                    'e': meta_electr["n_simulated"]},
                generator_spectra={'g': e_minus_2,
                                   'p': e_minus_2,
                                   'e': e_minus_2}
            )

            print("expected events:")
            for ch in ['g', 'p', 'e']:
                print(f"{ch}:", np.sum(SensCalc.event_weights[ch]))

            SensCalc.get_expected_events()
            SensCalc.get_expected_events_in_reco_e()

            if not which or "gen_spectrum" in which:
                # plot MC generator spectrum and selected spectrum
                fig, axs = plt.subplots(1, 3, figsize=(10, 5), sharey=True)
                for i, key in enumerate(events):
                    ax = axs[i]
                    plt.sca(ax)
                    plt.plot(bin_centres[key].value,
                             SensCalc.generator_energy_hists[key], label="generated",
                             # align="center", width=bin_widths[key].value
                             )
                    plt.plot(bin_centres[key].value,
                             SensCalc.selected_events[key], label="selected",
                             # align="center", width=bin_widths[key].value
                             )
                    plt.xlabel(
                        r"$E_\mathrm{MC} / \mathrm{" + str(bin_centres[key].unit) + "}$")
                    if i == 0:
                        plt.ylabel("number of (unweighted) events")
                    plt.gca().set_xscale("log")
                    plt.gca().set_yscale("log")
                    plt.title(channel_map[key])
                    plt.legend()
                plt.suptitle(mode)
                plt.subplots_adjust(left=.1, wspace=.1)
                if args.write:
                    save_fig(args.plots_dir + "generator_events_" + mode)
                plt.pause(.1)

            if not which or "expected_events" in which:
                # plot the number of expected events in each energy bin
                plt.figure()
                for key in ['g', 'p', 'e']:
                    plt.plot(
                        bin_centres[key] / energy_unit,
                        SensCalc.exp_events_per_energy_bin[key],
                        label=channel_map[key],
                        color=channel_color_map[key],
                        # align="center", width=bin_widths[key].value, alpha=.75
                    )
                plt.gca().set_xscale("log")
                plt.gca().set_yscale("log")
                plt.title(mode)

                plt.xlabel(r"$E_\mathrm{MC} / \mathrm{" + str(energy_unit) + "}$")
                plt.ylabel("expected events in {}".format(observation_time))
                plt.legend()
                if args.write:
                    save_fig(args.plots_dir + "expected_events_" + mode)
                plt.pause(.1)

            if not which or "event_rate" in which:
                # plot the number of expected events in each energy bin
                plt.figure()
                for key in ['p', 'e', 'g']:
                    plt.plot(
                        bin_centres[key] / energy_unit,
                        (SensCalc.exp_events_per_reco_energy_bin[key] /
                         observation_time).to(u.s**-1).value *
                        (1 if key == 'g' else alpha),
                        label=channel_map[key],
                        marker=channel_marker_map[key],
                        color=channel_color_map[key],
                        # align="center", width=bin_widths[key].value, alpha=.75
                    )
                plt.gca().set_xscale("log")
                plt.gca().set_yscale("log")

                plt.xlabel(r"$E_\mathrm{MC} / \mathrm{" + str(energy_unit) + "}$")
                plt.ylabel(r"event rate: $\frac{dN}{dt} / \mathrm{s}^{-1}$")
                plt.title(mode)
                plt.legend()
                if args.write:
                    save_fig(args.plots_dir + "event_rate_" + mode)
                plt.pause(.1)

            if not which or "effective_areas" in which:
                # plot effective area
                plt.figure()
                plt.suptitle("Effective Areas")
                for key in ['g', 'p', 'e']:
                    plt.plot(
                        bin_centres[key] / energy_unit,
                        SensCalc.effective_areas[key] / u.m**2,
                        label=channel_map[key], color=channel_color_map[key],
                        marker=channel_marker_map[key])
                plt.xlabel(r"$E_\mathrm{MC} / \mathrm{" + str(energy_unit) + "}$")
                plt.ylabel(r"$A_\mathrm{eff} / \mathrm{m}^2$")
                plt.gca().set_xscale("log")
                plt.gca().set_yscale("log")
                plt.title(mode)
                plt.legend()
                if args.write:
                    save_fig(args.plots_dir + "effective_areas_" + mode)
                plt.pause(.1)