Пример #1
0
def make_catalog_random_positions_sphere(size=100,
                                         distance_min="0 pc",
                                         distance_max="1 pc",
                                         random_state="random-seed"):
    """Sample random source locations in a sphere.

    This can be used to generate an isotropic source population
    in a sphere, e.g. to represent extra-galactic sources.

    Parameters
    ----------
    size : int
        Number of sources
    distance_min, distance_max : `~astropy.units.Quantity`
        Minimum and maximum distance
    random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`}
        Defines random number generator initialisation.
        Passed to `~gammapy.utils.random.get_random_state`.

    Returns
    -------
    catalog : `~astropy.table.Table`
        Table with 3D position spherical coordinates.
        Columns: lon (deg), lat (deg), distance(pc)
    """
    distance_min = Quantity(distance_min).to_value("pc")
    distance_max = Quantity(distance_max).to_value("pc")
    random_state = get_random_state(random_state)

    lon, lat = sample_sphere(size, random_state=random_state)
    distance = sample_sphere_distance(distance_min, distance_max, size,
                                      random_state)

    table = Table()

    table["lon"] = Column(lon, unit="rad", description="Spherical coordinate")
    table["lat"] = Column(lat, unit="rad", description="Spherical coordinate")
    table["distance"] = Column(distance,
                               unit="pc",
                               description="Spherical coordinate")

    return table
Пример #2
0
def test_sample_sphere():
    random_state = np.random.RandomState(seed=0)

    # test general case
    lon, lat = sample_sphere(size=2, random_state=random_state)
    assert_quantity_allclose(lon, Angle([3.44829694, 4.49366732], "radian"))
    assert_quantity_allclose(lat, Angle([0.20700192, 0.08988736], "radian"))

    # test specify a limited range
    lon_range = Angle([40.0, 45.0], "deg")
    lat_range = Angle([10.0, 15.0], "deg")
    lon, lat = sample_sphere(
        size=10, lon_range=lon_range, lat_range=lat_range, random_state=random_state
    )
    assert ((lon_range[0] <= lon) & (lon < lon_range[1])).all()
    assert ((lat_range[0] <= lat) & (lat < lat_range[1])).all()

    # test lon within (-180, 180) deg range
    lon_range = Angle([-40.0, 0.0], "deg")
    lon, lat = sample_sphere(size=10, lon_range=lon_range, random_state=random_state)
    assert ((lon_range[0] <= lon) & (lon < lon_range[1])).all()
    lat_range = Angle([-90.0, 90.0], "deg")
    assert ((lat_range[0] <= lat) & (lat < lat_range[1])).all()

    # test lon range explicitly (0, 360) deg
    lon_range = Angle([0.0, 360.0], "deg")
    lon, lat = sample_sphere(size=100, lon_range=lon_range, random_state=random_state)
    # test values in the desired range
    lat_range = Angle([-90.0, 90.0], "deg")
    assert ((lon_range[0] <= lon) & (lon < lon_range[1])).all()
    assert ((lat_range[0] <= lat) & (lat < lat_range[1])).all()
    # test if values are distributed along the whole range
    nbins = 4
    lon_delta = (lon_range[1] - lon_range[0]) / nbins
    lat_delta = (lat_range[1] - lat_range[0]) / nbins
    for i in np.arange(nbins):
        assert (
            (lon_range[0] + i * lon_delta <= lon)
            & (lon < lon_range[0] + (i + 1) * lon_delta)
        ).any()
        assert (
            (lat_range[0] + i * lat_delta <= lat)
            & (lat < lat_range[0] + (i + 1) * lat_delta)
        ).any()

    # test lon range explicitly (-180, 180) deg
    lon_range = Angle([-180.0, 180.0], "deg")
    lon, lat = sample_sphere(size=100, lon_range=lon_range, random_state=random_state)
    # test values in the desired range
    lat_range = Angle([-90.0, 90.0], "deg")
    assert ((lon_range[0] <= lon) & (lon < lon_range[1])).all()
    assert ((lat_range[0] <= lat) & (lat < lat_range[1])).all()
    # test if values are distributed along the whole range
    nbins = 4
    lon_delta = (lon_range[1] - lon_range[0]) / nbins
    lat_delta = (lat_range[1] - lat_range[0]) / nbins
    for i in np.arange(nbins):
        assert (
            (lon_range[0] + i * lon_delta <= lon)
            & (lon < lon_range[0] + (i + 1) * lon_delta)
        ).any()
        assert (
            (lat_range[0] + i * lat_delta <= lat)
            & (lat < lat_range[0] + (i + 1) * lat_delta)
        ).any()

    # test box around Galactic center
    lon_range = Angle([-5.0, 5.0], "deg")
    lon, lat = sample_sphere(size=10, lon_range=lon_range, random_state=random_state)
    # test if values are distributed along the whole range
    nbins = 2
    lon_delta = (lon_range[1] - lon_range[0]) / nbins
    for i in np.arange(nbins):
        assert (
            (lon_range[0] + i * lon_delta <= lon)
            & (lon < lon_range[0] + (i + 1) * lon_delta)
        ).any()

    # test box around Galactic anticenter
    lon_range = Angle([175.0, 185.0], "deg")
    lon, lat = sample_sphere(size=10, lon_range=lon_range, random_state=random_state)
    # test if values are distributed along the whole range
    nbins = 2
    lon_delta = (lon_range[1] - lon_range[0]) / nbins
    for i in np.arange(nbins):
        assert (
            (lon_range[0] + i * lon_delta <= lon)
            & (lon < lon_range[0] + (i + 1) * lon_delta)
        ).any()
Пример #3
0
def make_test_observation_table(
    observatory_name="hess",
    n_obs=10,
    az_range=Angle([0, 360], "deg"),
    alt_range=Angle([45, 90], "deg"),
    date_range=(Time("2010-01-01"), Time("2015-01-01")),
    use_abs_time=False,
    n_tels_range=(3, 4),
    random_state="random-seed",
):
    """Make a test observation table.
    Create an observation table following a specific pattern.
    For the moment, only random observation tables are created.
    The observation table is created according to a specific
    observatory, and randomizing the observation pointingpositions
    in a specified az-alt range.
    If a *date_range* is specified, the starting time
    of the observations will be restricted to the specified interval.
    These parameters are interpreted as date, the precise hour of the
    day is ignored, unless the end date is closer than 1 day to the
    starting date, in which case, the precise time of the day is also
    considered.
    In addition, a range can be specified for the number of telescopes.
    Parameters
    ----------
    observatory_name : str, optional
        Name of the observatory; a list of choices is given in
        `~gammapy.data.observatory_locations`.
    n_obs : int, optional
        Number of observations for the obs table.
    az_range : `~astropy.coordinates.Angle`, optional
        Azimuth angle range (start, end) for random generation of
        observation pointing positions.
    alt_range : `~astropy.coordinates.Angle`, optional
        Altitude angle range (start, end) for random generation of
        observation pointing positions.
    date_range : `~astropy.time.Time`, optional
        Date range (start, end) for random generation of observation
        start time.
    use_abs_time : bool, optional
        Use absolute UTC times instead of [MET]_ seconds after the reference.
    n_tels_range : int, optional
        Range (start, end) of number of telescopes participating in
        the observations.
    random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`}, optional
        Defines random number generator initialisation.
        Passed to `~gammapy.utils.random.get_random_state`.
    Returns
    -------
    obs_table : `~gammapy.data.ObservationTable`
        Observation table.
    """
    random_state = get_random_state(random_state)

    n_obs_start = 1

    obs_table = ObservationTable()

    # build a time reference as the start of 2010
    dateref = Time("2010-01-01T00:00:00")
    dateref_mjd_fra, dateref_mjd_int = np.modf(dateref.mjd)

    # define table header
    obs_table.meta["OBSERVATORY_NAME"] = observatory_name
    obs_table.meta["MJDREFI"] = dateref_mjd_int
    obs_table.meta["MJDREFF"] = dateref_mjd_fra
    obs_table.meta["TIMESYS"] = "TT"
    obs_table.meta["TIMEUNIT"] = "s"
    obs_table.meta["TIMEREF"] = "LOCAL"
    if use_abs_time:
        # show the observation times in UTC
        obs_table.meta["TIME_FORMAT"] = "absolute"
    else:
        # show the observation times in seconds after the reference
        obs_table.meta["TIME_FORMAT"] = "relative"
    header = obs_table.meta

    # obs id
    obs_id = np.arange(n_obs_start, n_obs_start + n_obs)
    obs_table["OBS_ID"] = obs_id

    # obs time: 30 min
    ontime = Quantity(30.0 * np.ones_like(obs_id), "minute").to("second")
    obs_table["ONTIME"] = ontime

    # livetime: 25 min
    time_live = Quantity(25.0 * np.ones_like(obs_id), "minute").to("second")
    obs_table["LIVETIME"] = time_live

    # start time
    #  - random points between the start of 2010 and the end of 2014 (unless
    # otherwise specified)
    #  - using the start of 2010 as a reference time for the header of the table
    #  - observations restrict to night time (only if specified time interval is
    # more than 1 day)
    #  - considering start of astronomical day at midday: implicit in setting
    # the start of the night, when generating random night hours
    datestart = date_range[0]
    dateend = date_range[1]
    time_start = random_state.uniform(datestart.mjd, dateend.mjd, len(obs_id))
    time_start = Time(time_start, format="mjd", scale="utc")

    # check if time interval selected is more than 1 day
    if (dateend - datestart).jd > 1.0:
        # keep only the integer part (i.e. the day, not the fraction of the day)
        time_start_f, time_start_i = np.modf(time_start.mjd)
        time_start = Time(time_start_i, format="mjd", scale="utc")

        # random generation of night hours: 6 h (from 22 h to 4 h), leaving 1/2 h
        # time for the last run to finish
        night_start = Quantity(22.0, "hour")
        night_duration = Quantity(5.5, "hour")
        hour_start = random_state.uniform(
            night_start.value, night_start.value + night_duration.value, len(obs_id)
        )
        hour_start = Quantity(hour_start, "hour")

        # add night hour to integer part of MJD
        time_start += hour_start

    if use_abs_time:
        # show the observation times in UTC
        time_start = Time(time_start.isot)
    else:
        # show the observation times in seconds after the reference
        time_start = time_relative_to_ref(time_start, header)
        # converting to quantity (better treatment of units)
        time_start = Quantity(time_start.sec, "second")

    obs_table["TSTART"] = time_start

    # stop time
    # calculated as TSTART + ONTIME
    if use_abs_time:
        time_stop = Time(obs_table["TSTART"])
        time_stop += TimeDelta(obs_table["ONTIME"])
    else:
        time_stop = TimeDelta(obs_table["TSTART"])
        time_stop += TimeDelta(obs_table["ONTIME"])
        # converting to quantity (better treatment of units)
        time_stop = Quantity(time_stop.sec, "second")

    obs_table["TSTOP"] = time_stop

    # az, alt
    # random points in a portion of sphere; default: above 45 deg altitude
    az, alt = sample_sphere(
        size=len(obs_id),
        lon_range=az_range,
        lat_range=alt_range,
        random_state=random_state,
    )
    az = Angle(az, "deg")
    alt = Angle(alt, "deg")
    obs_table["AZ"] = az
    obs_table["ALT"] = alt

    # RA, dec
    # derive from az, alt taking into account that alt, az represent the values
    # at the middle of the observation, i.e. at time_ref + (TIME_START + TIME_STOP)/2
    # (or better: time_ref + TIME_START + (TIME_OBSERVATION/2))
    # in use_abs_time mode, the time_ref should not be added, since it's already included
    # in TIME_START and TIME_STOP
    az = Angle(obs_table["AZ"])
    alt = Angle(obs_table["ALT"])
    if use_abs_time:
        obstime = Time(obs_table["TSTART"])
        obstime += TimeDelta(obs_table["ONTIME"]) / 2.0
    else:
        obstime = time_ref_from_dict(obs_table.meta)
        obstime += TimeDelta(obs_table["TSTART"])
        obstime += TimeDelta(obs_table["ONTIME"]) / 2.0
    location = observatory_locations[observatory_name]
    altaz_frame = AltAz(obstime=obstime, location=location)
    alt_az_coord = SkyCoord(az, alt, frame=altaz_frame)
    sky_coord = alt_az_coord.transform_to("icrs")
    obs_table["RA_PNT"] = sky_coord.ra
    obs_table["DEC_PNT"] = sky_coord.dec

    # positions

    # number of telescopes
    # random integers in a specified range; default: between 3 and 4
    n_tels = random_state.randint(n_tels_range[0], n_tels_range[1] + 1, len(obs_id))
    obs_table["N_TELS"] = n_tels

    # muon efficiency
    # random between 0.6 and 1.0
    muoneff = random_state.uniform(low=0.6, high=1.0, size=len(obs_id))
    obs_table["MUONEFF"] = muoneff

    return obs_table