def _scan_position(self, name, **kwargs): saved_state = LikelihoodState(self.like) skydir = kwargs.pop('skydir', self.roi[name].skydir) scan_cdelt = kwargs.pop('scan_cdelt', 0.02) nstep = kwargs.pop('nstep', 5) use_cache = kwargs.get('use_cache', True) use_pylike = kwargs.get('use_pylike', False) optimizer = kwargs.get('optimizer', {}) # Fit without source self.zero_source(name, loglevel=logging.DEBUG) fit_output_nosrc = self._fit(loglevel=logging.DEBUG, **optimizer) self.unzero_source(name, loglevel=logging.DEBUG) saved_state.restore() self.free_norm(name, loglevel=logging.DEBUG) lnlmap = WcsNDMap.create(skydir=skydir, binsz=scan_cdelt, npix=(nstep, nstep), coordsys=wcs_utils.get_coordsys(self.geom.wcs)) src = self.roi.copy_source(name) if use_cache and not use_pylike: self._create_srcmap_cache(src.name, src) coord = MapCoord.create(lnlmap.geom.get_coord(flat=True), coordsys=lnlmap.geom.coordsys) scan_skydir = coord.skycoord.icrs for lon, lat, ra, dec in zip(coord.lon, coord.lat, scan_skydir.ra.deg, scan_skydir.dec.deg): spatial_pars = {'ra': ra, 'dec': dec} self.set_source_morphology(name, spatial_pars=spatial_pars, use_pylike=use_pylike) fit_output = self._fit(loglevel=logging.DEBUG, **optimizer) lnlmap.set_by_coord((lon, lat), fit_output['loglike']) self.set_source_morphology(name, spatial_pars=src.spatial_pars, use_pylike=use_pylike) saved_state.restore() lnlmap.data -= fit_output_nosrc['loglike'] tsmap = WcsNDMap(lnlmap.geom, 2.0 * lnlmap.data) self._clear_srcmap_cache() return tsmap, fit_output_nosrc['loglike']
def test_wcsndmap_reproject_allsky_car(): geom = WcsGeom.create(binsz=10.0, proj="CAR", coordsys="CEL") m = WcsNDMap(geom) coords = m.geom.get_coord() m.set_by_coord(coords, coords[0].value) geom0 = WcsGeom.create(binsz=1.0, proj="CAR", coordsys="CEL", skydir=(180.0, 0.0), width=30.0) m0 = m.reproject(geom0, order=1) coords0 = m0.geom.get_coord() assert_allclose(m0.get_by_coord(coords0), coords0[0].value) geom1 = HpxGeom.create(binsz=5.0, coordsys="CEL") m1 = m.reproject(geom1, order=1) coords1 = m1.geom.get_coord() m = (coords1[0] > 10) & (coords1[0] < 350) assert_allclose(m1.get_by_coord((coords1[0][m], coords1[1][m])), coords1[0][m])
def test_wcsndmap_read_write_fgst(tmp_path): path = tmp_path / "tmp.fits" axis = MapAxis.from_bounds(100.0, 1000.0, 4, name="energy", unit="MeV") geom = WcsGeom.create(npix=10, binsz=1.0, proj="AIT", frame="galactic", axes=[axis]) # Test Counts Cube m = WcsNDMap(geom) m.write(path, format="fgst-ccube", overwrite=True) with fits.open(path, memmap=False) as hdulist: assert "EBOUNDS" in hdulist m2 = Map.read(path) assert m2.geom.axes[0].name == "energy" # Test Model Cube m.write(path, format="fgst-template", overwrite=True) with fits.open(path, memmap=False) as hdulist: assert "ENERGIES" in hdulist
def test_wcsndmap_read_write_fgst(tmpdir): filename = str(tmpdir / "map.fits") axis = MapAxis.from_bounds(100.0, 1000.0, 4, name="energy", unit="MeV") geom = WcsGeom.create(npix=10, binsz=1.0, proj="AIT", coordsys="GAL", axes=[axis]) # Test Counts Cube m = WcsNDMap(geom) m.write(filename, conv="fgst-ccube", overwrite=True) with fits.open(filename) as h: assert "EBOUNDS" in h m2 = Map.read(filename) assert m2.geom.axes[0].name == "energy" # Test Model Cube m.write(filename, conv="fgst-template", overwrite=True) with fits.open(filename) as h: assert "ENERGIES" in h
# We see that the geometry of the images is completely different, so we need to apply our geometric configuration to the diffuse emission file: # In[ ]: coord = maps["counts"].geom.get_coord() data = diffuse_gal.interp_by_coord( { "skycoord": coord.skycoord, "energy": coord["energy"] * maps["counts"].geom.get_axis_by_name("energy").unit, }, interp=3, ) diffuse_galactic = WcsNDMap(maps["counts"].geom, data) print("Before: \n", diffuse_gal.geom) print("Now (same as maps): \n", diffuse_galactic.geom) # In[ ]: # diffuse_galactic.slice_by_idx({"energy": 0}).plot(add_cbar=True); # this can be used to check image at different energy bins diffuse = diffuse_galactic.sum_over_axes() diffuse.smooth(5).plot(stretch="sqrt", add_cbar=True) print(diffuse) # We now multiply the exposure for this diffuse emission to subtract the result from the counts along with the background. # In[ ]: combination = diffuse * exposure
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
def test_plot_grid(): axis = MapAxis([0, 1, 2], node_type="edges") m = WcsNDMap.create(binsz=0.1 * u.deg, width=1 * u.deg, axes=[axis]) with mpl_plot_check(): m.plot_grid()
def test_wcsndmap_read_write(tmp_path, npix, binsz, frame, proj, skydir, axes): geom = WcsGeom.create(npix=npix, binsz=binsz, proj=proj, frame=frame, axes=axes) path = tmp_path / "tmp.fits" m0 = WcsNDMap(geom) fill_poisson(m0, mu=0.5) m0.write(path, overwrite=True) m1 = WcsNDMap.read(path) m2 = Map.read(path) m3 = Map.read(path, map_type="wcs") assert_allclose(m0.data, m1.data) assert_allclose(m0.data, m2.data) assert_allclose(m0.data, m3.data) m0.write(path, sparse=True, overwrite=True) m1 = WcsNDMap.read(path) m2 = Map.read(path) m3 = Map.read(path, map_type="wcs") assert_allclose(m0.data, m1.data) assert_allclose(m0.data, m2.data) assert_allclose(m0.data, m3.data) # Specify alternate HDU name for IMAGE and BANDS table m0.write(path, hdu="IMAGE", hdu_bands="TEST", overwrite=True) m1 = WcsNDMap.read(path) m2 = Map.read(path) m3 = Map.read(path, map_type="wcs")
def _make_tsmap_fast(self, prefix, **kwargs): """ Make a TS map from a GTAnalysis instance. This is a simplified implementation optimized for speed that only fits for the source normalization (all background components are kept fixed). The spectral/spatial characteristics of the test source can be defined with the src_dict argument. By default this method will generate a TS map for a point source with an index=2.0 power-law spectrum. Parameters ---------- model : dict or `~fermipy.roi_model.Source` Dictionary or Source object defining the properties of the test source that will be used in the scan. """ loglevel = kwargs.get('loglevel', self.loglevel) src_dict = copy.deepcopy(kwargs.setdefault('model', {})) src_dict = {} if src_dict is None else src_dict multithread = kwargs.setdefault('multithread', False) threshold = kwargs.setdefault('threshold', 1E-2) max_kernel_radius = kwargs.get('max_kernel_radius') loge_bounds = kwargs.setdefault('loge_bounds', None) use_pylike = kwargs.setdefault('use_pylike', True) if loge_bounds: if len(loge_bounds) != 2: raise Exception('Wrong size of loge_bounds array.') loge_bounds[0] = (loge_bounds[0] if loge_bounds[0] is not None else self.log_energies[0]) loge_bounds[1] = (loge_bounds[1] if loge_bounds[1] is not None else self.log_energies[-1]) else: loge_bounds = [self.log_energies[0], self.log_energies[-1]] # Put the test source at the pixel closest to the ROI center xpix, ypix = (np.round( (self.npix - 1.0) / 2.), np.round((self.npix - 1.0) / 2.)) cpix = np.array([xpix, ypix]) map_geom = self._geom.to_image() frame = coordsys_to_frame(map_geom.coordsys) skydir = SkyCoord(*map_geom.pix_to_coord((cpix[0], cpix[1])), frame=frame, unit='deg') skydir = skydir.transform_to('icrs') src_dict['ra'] = skydir.ra.deg src_dict['dec'] = skydir.dec.deg src_dict.setdefault('SpatialModel', 'PointSource') src_dict.setdefault('SpatialWidth', 0.3) src_dict.setdefault('Index', 2.0) src_dict.setdefault('Prefactor', 1E-13) counts = [] bkg = [] model = [] c0_map = [] eslices = [] enumbins = [] model_npred = 0 for c in self.components: imin = utils.val_to_edge(c.log_energies, loge_bounds[0])[0] imax = utils.val_to_edge(c.log_energies, loge_bounds[1])[0] eslice = slice(imin, imax) bm = c.model_counts_map( exclude=kwargs['exclude']).data.astype('float')[eslice, ...] cm = c.counts_map().data.astype('float')[eslice, ...] bkg += [bm] counts += [cm] c0_map += [cash(cm, bm)] eslices += [eslice] enumbins += [cm.shape[0]] self.add_source('tsmap_testsource', src_dict, free=True, init_source=False, use_single_psf=True, use_pylike=use_pylike, loglevel=logging.DEBUG) src = self.roi['tsmap_testsource'] # self.logger.info(str(src_dict)) modelname = utils.create_model_name(src) for c, eslice in zip(self.components, eslices): mm = c.model_counts_map('tsmap_testsource').data.astype('float')[ eslice, ...] model_npred += np.sum(mm) model += [mm] self.delete_source('tsmap_testsource', loglevel=logging.DEBUG) for i, mm in enumerate(model): dpix = 3 for j in range(mm.shape[0]): ix, iy = np.unravel_index(np.argmax(mm[j, ...]), mm[j, ...].shape) mx = mm[j, ix, :] > mm[j, ix, iy] * threshold my = mm[j, :, iy] > mm[j, ix, iy] * threshold dpix = max(dpix, np.round(np.sum(mx) / 2.)) dpix = max(dpix, np.round(np.sum(my) / 2.)) if max_kernel_radius is not None and \ dpix > int(max_kernel_radius / self.components[i].binsz): dpix = int(max_kernel_radius / self.components[i].binsz) xslice = slice(max(int(xpix - dpix), 0), min(int(xpix + dpix + 1), self.npix)) model[i] = model[i][:, xslice, xslice] ts_values = np.zeros((self.npix, self.npix)) amp_values = np.zeros((self.npix, self.npix)) wrap = functools.partial(_ts_value_newton, counts=counts, bkg=bkg, model=model, C_0_map=c0_map) if kwargs['map_skydir'] is not None: map_offset = wcs_utils.skydir_to_pix(kwargs['map_skydir'], map_geom.wcs) map_delta = 0.5 * kwargs['map_size'] / self.components[0].binsz xmin = max(int(np.ceil(map_offset[1] - map_delta)), 0) xmax = min(int(np.floor(map_offset[1] + map_delta)) + 1, self.npix) ymin = max(int(np.ceil(map_offset[0] - map_delta)), 0) ymax = min(int(np.floor(map_offset[0] + map_delta)) + 1, self.npix) xslice = slice(xmin, xmax) yslice = slice(ymin, ymax) xyrange = [range(xmin, xmax), range(ymin, ymax)] wcs = map_geom.wcs.deepcopy() npix = (ymax - ymin, xmax - xmin) crpix = (map_geom._crpix[0] - ymin, map_geom._crpix[1] - xmin) wcs.wcs.crpix[0] -= ymin wcs.wcs.crpix[1] -= xmin # FIXME: We should implement this with a proper cutout method map_geom = WcsGeom(wcs, npix, crpix=crpix) else: xyrange = [range(self.npix), range(self.npix)] xslice = slice(0, self.npix) yslice = slice(0, self.npix) positions = [] for i, j in itertools.product(xyrange[0], xyrange[1]): p = [[k // 2, i, j] for k in enumbins] positions += [p] self.logger.log(loglevel, 'Fitting test source.') if multithread: pool = Pool() results = pool.map(wrap, positions) pool.close() pool.join() else: results = map(wrap, positions) for i, r in enumerate(results): ix = positions[i][0][1] iy = positions[i][0][2] ts_values[ix, iy] = r[0] amp_values[ix, iy] = r[1] ts_values = ts_values[xslice, yslice] amp_values = amp_values[xslice, yslice] ts_map = WcsNDMap(map_geom, ts_values) sqrt_ts_map = WcsNDMap(map_geom, ts_values**0.5) npred_map = WcsNDMap(map_geom, amp_values * model_npred) amp_map = WcsNDMap(map_geom, amp_values * src.get_norm()) o = { 'name': utils.join_strings([prefix, modelname]), 'src_dict': copy.deepcopy(src_dict), 'file': None, 'ts': ts_map, 'sqrt_ts': sqrt_ts_map, 'npred': npred_map, 'amplitude': amp_map, 'loglike': -self.like(), 'config': kwargs } return o
def _make_residual_map_wcs(self, prefix, **kwargs): src_dict = copy.deepcopy(kwargs.setdefault('model', {})) exclude = kwargs.setdefault('exclude', None) loge_bounds = kwargs.setdefault('loge_bounds', None) use_weights = kwargs.setdefault('use_weights', False) if loge_bounds: if len(loge_bounds) != 2: raise Exception('Wrong size of loge_bounds array.') loge_bounds[0] = (loge_bounds[0] if loge_bounds[0] is not None else self.log_energies[0]) loge_bounds[1] = (loge_bounds[1] if loge_bounds[1] is not None else self.log_energies[-1]) else: loge_bounds = [self.log_energies[0], self.log_energies[-1]] # Put the test source at the pixel closest to the ROI center xpix, ypix = (np.round( (self.npix[0] - 1.0) / 2.), np.round((self.npix[1] - 1.0) / 2.)) cpix = np.array([xpix, ypix]) geom = self.geom.to_image() skywcs = self.geom.wcs skydir = wcs_utils.pix_to_skydir(cpix[0], cpix[1], skywcs) if src_dict is None: src_dict = {} src_dict['ra'] = skydir.ra.deg src_dict['dec'] = skydir.dec.deg src_dict.setdefault('SpatialModel', 'PointSource') src_dict.setdefault('SpatialWidth', 0.3) src_dict.setdefault('Index', 2.0) kernel = None if src_dict['SpatialModel'] == 'Gaussian': kernel = utils.make_gaussian_kernel(src_dict['SpatialWidth'], cdelt=self.binsz, npix=101) kernel /= np.sum(kernel) cpix = [50, 50] self.add_source('residmap_testsource', src_dict, free=True, init_source=False, save_source_maps=False) src = self.roi.get_source_by_name('residmap_testsource') modelname = utils.create_model_name(src) mmst = np.zeros(self.npix[::-1]) cmst = np.zeros(self.npix[::-1]) emst = np.zeros(self.npix[::-1]) sm = get_source_kernel(self, 'residmap_testsource', kernel) ts = np.zeros(self.npix[::-1]) sigma = np.zeros(self.npix[::-1]) excess = np.zeros(self.npix[::-1]) self.delete_source('residmap_testsource') for i, c in enumerate(self.components): imin = utils.val_to_edge(c.log_energies, loge_bounds[0])[0] imax = utils.val_to_edge(c.log_energies, loge_bounds[1])[0] mc = c.model_counts_map(exclude=exclude).data.astype('float') cc = c.counts_map().data.astype('float') ec = np.ones(mc.shape) if use_weights: wmap = c.weight_map().data mask = np.where(wmap > 0, 1., 0.) else: wmap = None mask = None ccs = convolve_map(cc, sm[i], cpix, imin=imin, imax=imax, wmap=wmap) mcs = convolve_map(mc, sm[i], cpix, imin=imin, imax=imax, wmap=wmap) ecs = convolve_map(ec, sm[i], cpix, imin=imin, imax=imax, wmap=wmap) cms = np.sum(ccs, axis=0) mms = np.sum(mcs, axis=0) ems = np.sum(ecs, axis=0) cmst += cms mmst += mms emst += ems # cts = 2.0 * (poisson_lnl(cms, cms) - poisson_lnl(cms, mms)) excess += cms - mms ts = 2.0 * (poisson_lnl(cmst, cmst) - poisson_lnl(cmst, mmst)) sigma = np.sqrt(ts) sigma[excess < 0] *= -1 emst /= np.max(emst) sigma_map = WcsNDMap(geom, sigma) model_map = WcsNDMap(geom, mmst / emst) data_map = WcsNDMap(geom, cmst / emst) excess_map = WcsNDMap(geom, excess / emst) o = { 'name': utils.join_strings([prefix, modelname]), 'projtype': 'WCS', 'file': None, 'sigma': sigma_map, 'model': model_map, 'data': data_map, 'excess': excess_map, 'mask': mask, 'config': kwargs } return o
exposure_hpx.plot(); # In[ ]: # For exposure, we choose a geometry with node_type='center', # whereas for counts it was node_type='edge' axis = MapAxis.from_nodes( counts.geom.axes[0].center, name="energy", unit="GeV", interp="log" ) geom = WcsGeom(wcs=counts.geom.wcs, npix=counts.geom.npix, axes=[axis]) coord = counts.geom.get_coord() data = exposure_hpx.interp_by_coord(coord) exposure = WcsNDMap(geom, data, unit=exposure_hpx.unit) print(exposure.geom) print(exposure.geom.axes[0]) # In[ ]: # Exposure is almost constant accross the field of view exposure.slice_by_idx({"energy": 0}).plot(add_cbar=True); # In[ ]: # Exposure varies very little with energy at these high energies
# # Exclusion mask currently unused. Remove here or move to later in the tutorial? # In[ ]: target_position = SkyCoord(0, 0, unit="deg", frame="galactic") on_radius = 0.2 * u.deg on_region = CircleSkyRegion(center=target_position, radius=on_radius) # In[ ]: exclusion_mask = geom.to_image().region_mask([on_region], inside=False) exclusion_mask = WcsNDMap(geom.to_image(), exclusion_mask) exclusion_mask.plot(); # In[ ]: get_ipython().run_cell_magic('time', '', 'maker = MapMaker(geom, offset_max="2 deg")\nmaps = maker.run(observations)\nprint(maps.keys())') # In[ ]: # The maps are cubes, with an energy axis. # Let's also make some images: images = maker.run_images()
# Define some observation parameters # we are not simulating many pointings / observations pointing = SkyCoord(0.2, 0.5, unit="deg", frame="galactic") livetime = 20 * u.hour exposure_map = make_map_exposure_true_energy( pointing=pointing, livetime=livetime, aeff=aeff, geom=geom ) evaluator = MapEvaluator(model=compound_model, exposure=exposure_map) npred = evaluator.compute_npred() npred_map = WcsNDMap(geom, npred) fig, ax, cbar = npred_map.sum_over_axes().plot(add_cbar=True) ax.scatter( [lon_0_1, lon_0_2, pointing.galactic.l.degree], [lat_0_1, lat_0_2, pointing.galactic.b.degree], transform=ax.get_transform("galactic"), marker="+", color="cyan", ) # plt.show() plt.clf() rng = get_random_state(42) counts = rng.poisson(npred) counts_map = WcsNDMap(geom, counts)
def test_plot(): m = WcsNDMap.create(binsz=0.1 * u.deg, width=1 * u.deg) with mpl_plot_check(): m.plot(add_cbar=True)
# Define some observation parameters # we are not simulating many pointings / observations pointing = SkyCoord(0.2, 0.5, unit="deg", frame="galactic") livetime = 20 * u.hour exposure_map = make_map_exposure_true_energy( pointing=pointing, livetime=livetime, aeff=aeff, geom=geom ) evaluator = MapEvaluator(model=models, exposure=exposure_map) npred = evaluator.compute_npred() npred_map = WcsNDMap(geom, npred) fig, ax, cbar = npred_map.sum_over_axes().plot(add_cbar=True) ax.scatter( [lon_0_1, lon_0_2, pointing.galactic.l.degree], [lat_0_1, lat_0_2, pointing.galactic.b.degree], transform=ax.get_transform("galactic"), marker="+", color="cyan", ) # plt.show() plt.clf() rng = get_random_state(42) counts = rng.poisson(npred) counts_map = WcsNDMap(geom, counts)
def test_wcsndmap_read_write(tmpdir, npix, binsz, coordsys, proj, skydir, axes): geom = WcsGeom.create(npix=npix, binsz=binsz, proj=proj, coordsys=coordsys, axes=axes) filename = str(tmpdir / "map.fits") m0 = WcsNDMap(geom) fill_poisson(m0, mu=0.5) m0.write(filename, overwrite=True) m1 = WcsNDMap.read(filename) m2 = Map.read(filename) m3 = Map.read(filename, map_type="wcs") assert_allclose(m0.data, m1.data) assert_allclose(m0.data, m2.data) assert_allclose(m0.data, m3.data) m0.write(filename, sparse=True, overwrite=True) m1 = WcsNDMap.read(filename) m2 = Map.read(filename) m3 = Map.read(filename, map_type="wcs") assert_allclose(m0.data, m1.data) assert_allclose(m0.data, m2.data) assert_allclose(m0.data, m3.data) # Specify alternate HDU name for IMAGE and BANDS table m0.write(filename, hdu="IMAGE", hdu_bands="TEST", overwrite=True) m1 = WcsNDMap.read(filename) m2 = Map.read(filename) m3 = Map.read(filename, map_type="wcs")
def test_wcsndmap_set_get_by_coord(npix, binsz, coordsys, proj, skydir, axes): geom = WcsGeom.create(npix=npix, binsz=binsz, skydir=skydir, proj=proj, coordsys=coordsys, axes=axes) m = WcsNDMap(geom) coords = m.geom.get_coord() m.set_by_coord(coords, coords[0]) assert_allclose(coords[0], m.get_by_coord(coords)) if not geom.is_allsky: coords[1][...] = 0.0 assert_allclose(np.nan * np.ones(coords[0].shape), m.get_by_coord(coords)) # Test with SkyCoords m = WcsNDMap(geom) coords = m.geom.get_coord() skydir = SkyCoord(coords[0], coords[1], unit="deg", frame=coordsys_to_frame(geom.coordsys)) skydir_cel = skydir.transform_to("icrs") skydir_gal = skydir.transform_to("galactic") m.set_by_coord((skydir_gal, ) + tuple(coords[2:]), coords[0]) assert_allclose(coords[0], m.get_by_coord(coords)) assert_allclose( m.get_by_coord((skydir_cel, ) + tuple(coords[2:])), m.get_by_coord((skydir_gal, ) + tuple(coords[2:])), ) # Test with MapCoord m = WcsNDMap(geom) coords = m.geom.get_coord() coords_dict = dict(lon=coords[0], lat=coords[1]) if axes: for i, ax in enumerate(axes): coords_dict[ax.name] = coords[i + 2] map_coords = MapCoord.create(coords_dict, coordsys=coordsys) m.set_by_coord(map_coords, coords[0]) assert_allclose(coords[0], m.get_by_coord(map_coords))
def estimate_maps(self, dataset): """Run adaptive smoothing on input Maps. Parameters ---------- dataset : `MapDataset` Dataset Returns ------- images : dict of `~gammapy.maps.WcsNDMap` Smoothed images; keys are: * 'counts' * 'background' * 'flux' (optional) * 'scales' * 'sqrt_ts'. """ dataset_image = dataset.to_image(name=dataset.name) dataset_image.models = dataset.models # extract 2d arrays counts = dataset_image.counts.data[0].astype(float) background = dataset_image.npred_background().data[0] if isinstance(dataset_image, MapDatasetOnOff): background = dataset_image.background.data[0] if dataset_image.exposure is not None: exposure = estimate_exposure_reco_energy(dataset_image, self.spectrum) else: exposure = None pixel_scale = dataset_image.counts.geom.pixel_scales.mean() kernels = self.get_kernels(pixel_scale) cubes = {} cubes["counts"] = scale_cube(counts, kernels) cubes["background"] = scale_cube(background, kernels) if exposure is not None: flux = (dataset_image.counts - background) / exposure cubes["flux"] = scale_cube(flux.data[0], kernels) cubes["sqrt_ts"] = self._sqrt_ts_cube(cubes, method=self.method) smoothed = self._reduce_cubes(cubes, kernels) result = {} geom = dataset_image.counts.geom for name, data in smoothed.items(): # set remaining pixels with sqrt_ts < threshold to mean value if name in ["counts", "background"]: mask = np.isnan(data) data[mask] = np.mean(locals()[name][mask]) result[name] = WcsNDMap(geom, data, unit="") else: unit = "deg" if name == "scale" else "" result[name] = WcsNDMap(geom, data, unit=unit) if exposure is not None: data = smoothed["flux"] mask = np.isnan(data) data[mask] = np.mean(flux.data[0][mask]) result["flux"] = WcsNDMap(geom, data, unit=flux.unit) return result
"""Example how to compute and plot reflected regions.""" import matplotlib.pyplot as plt import numpy as np from astropy.coordinates import SkyCoord, Angle from regions import CircleSkyRegion from gammapy.maps import WcsNDMap from gammapy.background import ReflectedRegionsFinder # Exclude a rectangular region exclusion_mask = WcsNDMap.create(npix=(801, 701), binsz=0.01, skydir=(83.6, 23.0)) coords = exclusion_mask.geom.get_coord().skycoord mask = (Angle("23d") < coords.dec) & (coords.dec < Angle("24d")) exclusion_mask.data = np.invert(mask) pos = SkyCoord(83.633, 22.014, unit="deg") radius = Angle(0.3, "deg") on_region = CircleSkyRegion(pos, radius) center = SkyCoord(83.633, 24, unit="deg") # One can impose a minimal distance between ON region and first reflected regions finder = ReflectedRegionsFinder( region=on_region, center=center, exclusion_mask=exclusion_mask, min_distance_input="0.2 rad", ) finder.run() fig1 = plt.figure(1) finder.plot(fig=fig1)
profile.scale_to_local_density() position = SkyCoord(0.0, 0.0, frame="galactic", unit="deg") geom = WcsGeom.create(binsz=0.05, skydir=position, width=3.0, coordsys="GAL") # In[ ]: jfactory = JFactory(geom=geom, profile=profile, distance=profiles.DMProfile.DISTANCE_GC) jfact = jfactory.compute_jfactor() # In[ ]: jfact_map = WcsNDMap(geom=geom, data=jfact.value, unit=jfact.unit) fig, ax, im = jfact_map.plot(cmap="viridis", norm=LogNorm(), add_cbar=True) plt.title("J-Factor [{}]".format(jfact_map.unit)) # 1 deg circle usually used in H.E.S.S. analyses sky_reg = CircleSkyRegion(center=position, radius=1 * u.deg) pix_reg = sky_reg.to_pixel(wcs=geom.wcs) pix_reg.plot(ax=ax, facecolor="none", edgecolor="red", label="1 deg circle") plt.legend() # In[ ]: # NOTE: https://arxiv.org/abs/1607.08142 quote 2.67e21 without the +/- 0.3 deg band around the plane total_jfact = pix_reg.to_mask().multiply(jfact).sum() print("J-factor in 1 deg circle around GC assuming a " "{} is {:.3g}".format(profile.__class__.__name__, total_jfact))
def test_wcsndmap_crop(npix, binsz, frame, proj, skydir, axes): geom = WcsGeom.create(npix=npix, binsz=binsz, proj=proj, frame=frame, axes=axes) m = WcsNDMap(geom) m.crop(1)
def test_plot_allsky(): axis = MapAxis([0, 1], node_type="edges") m = WcsNDMap.create(binsz=10 * u.deg, axes=[axis]) with mpl_plot_check(): m.plot()
def test_wcsndmap_upsample(npix, binsz, frame, proj, skydir, axes): geom = WcsGeom.create(npix=npix, binsz=binsz, proj=proj, frame=frame, axes=axes) m = WcsNDMap(geom, unit="m2") m2 = m.upsample(2, preserve_counts=True) assert_allclose(np.nansum(m.data), np.nansum(m2.data)) assert m.unit == m2.unit
npred = dataset.npred() # In[ ]: npred.sum_over_axes().plot(add_cbar=True) # In[ ]: # This one line is the core of how to simulate data when # using binned simulation / analysis: you Poisson fluctuate # npred to obtain simulated observed counts. # Compute counts as a Poisson fluctuation rng = np.random.RandomState(seed=42) counts = rng.poisson(npred.data) counts_map = WcsNDMap(geom, counts) # In[ ]: counts_map.sum_over_axes().plot() # ## Fit # # Now let's analyse the simulated data. # Here we just fit it again with the same model we had before, but you could do any analysis you like here, e.g. fit a different model, or do a region-based analysis, ... # In[ ]: # Define sky model to fit the data spatial_model = SkyGaussian(lon_0="0.1 deg", lat_0="0.1 deg", sigma="0.5 deg") spectral_model = PowerLaw(index=2,
def to_cube(image): # introduce a fake enery axis for now axis = MapAxis.from_edges([1, 10] * u.TeV, name="energy") geom = image.geom.to_cube([axis]) return WcsNDMap.from_geom(geom=geom, data=image.data)
def test_wcsndmap_set_get_by_coord(npix, binsz, frame, proj, skydir, axes): geom = WcsGeom.create(npix=npix, binsz=binsz, skydir=skydir, proj=proj, frame=frame, axes=axes) m = WcsNDMap(geom) coords = m.geom.get_coord() m.set_by_coord(coords, coords[0]) assert_allclose(coords[0].value, m.get_by_coord(coords)) # Test with SkyCoords m = WcsNDMap(geom) coords = m.geom.get_coord() skydir = coords.skycoord skydir_cel = skydir.transform_to("icrs") skydir_gal = skydir.transform_to("galactic") m.set_by_coord((skydir_gal, ) + tuple(coords[2:]), coords[0]) assert_allclose(coords[0].value, m.get_by_coord(coords)) assert_allclose( m.get_by_coord((skydir_cel, ) + tuple(coords[2:])), m.get_by_coord((skydir_gal, ) + tuple(coords[2:])), ) # Test with MapCoord m = WcsNDMap(geom) coords = m.geom.get_coord() coords_dict = dict(lon=coords[0], lat=coords[1]) if axes: for i, ax in enumerate(axes): coords_dict[ax.name] = coords[i + 2] map_coords = MapCoord.create(coords_dict, frame=frame) m.set_by_coord(map_coords, coords[0]) assert_allclose(coords[0].value, m.get_by_coord(map_coords))
def make_map(): return WcsNDMap.create(skydir=config.source_pos, proj="TAN", width=8, binsz=0.05)
def test_plot_allsky(): m = WcsNDMap.create(binsz=10 * u.deg) with mpl_plot_check(): m.plot()
on_ellipse_annulus = EllipseAnnulusSkyRegion( center=position, inner_width=1.5 * u.deg, outer_width=2.5 * u.deg, inner_height=3 * u.deg, outer_height=4 * u.deg, angle=130 * u.deg, ) another_position = SkyCoord(80.3, 22.0, unit="deg") on_rectangle = RectangleSkyRegion(center=another_position, width=2.0 * u.deg, height=4.0 * u.deg, angle=50 * u.deg) # Now we plot those regions. We first create an empty map empty_map = WcsNDMap.create(skydir=position, width=10 * u.deg, binsz=0.1 * u.deg, proj="TAN") empty_map.data += 1.0 empty_map.plot(cmap="gray", vmin=0, vmax=1) # To plot the regions, we convert them to PixelRegion with the map wcs on_circle.to_pixel(empty_map.geom.wcs).plot() on_rectangle.to_pixel(empty_map.geom.wcs).plot() on_ellipse_annulus.to_pixel(empty_map.geom.wcs).plot() plt.show()
import matplotlib.pyplot as plt from astropy.coordinates import Angle, SkyCoord from regions import CircleSkyRegion from gammapy.makers import ReflectedRegionsFinder from gammapy.maps import WcsNDMap, RegionGeom # Exclude a rectangular region exclusion_mask = WcsNDMap.create(npix=(801, 701), binsz=0.01, skydir=(83.6, 23.0)) coords = exclusion_mask.geom.get_coord().skycoord data = (Angle("23 deg") < coords.dec) & (coords.dec < Angle("24 deg")) exclusion_mask.data = ~data pos = SkyCoord(83.633, 22.014, unit="deg") radius = Angle(0.3, "deg") on_region = CircleSkyRegion(pos, radius) center = SkyCoord(83.633, 24, unit="deg") # One can impose a minimal distance between ON region and first reflected regions finder = ReflectedRegionsFinder( region=on_region, center=center, exclusion_mask=exclusion_mask, min_distance_input="0.2 rad", ) regions = finder.run() fig, axes = plt.subplots( ncols=3,
def test_map_fill_events_keyerror(events): axis = MapAxis([0, 1, 2], name="nokey") m = WcsNDMap.create(binsz=0.1, npix=10, axes=[axis]) with pytest.raises(KeyError): m.fill_events(events)
def estimate_maps(self, counts, background, exposure=None): """ Run adaptive smoothing on input Maps. Parameters ---------- counts : `~gammapy.maps.Map` counts map background : `~gammapy.maps.Map` estimated background counts map exposure : `~gammapy.maps.Map` exposure map. If set, it will produce a flux smoothed map. Returns ------- images : dict of `~gammapy.maps.WcsNDMap` Smoothed images; keys are: * 'counts' * 'background' * 'flux' (optional) * 'scales' * 'significance'. """ pixel_scale = counts.geom.pixel_scales.mean() kernels = self.kernels(pixel_scale) cubes = {} cubes["counts"] = scale_cube(counts.data, kernels) if background is not None: cubes["background"] = scale_cube(background.data, kernels) else: # TODO: Estimate background with asmooth method raise ValueError("Background estimation required.") if exposure is not None: flux = (counts - background) / exposure cubes["flux"] = scale_cube(flux.data, kernels) cubes["significance"] = self._significance_cube(cubes, method=self.method) smoothed = self._reduce_cubes(cubes, kernels) result = {} for key in ["counts", "background", "scale", "significance"]: data = smoothed[key] # set remaining pixels with significance < threshold to mean value if key in ["counts", "background"]: mask = np.isnan(data) data[mask] = np.mean(locals()[key].data[mask]) result[key] = WcsNDMap(counts.geom, data, unit=counts.unit) else: result[key] = WcsNDMap(counts.geom, data, unit="deg") if exposure is not None: data = smoothed["flux"] mask = np.isnan(data) data[mask] = np.mean(flux.data[mask]) result["flux"] = WcsNDMap(counts.geom, data, unit=flux.unit) return result
def simulate_dataset( skymodel, geom, pointing, irfs, livetime=1 * u.h, offset=0 * u.deg, max_radius=0.8 * u.deg, random_state="random-seed", ): """Simulate a 3D dataset. Simulate a source defined with a sky model for a given pointing, geometry and irfs for a given exposure time. This will return a dataset object which includes the counts cube, the exposure cube, the psf cube, the background model and the sky model. Parameters ---------- skymodel : `~gammapy.modeling.models.SkyModel` Background model map geom : `~gammapy.maps.WcsGeom` Geometry object for the observation pointing : `~astropy.coordinates.SkyCoord` Pointing position irfs : dict Irfs used for simulating the observation livetime : `~astropy.units.Quantity` Livetime exposure of the simulated observation offset : `~astropy.units.Quantity` Offset from the center of the pointing position. This is used for the PSF and Edisp estimation max_radius : `~astropy.coordinates.Angle` The maximum radius of the PSF kernel. random_state: {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`} Defines random number generator initialisation. Returns ------- dataset : `~gammapy.cube.MapDataset` A dataset of the simulated observation. """ background = make_map_background_irf( pointing=pointing, ontime=livetime, bkg=irfs["bkg"], geom=geom ) background_model = BackgroundModel(background) psf = irfs["psf"].to_energy_dependent_table_psf(theta=offset) psf_kernel = PSFKernel.from_table_psf(psf, geom, max_radius=max_radius) exposure = make_map_exposure_true_energy( pointing=pointing, livetime=livetime, aeff=irfs["aeff"], geom=geom ) if "edisp" in irfs: energy = geom.axes[0].edges edisp = irfs["edisp"].to_energy_dispersion(offset, e_reco=energy, e_true=energy) else: edisp = None dataset = MapDataset( model=skymodel, exposure=exposure, background_model=background_model, psf=psf_kernel, edisp=edisp, ) npred_map = dataset.npred() rng = get_random_state(random_state) counts = rng.poisson(npred_map.data) dataset.counts = WcsNDMap(geom, counts) return dataset