Exemple #1
0
def test_integrate_energy():
    from pyirf.simulations import SimulatedEventsInfo

    info = SimulatedEventsInfo(
        n_showers=int(1e6),
        energy_min=100 * u.GeV,
        energy_max=10 * u.TeV,
        max_impact=500 * u.m,
        spectral_index=-2,
        viewcone=10 * u.deg,
    )
    # simplest case, no bins  outside e_min, e_max
    energy_bins = np.geomspace(info.energy_min, info.energy_max, 20)
    assert np.isclose(
        info.calculate_n_showers_per_energy(energy_bins).sum(),
        info.n_showers,
    )

    # simple case, e_min and e_max on bin edges
    energy_bins = np.geomspace(info.energy_min, info.energy_max, 20)
    energy_bins = np.append(0.9 * info.energy_min, energy_bins)
    energy_bins = np.append(energy_bins, 1.1 * info.energy_max)

    events = info.calculate_n_showers_per_energy(energy_bins)
    assert np.isclose(info.n_showers, events.sum())

    assert events[0] == 0
    assert events[-1] == 0

    # complex case, e_min and e_max inside bins
    energy_bins = np.geomspace(0.5 * info.energy_min, 2 * info.energy_max, 5)
    events = info.calculate_n_showers_per_energy(energy_bins)
    assert np.isclose(info.n_showers, events.sum())
    assert events[0] > 0
    assert events[-1] > 0
Exemple #2
0
def test_effective_area_per_energy():
    from pyirf.irf import effective_area_per_energy
    from pyirf.simulations import SimulatedEventsInfo

    true_energy_bins = [0.1, 1.0, 10.0] * u.TeV
    selected_events = QTable({
        "true_energy":
        np.append(np.full(1000, 0.5), np.full(10, 5)),
    })

    # this should give 100000 events in the first bin and 10000 in the second
    simulation_info = SimulatedEventsInfo(
        n_showers=110000,
        energy_min=true_energy_bins[0],
        energy_max=true_energy_bins[-1],
        max_impact=100 / np.sqrt(np.pi) *
        u.m,  # this should give a nice round area
        spectral_index=-2,
        viewcone=0 * u.deg,
    )

    area = effective_area_per_energy(selected_events, simulation_info,
                                     true_energy_bins)

    assert area.shape == (len(true_energy_bins) - 1, )
    assert area.unit == u.m**2
    assert u.allclose(area, [100, 10] * u.m**2)
Exemple #3
0
def read_sim_info(path):
    key = "/simulation/run_config"
    log.info(f"Reading sim info for file {path} and key {key}")
    run_info = read_table(path, key)
    e_min = np.unique(run_info.columns["energy_range_min"])
    assert len(e_min) == 1
    e_max = np.unique(run_info.columns["energy_range_max"])
    assert len(e_max) == 1
    max_impact = np.unique(run_info.columns["max_scatter_range"])
    assert len(max_impact) == 1
    index = np.unique(run_info.columns["spectral_index"])
    assert len(index) == 1
    view_cone = np.unique(run_info.columns["max_viewcone_radius"])
    assert len(view_cone) == 1

    sim_info = SimulatedEventsInfo(
        n_showers=(run_info.columns["shower_reuse"] *
                   run_info.columns["num_showers"]).sum(),
        energy_min=u.Quantity(e_min[0], u.TeV),
        energy_max=u.Quantity(e_max[0], u.TeV),
        max_impact=u.Quantity(max_impact[0], u.m),
        spectral_index=index[0],
        viewcone=u.Quantity(view_cone[0], u.deg),
    )

    return sim_info
Exemple #4
0
def test_integrate_energy_fov_pointlike():
    from pyirf.simulations import SimulatedEventsInfo

    info = SimulatedEventsInfo(
        n_showers=int(1e6),
        energy_min=100 * u.GeV,
        energy_max=10 * u.TeV,
        max_impact=500 * u.m,
        spectral_index=-2,
        viewcone=0 * u.deg,
    )

    fov_bins = [0, 9, 11, 20] * u.deg
    energy_bins = np.geomspace(info.energy_min, info.energy_max, 20)

    # make sure we raise an error on invalid input
    with pytest.raises(ValueError):
        info.calculate_n_showers_per_energy_and_fov(energy_bins, fov_bins)
Exemple #5
0
def read_mc_dl2_to_QTable(filename):
    """
    Read MC DL2 files from lstchain and convert into pyirf internal format
    - astropy.table.QTable

    Parameters
    ----------
    filename: path

    Returns
    -------
    `astropy.table.QTable`, `pyirf.simulations.SimulatedEventsInfo`
    """

    # mapping
    name_mapping = {
        "mc_energy": "true_energy",
        "mc_alt": "true_alt",
        "mc_az": "true_az",
        "mc_alt_tel": "pointing_alt",
        "mc_az_tel": "pointing_az",
        "gammaness": "gh_score",
    }

    unit_mapping = {
        "true_energy": u.TeV,
        "reco_energy": u.TeV,
        "pointing_alt": u.rad,
        "pointing_az": u.rad,
        "true_alt": u.rad,
        "true_az": u.rad,
        "reco_alt": u.rad,
        "reco_az": u.rad,
    }

    simu_info = read_simu_info_merged_hdf5(filename)
    pyirf_simu_info = SimulatedEventsInfo(
        n_showers=simu_info.num_showers * simu_info.shower_reuse,
        energy_min=simu_info.energy_range_min,
        energy_max=simu_info.energy_range_max,
        max_impact=simu_info.max_scatter_range,
        spectral_index=simu_info.spectral_index,
        viewcone=simu_info.max_viewcone_radius,
    )

    events = pd.read_hdf(
        filename, key=dl2_params_lstcam_key).rename(columns=name_mapping)
    events = QTable.from_pandas(events)

    for k, v in unit_mapping.items():
        events[k] *= v

    return events, pyirf_simu_info
Exemple #6
0
def read_file(infile):
    log.debug(f"Reading {infile}")
    events = read_h5py(infile, key='events', columns=list(COLUMN_MAP.keys()))
    sim_runs = read_h5py(infile, key='corsika_runs')

    events.rename(columns=COLUMN_MAP, inplace=True)

    n_showers = np.sum(sim_runs.num_showers * sim_runs.shower_reuse)
    log.debug(f"Number of events from corsika_runs: {n_showers}")

    sim_info = SimulatedEventsInfo(
        n_showers=n_showers,
        energy_min=u.Quantity(sim_runs["energy_range_min"][0], u.TeV),
        energy_max=u.Quantity(sim_runs["energy_range_max"][0], u.TeV),
        max_impact=u.Quantity(sim_runs["max_scatter_range"][0], u.m),
        spectral_index=sim_runs["spectral_index"][0],
        viewcone=u.Quantity(
            sim_runs["max_viewcone_radius"][0] -
            sim_runs["min_viewcone_radius"][0], u.deg),
    )
    return table.QTable.from_pandas(events, units=UNIT_MAP), sim_info
Exemple #7
0
def test_effective_area_energy_fov():
    from pyirf.irf import effective_area_per_energy_and_fov
    from pyirf.simulations import SimulatedEventsInfo

    true_energy_bins = [0.1, 1.0, 10.0] * u.TeV
    # choose edges so that half are in each bin in fov
    fov_offset_bins = [0, np.arccos(0.98), np.arccos(0.96)] * u.rad
    center_1, center_2 = 0.5 * (fov_offset_bins[:-1] +
                                fov_offset_bins[1:]).to_value(u.deg)

    selected_events = QTable({
        "true_energy":
        np.concatenate([
            np.full(1000, 0.5),
            np.full(10, 5),
            np.full(500, 0.5),
            np.full(5, 5),
        ]) * u.TeV,
        "true_source_fov_offset":
        np.append(np.full(1010, center_1), np.full(505, center_2)) * u.deg,
    })

    # this should give 100000 events in the first bin and 10000 in the second
    simulation_info = SimulatedEventsInfo(
        n_showers=110000,
        energy_min=true_energy_bins[0],
        energy_max=true_energy_bins[-1],
        max_impact=100 / np.sqrt(np.pi) *
        u.m,  # this should give a nice round area
        spectral_index=-2,
        viewcone=fov_offset_bins[-1],
    )

    area = effective_area_per_energy_and_fov(selected_events, simulation_info,
                                             true_energy_bins, fov_offset_bins)

    assert area.shape == (len(true_energy_bins) - 1, len(fov_offset_bins) - 1)
    assert area.unit == u.m**2
    assert u.allclose(area[:, 0], [200, 20] * u.m**2)
    assert u.allclose(area[:, 1], [100, 10] * u.m**2)
Exemple #8
0
def test_integrate_energy_fov():
    from pyirf.simulations import SimulatedEventsInfo

    # simple case, max viewcone on bin edge
    info = SimulatedEventsInfo(
        n_showers=int(1e6),
        energy_min=100 * u.GeV,
        energy_max=10 * u.TeV,
        max_impact=500 * u.m,
        spectral_index=-2,
        viewcone=10 * u.deg,
    )

    fov_bins = [0, 10, 20] * u.deg
    energy_bins = np.geomspace(info.energy_min, info.energy_max, 20)

    n_events = info.calculate_n_showers_per_energy_and_fov(
        energy_bins, fov_bins)

    assert np.all(n_events[:, 1:] == 0)
    assert np.isclose(np.sum(n_events), int(1e6))

    # viewcone inside of bin
    info = SimulatedEventsInfo(
        n_showers=int(1e6),
        energy_min=100 * u.GeV,
        energy_max=10 * u.TeV,
        max_impact=500 * u.m,
        spectral_index=-2,
        viewcone=10 * u.deg,
    )

    fov_bins = [0, 9, 11, 20] * u.deg
    energy_bins = np.geomspace(info.energy_min, info.energy_max, 20)
    n_events = info.calculate_n_showers_per_energy_and_fov(
        energy_bins, fov_bins)

    assert np.all(n_events[:, 1:2] > 0)
    assert np.all(n_events[:, 2:] == 0)
    assert np.isclose(np.sum(n_events), int(1e6))
Exemple #9
0
def read_mc_dl2_to_QTable(filename):
    """
    Read MC DL2 files from lstchain and convert into pyirf internal format
    - astropy.table.QTable

    Parameters
    ----------
    filename: path

    Returns
    -------
    `astropy.table.QTable`, `pyirf.simulations.SimulatedEventsInfo`
    """
    
    # mapping
    name_mapping = {
        "mc_energy": "true_energy",
        "mc_alt": "true_alt",
        "mc_az": "true_az",
        "mc_alt_tel": "pointing_alt",
        "mc_az_tel": "pointing_az",
        "gammaness": "gh_score",
    }

    unit_mapping = {
        "true_energy": u.TeV,
        "reco_energy": u.TeV,
        "pointing_alt": u.rad,
        "pointing_az": u.rad,
        "true_alt": u.rad,
        "true_az": u.rad,
        "reco_alt": u.rad,
        "reco_az": u.rad,
    }

    # add alpha for source-dependent analysis
    srcdep_flag = dl2_params_src_dep_lstcam_key in get_dataset_keys(filename)
    
    if srcdep_flag:
        unit_mapping['alpha'] = u.deg

    simu_info = read_simu_info_merged_hdf5(filename)
    pyirf_simu_info = SimulatedEventsInfo(
        n_showers=simu_info.num_showers * simu_info.shower_reuse,
        energy_min=simu_info.energy_range_min,
        energy_max=simu_info.energy_range_max,
        max_impact=simu_info.max_scatter_range,
        spectral_index=simu_info.spectral_index,
        viewcone=simu_info.max_viewcone_radius,
    )

    events = pd.read_hdf(filename, key=dl2_params_lstcam_key)

    if srcdep_flag:
        events_srcdep = get_srcdep_params(filename, 'on')
        events = pd.concat([events, events_srcdep], axis=1)

    events = events.rename(columns=name_mapping)

    events = QTable.from_pandas(events)

    for k, v in unit_mapping.items():
        events[k] *= v

    return events, pyirf_simu_info
Exemple #10
0
def read_DL2_pyirf(infile, run_header):
    """
    Read a DL2 HDF5 protopipe file and adapt them to pyirf format.

    Parameters
    ----------
    infile: str or pathlib.Path
        Path to the input fits file
    run_header: dict
        Dictionary with info about simulated particle informations

    Returns
    -------
    events: astropy.QTable
        Astropy Table object containing the reconstructed events information.
    simulated_events: ``~pyirf.simulations.SimulatedEventsInfo``
    """
    log = logging.getLogger("pyirf")
    log.debug(f"Reading {infile}")
    df = pd.read_hdf(infile, "/reco_events")

    events = QTable(
        [
            list(df['obs_id']),
            list(df['event_id']),
            list(df['true_energy']) * u.TeV,
            list(df['reco_energy']) * u.TeV,
            list(df['gammaness']),
            list(df['NTels_reco']),
            list(df['reco_alt']) * u.deg,
            list(df['reco_az']) * u.deg,
            list(df['true_alt']) * u.deg,
            list(df['true_az']) * u.deg,
            list(df['pointing_alt']) * u.deg,
            list(df['pointing_az']) * u.deg,
            list(df['success']),
        ],
        names=('obs_id', 'event_id', 'true_energy', 'reco_energy', 'gh_score',
               'multiplicity', 'reco_alt', 'reco_az', 'true_alt', 'true_az',
               'pointing_alt', 'pointing_az', 'success'),
    )

    # Select only DL2 events marked as fully reconstructed
    mask = events['success']
    events = events[mask]

    n_runs = len(set(events['obs_id']))
    log.info(f"Estimated number of runs from obs ids: {n_runs}")

    n_showers = n_runs * run_header["num_use"] * run_header["num_showers"]
    log.debug(f"Number of events from n_runs and run header: {n_showers}")

    sim_info = SimulatedEventsInfo(
        n_showers=n_showers,
        energy_min=u.Quantity(run_header["e_min"], u.TeV),
        energy_max=u.Quantity(run_header["e_max"], u.TeV),
        max_impact=u.Quantity(run_header["gen_radius"], u.m),
        spectral_index=run_header["gen_gamma"],
        viewcone=u.Quantity(run_header["diff_cone"], u.deg),
    )
    return events, sim_info