def test_sky_to_fov(): # test some simple cases lon, lat = sky_to_fov(1 * u.deg, 1 * u.deg, 0 * u.deg, 0 * u.deg) assert_allclose(lon.value, -1) assert_allclose(lat.value, 1) lon, lat = sky_to_fov(269 * u.deg, 0 * u.deg, 270 * u.deg, 0 * u.deg) assert_allclose(lon.value, 1) assert_allclose(lat.value, 0, atol=1e-7) lon, lat = sky_to_fov(1 * u.deg, 60 * u.deg, 0 * u.deg, 60 * u.deg) assert_allclose(lon.value, -0.5, rtol=1e-3) assert_allclose(lat.value, 0.003779, rtol=1e-3) # these are cross-checked with the # transformation as implemented in H.E.S.S. az = [51.320575, 50.899125, 52.154053, 48.233023] alt = [49.505451, 50.030165, 51.811739, 54.700102] az_pointing = [52.42056255, 52.24706061, 52.06655505, 51.86795724] alt_pointing = [51.11908203, 51.23454751, 51.35376141, 51.48385814] lon, lat = sky_to_fov(az * u.deg, alt * u.deg, az_pointing * u.deg, alt_pointing * u.deg) assert_allclose(lon.value, [0.7145614, 0.86603433, -0.05409698, 2.10295248], rtol=1e-5) assert_allclose(lat.value, [-1.60829115, -1.19643974, 0.45800984, 3.26844192], rtol=1e-5)
def make_map_background_irf(pointing, ontime, bkg, geom, oversampling=None, use_region_center=True): """Compute background map from background IRFs. Parameters ---------- pointing : `~gammapy.data.FixedPointingInfo` or `~astropy.coordinates.SkyCoord` Observation pointing - If a ``FixedPointingInfo`` is passed, FOV coordinates are properly computed. - If a ``SkyCoord`` is passed, FOV frame rotation is not taken into account. ontime : `~astropy.units.Quantity` Observation ontime. i.e. not corrected for deadtime see https://gamma-astro-data-formats.readthedocs.io/en/stable/irfs/full_enclosure/bkg/index.html#notes) bkg : `~gammapy.irf.Background3D` Background rate model geom : `~gammapy.maps.WcsGeom` Reference geometry oversampling: int Oversampling factor in energy, used for the background model evaluation. use_region_center: bool If geom is a RegionGeom, whether to just consider the values at the region center or the insted the sum over the whole region Returns ------- background : `~gammapy.maps.WcsNDMap` Background predicted counts sky cube in reco energy """ # TODO: # This implementation can be improved in two ways: # 1. Create equal time intervals between TSTART and TSTOP and sum up the # background IRF for each interval. This is instead of multiplying by # the total ontime. This then handles the rotation of the FoV. # 2. Use the pointing table (does not currently exist in CTA files) to # obtain the RA DEC and time for each interval. This then considers that # the pointing might change slightly over the observation duration # Get altaz coords for map if oversampling is not None: geom = geom.upsample(factor=oversampling, axis_name="energy") coords = {"energy": geom.axes["energy"].edges.reshape((-1, 1, 1))} if not use_region_center: image_geom = geom.to_wcs_geom().to_image() region_coord, weights = geom.get_wcs_coord_and_weights() idx = image_geom.coord_to_idx(region_coord) sky_coord = region_coord.skycoord d_omega = image_geom.solid_angle().T[idx] else: image_geom = geom.to_image() map_coord = image_geom.get_coord() sky_coord = map_coord.skycoord d_omega = image_geom.solid_angle() if bkg.is_offset_dependent: coords["offset"] = sky_coord.separation(pointing) else: if isinstance(pointing, FixedPointingInfo): altaz_coord = sky_coord.transform_to(pointing.altaz_frame) # Compute FOV coordinates of map relative to pointing fov_lon, fov_lat = sky_to_fov(altaz_coord.az, altaz_coord.alt, pointing.altaz.az, pointing.altaz.alt) else: # Create OffsetFrame frame = SkyOffsetFrame(origin=pointing) pseudo_fov_coord = sky_coord.transform_to(frame) fov_lon = pseudo_fov_coord.lon fov_lat = pseudo_fov_coord.lat coords["fov_lon"] = fov_lon coords["fov_lat"] = fov_lat bkg_de = bkg.integrate_log_log(**coords, axis_name="energy") values = (bkg_de * d_omega * ontime).to_value("") if not use_region_center: data = np.sum(weights * values, axis=2) else: data = values bkg_map = Map.from_geom(geom, data=data) if oversampling is not None: bkg_map = bkg_map.downsample(factor=oversampling, axis_name="energy") return bkg_map
def make_map_background_irf(pointing, ontime, bkg, geom, oversampling=None): """Compute background map from background IRFs. Parameters ---------- pointing : `~gammapy.data.FixedPointingInfo` or `~astropy.coordinates.SkyCoord` Observation pointing - If a ``FixedPointingInfo`` is passed, FOV coordinates are properly computed. - If a ``SkyCoord`` is passed, FOV frame rotation is not taken into account. ontime : `~astropy.units.Quantity` Observation ontime. i.e. not corrected for deadtime see https://gamma-astro-data-formats.readthedocs.io/en/stable/irfs/full_enclosure/bkg/index.html#notes) bkg : `~gammapy.irf.Background3D` Background rate model geom : `~gammapy.maps.WcsGeom` Reference geometry oversampling: int Oversampling factor in energy, used for the background model evaluation. Returns ------- background : `~gammapy.maps.WcsNDMap` Background predicted counts sky cube in reco energy """ # TODO: # This implementation can be improved in two ways: # 1. Create equal time intervals between TSTART and TSTOP and sum up the # background IRF for each interval. This is instead of multiplying by # the total ontime. This then handles the rotation of the FoV. # 2. Use the pointing table (does not currently exist in CTA files) to # obtain the RA DEC and time for each interval. This then considers that # the pointing might change slightly over the observation duration # Get altaz coords for map if oversampling is not None: geom = geom.upsample(factor=oversampling, axis="energy") map_coord = geom.to_image().get_coord() sky_coord = map_coord.skycoord if isinstance(pointing, FixedPointingInfo): altaz_coord = sky_coord.transform_to(pointing.altaz_frame) # Compute FOV coordinates of map relative to pointing fov_lon, fov_lat = sky_to_fov(altaz_coord.az, altaz_coord.alt, pointing.altaz.az, pointing.altaz.alt) else: # Create OffsetFrame frame = SkyOffsetFrame(origin=pointing) pseudo_fov_coord = sky_coord.transform_to(frame) fov_lon = pseudo_fov_coord.lon fov_lat = pseudo_fov_coord.lat energies = geom.get_axis_by_name("energy").edges bkg_de = bkg.evaluate_integrate( fov_lon=fov_lon, fov_lat=fov_lat, energy_reco=energies[:, np.newaxis, np.newaxis], ) d_omega = geom.to_image().solid_angle() data = (bkg_de * d_omega * ontime).to_value("") bkg_map = WcsNDMap(geom, data=data) if oversampling is not None: bkg_map = bkg_map.downsample(factor=oversampling, axis="energy") return bkg_map