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
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()
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