def estimate_pad_width(self, dataset, kernel=None): """Estimate pad width of the dataset Parameters ---------- dataset : `MapDataset` Input MapDataset. kernel : `WcsNDMap` Source model kernel. Returns ------- pad_width : tuple Padding width """ if kernel is None: kernel = self.estimate_kernel(dataset=dataset) geom = dataset.counts.geom.to_image() geom_kernel = kernel.geom.to_image() pad_width = np.array(geom_kernel.data_shape) // 2 if self.downsampling_factor and self.downsampling_factor > 1: shape = tuple(np.array(geom.data_shape) + 2 * pad_width) pad_width = symmetric_crop_pad_width(geom.data_shape, shape_2N(shape))[0] return tuple(pad_width)
def estimate_pad_width(self, dataset): """Estimate pad width of the dataset Parameters ---------- dataset : `~gammapy.datasets.MapDataset` Input MapDataset. Returns ------- pad_width : tuple Padding width """ geom = dataset.counts.geom.to_image() geom_kernel = geom.to_odd_npix(max_radius=self.kernel_width / 2) pad_width = np.array(geom_kernel.data_shape) // 2 if self.downsampling_factor and self.downsampling_factor > 1: shape = tuple(np.array(geom.data_shape) + 2 * pad_width) pad_width = symmetric_crop_pad_width(geom.data_shape, shape_2N(shape))[0] return tuple(pad_width)
def run(self, dataset, steps="all"): """ Run TS map estimation. Requires a MapDataset with counts, exposure and background_model properly set to run. Parameters ---------- dataset : `~gammapy.datasets.MapDataset` Input MapDataset. steps : list of str or 'all' Which maps to compute. Available options are: * "ts": estimate delta TS and significance (sqrt_ts) * "flux-err": estimate symmetric error on flux. * "flux-ul": estimate upper limits on flux. By default all steps are executed. Returns ------- maps : dict Dictionary containing result maps. Keys are: * ts : delta TS map * sqrt_ts : sqrt(delta TS), or significance map * flux : flux map * flux_err : symmetric error map * flux_ul : upper limit map """ p = self.parameters # First create 2D map arrays counts = dataset.counts.sum_over_axes(keepdims=False) background = dataset.npred().sum_over_axes(keepdims=False) exposure = dataset.exposure.sum_over_axes(keepdims=False) kernel = self.get_kernel(dataset) if dataset.mask is not None: mask = counts.copy( data=(dataset.mask.sum(axis=0) > 0).astype("int")) else: mask = counts.copy(data=np.ones_like(counts).astype("int")) if self.downsampling_factor: shape = counts.data.shape pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0] counts = counts.pad(pad_width).downsample(self.downsampling_factor, preserve_counts=True) background = background.pad(pad_width).downsample( self.downsampling_factor, preserve_counts=True) exposure = exposure.pad(pad_width).downsample( self.downsampling_factor, preserve_counts=False) mask = mask.pad(pad_width).downsample(self.downsampling_factor, preserve_counts=False) mask.data = mask.data.astype("int") mask.data &= self.mask_default(exposure, background, kernel.data).data if steps == "all": steps = ["ts", "sqrt_ts", "flux", "flux_err", "flux_ul", "niter"] result = {} for name in steps: data = np.nan * np.ones_like(counts.data) result[name] = counts.copy(data=data) flux_map = self.flux_default(dataset, kernel.data) if p["threshold"] or p["method"] == "root newton": flux = flux_map.data else: flux = None # prepare dtype for cython methods counts_array = counts.data.astype(float) background_array = background.data.astype(float) exposure_array = exposure.data.astype(float) # Compute null statistics per pixel for the whole image c_0 = cash(counts_array, background_array) error_method = p["error_method"] if "flux_err" in steps else "none" ul_method = p["ul_method"] if "flux_ul" in steps else "none" wrap = functools.partial( _ts_value, counts=counts_array, exposure=exposure_array, background=background_array, c_0=c_0, kernel=kernel.data, flux=flux, method=p["method"], error_method=error_method, threshold=p["threshold"], error_sigma=p["error_sigma"], ul_method=ul_method, ul_sigma=p["ul_sigma"], rtol=p["rtol"], ) x, y = np.where(np.squeeze(mask.data)) positions = list(zip(x, y)) results = list(map(wrap, positions)) # Set TS values at given positions j, i = zip(*positions) for name in ["ts", "flux", "niter"]: result[name].data[j, i] = [_[name] for _ in results] if "flux_err" in steps: result["flux_err"].data[j, i] = [_["flux_err"] for _ in results] if "flux_ul" in steps: result["flux_ul"].data[j, i] = [_["flux_ul"] for _ in results] # Compute sqrt(TS) values if "sqrt_ts" in steps: result["sqrt_ts"] = self.sqrt_ts(result["ts"]) if self.downsampling_factor: for name in steps: order = 0 if name == "niter" else 1 result[name] = result[name].upsample( factor=self.downsampling_factor, preserve_counts=False, order=order) result[name] = result[name].crop(crop_width=pad_width) # Set correct units if "flux" in steps: result["flux"].unit = flux_map.unit if "flux_err" in steps: result["flux_err"].unit = flux_map.unit if "flux_ul" in steps: result["flux_ul"].unit = flux_map.unit return result
def run(self, dataset): """ Run TS map estimation. Requires a MapDataset with counts, exposure and background_model properly set to run. Parameters ---------- dataset : `~gammapy.datasets.MapDataset` Input MapDataset. Returns ------- maps : dict Dictionary containing result maps. Keys are: * ts : delta TS map * sqrt_ts : sqrt(delta TS), or significance map * flux : flux map * flux_err : symmetric error map * flux_ul : upper limit map """ dataset_models = dataset.models if self.downsampling_factor: shape = dataset.counts.geom.to_image().data_shape pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0] dataset = dataset.pad(pad_width).downsample( self.downsampling_factor) # TODO: add support for joint likelihood fitting to TSMapEstimator datasets = Datasets(dataset) if self.energy_edges is None: energy_axis = dataset.counts.geom.axes["energy"] energy_edges = u.Quantity( [energy_axis.edges[0], energy_axis.edges[-1]]) else: energy_edges = self.energy_edges results = [] for energy_min, energy_max in zip(energy_edges[:-1], energy_edges[1:]): sliced_dataset = datasets.slice_by_energy(energy_min, energy_max)[0] if self.sum_over_energy_groups: sliced_dataset = sliced_dataset.to_image() sliced_dataset.models = dataset_models result = self.estimate_flux_map(sliced_dataset) results.append(result) result_all = {} for name in self.selection_all: map_all = Map.from_images(images=[_[name] for _ in results]) if self.downsampling_factor: order = 0 if name == "niter" else 1 map_all = map_all.upsample(factor=self.downsampling_factor, preserve_counts=False, order=order) map_all = map_all.crop(crop_width=pad_width) result_all[name] = map_all result_all["sqrt_ts"] = self.estimate_sqrt_ts(result_all["ts"], result_all["norm"]) return FluxMaps(data=result_all, reference_model=self.model, gti=dataset.gti)
def run(self, dataset, kernel, which="all", downsampling_factor=None): """ Run TS map estimation. Requires a MapDataset with counts, exposure and background_model properly set to run. Parameters ---------- kernel : `astropy.convolution.Kernel2D` or 2D `~numpy.ndarray` Source model kernel. which : list of str or 'all' Which maps to compute. downsampling_factor : int Sample down the input maps to speed up the computation. Only integer values that are a multiple of 2 are allowed. Note that the kernel is not sampled down, but must be provided with the downsampled bin size. Returns ------- maps : dict Result maps. """ p = self.parameters if (np.array(kernel.shape) > np.array(dataset.counts.data.shape[1:])).any(): raise ValueError( "Kernel shape larger than map shape, please adjust" " size of the kernel" ) # First create 2D map arrays counts = dataset.counts.sum_over_axes(keepdims=False) background = dataset.npred().sum_over_axes(keepdims=False) exposure = dataset.exposure.sum_over_axes(keepdims=False) if dataset.mask is not None: mask = counts.copy(data=(dataset.mask.sum(axis=0) > 0).astype("int")) else: mask = counts.copy(data=np.ones_like(counts).astype("int")) if downsampling_factor: shape = counts.data.shape pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0] counts = counts.pad(pad_width).downsample( downsampling_factor, preserve_counts=True ) background = background.pad(pad_width).downsample( downsampling_factor, preserve_counts=True ) exposure = exposure.pad(pad_width).downsample( downsampling_factor, preserve_counts=False ) mask = mask.pad(pad_width).downsample( downsampling_factor, preserve_counts=False ) mask.data = mask.data.astype("int") mask.data &= self.mask_default(exposure, background, kernel).data if not isinstance(kernel, Kernel2D): kernel = CustomKernel(kernel) if which == "all": which = ["ts", "sqrt_ts", "flux", "flux_err", "flux_ul", "niter"] result = {} for name in which: data = np.nan * np.ones_like(counts.data) result[name] = counts.copy(data=data) flux_map = self.flux_default(dataset, kernel) if p["threshold"] or p["method"] == "root newton": flux = flux_map.data else: flux = None # prepare dtype for cython methods counts_array = counts.data.astype(float) background_array = background.data.astype(float) exposure_array = exposure.data.astype(float) # Compute null statistics per pixel for the whole image c_0 = cash(counts_array, background_array) error_method = p["error_method"] if "flux_err" in which else "none" ul_method = p["ul_method"] if "flux_ul" in which else "none" wrap = functools.partial( _ts_value, counts=counts_array, exposure=exposure_array, background=background_array, c_0=c_0, kernel=kernel, flux=flux, method=p["method"], error_method=error_method, threshold=p["threshold"], error_sigma=p["error_sigma"], ul_method=ul_method, ul_sigma=p["ul_sigma"], rtol=p["rtol"], ) x, y = np.where(np.squeeze(mask.data)) positions = list(zip(x, y)) results = list(map(wrap, positions)) # Set TS values at given positions j, i = zip(*positions) for name in ["ts", "flux", "niter"]: result[name].data[j, i] = [_[name] for _ in results] if "flux_err" in which: result["flux_err"].data[j, i] = [_["flux_err"] for _ in results] if "flux_ul" in which: result["flux_ul"].data[j, i] = [_["flux_ul"] for _ in results] # Compute sqrt(TS) values if "sqrt_ts" in which: result["sqrt_ts"] = self.sqrt_ts(result["ts"]) if downsampling_factor: for name in which: order = 0 if name == "niter" else 1 result[name] = result[name].upsample( factor=downsampling_factor, preserve_counts=False, order=order ) result[name] = result[name].crop(crop_width=pad_width) # Set correct units if "flux" in which: result["flux"].unit = flux_map.unit if "flux_err" in which: result["flux_err"].unit = flux_map.unit if "flux_ul" in which: result["flux_ul"].unit = flux_map.unit return result
def run(self, maps, kernel, which="all", downsampling_factor=None): """ Run TS map estimation. Requires "counts", "exposure" and "background" map to run. Parameters ---------- maps : dict Input sky maps. kernel : `astropy.convolution.Kernel2D` or 2D `~numpy.ndarray` Source model kernel. which : list of str or 'all' Which maps to compute. downsampling_factor : int Sample down the input maps to speed up the computation. Only integer values that are a multiple of 2 are allowed. Note that the kernel is not sampled down, but must be provided with the downsampled bin size. Returns ------- maps : dict Result maps. """ p = self.parameters if (np.array(kernel.shape) > np.array(maps["counts"].data.shape)).any(): raise ValueError( "Kernel shape larger than map shape, please adjust" " size of the kernel" ) if downsampling_factor: maps_downsampled = {} shape = maps["counts"].data.shape pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0] for name, map_ in maps.items(): preserve_counts = name in ["counts", "background", "exclusion"] maps_downsampled[name] = map_.pad(pad_width).downsample( downsampling_factor, preserve_counts=preserve_counts ) maps = maps_downsampled if not isinstance(kernel, Kernel2D): kernel = CustomKernel(kernel) if which == "all": which = ["ts", "sqrt_ts", "flux", "flux_err", "flux_ul", "niter"] result = {} for name in which: data = np.nan * np.ones_like(maps["counts"].data) result[name] = maps["counts"].copy(data=data) mask = self.mask_default(maps, kernel) if "mask" in maps: mask.data &= maps["mask"].data if p["threshold"] or p["method"] == "root newton": flux = self.flux_default(maps, kernel).data else: flux = None # prepare dtype for cython methods counts = maps["counts"].data.astype(float) background = maps["background"].data.astype(float) exposure = maps["exposure"].data.astype(float) # Compute null statistics per pixel for the whole image c_0 = cash(counts, background) error_method = p["error_method"] if "flux_err" in which else "none" ul_method = p["ul_method"] if "flux_ul" in which else "none" wrap = functools.partial( _ts_value, counts=counts, exposure=exposure, background=background, c_0=c_0, kernel=kernel, flux=flux, method=p["method"], error_method=error_method, threshold=p["threshold"], error_sigma=p["error_sigma"], ul_method=ul_method, ul_sigma=p["ul_sigma"], rtol=p["rtol"], ) x, y = np.where(mask.data) positions = list(zip(x, y)) with contextlib.closing(multiprocessing.Pool(processes=p["n_jobs"])) as pool: log.info("Using {} jobs to compute TS map.".format(p["n_jobs"])) results = pool.map(wrap, positions) pool.join() # Set TS values at given positions j, i = zip(*positions) for name in ["ts", "flux", "niter"]: result[name].data[j, i] = [_[name] for _ in results] if "flux_err" in which: result["flux_err"].data[j, i] = [_["flux_err"] for _ in results] if "flux_ul" in which: result["flux_ul"].data[j, i] = [_["flux_ul"] for _ in results] # Compute sqrt(TS) values if "sqrt_ts" in which: result["sqrt_ts"] = self.sqrt_ts(result["ts"]) if downsampling_factor: for name in which: order = 0 if name == "niter" else 1 result[name] = result[name].upsample( factor=downsampling_factor, preserve_counts=False, order=order ) result[name] = result[name].crop(crop_width=pad_width) return result
def test_shape_2N(): shape = (34, 89, 120, 444) expected_shape = (40, 96, 128, 448) assert expected_shape == shape_2N(shape=shape, N=3)
def run(self, dataset): """ Run TS map estimation. Requires a MapDataset with counts, exposure and background_model properly set to run. Parameters ---------- dataset : `~gammapy.datasets.MapDataset` Input MapDataset. Returns ------- maps : dict Dictionary containing result maps. Keys are: * ts : delta TS map * sqrt_ts : sqrt(delta TS), or significance map * flux : flux map * flux_err : symmetric error map * flux_ul : upper limit map """ if self.downsampling_factor: shape = dataset.counts.geom.to_image().data_shape pad_width = symmetric_crop_pad_width(shape, shape_2N(shape))[0] dataset = dataset.pad(pad_width).downsample( self.downsampling_factor) # First create 2D map arrays counts = dataset.counts background = dataset.npred() exposure = self.estimate_exposure(dataset) kernel = self.estimate_kernel(dataset) mask = self.estimate_mask_default(dataset, kernel.data) flux = self.estimate_flux_default(dataset, kernel.data, exposure=exposure) wrap = functools.partial(_ts_value, counts=counts.data.astype(float), exposure=exposure.data.astype(float), background=background.data.astype(float), kernel=kernel.data, flux=flux.data, flux_estimator=self._flux_estimator) x, y = np.where(np.squeeze(mask.data)) positions = list(zip(x, y)) if self.n_jobs is None: results = list(map(wrap, positions)) else: with contextlib.closing(Pool(processes=self.n_jobs)) as pool: log.info("Using {} jobs to compute TS map.".format( self.n_jobs)) results = pool.map(wrap, positions) pool.join() names = ["ts", "flux", "niter", "flux_err"] if "errn-errp" in self.selection_optional: names += ["flux_errp", "flux_errn"] if "ul" in self.selection_optional: names += ["flux_ul"] result = {} geom = counts.geom.to_image() j, i = zip(*positions) for name in names: unit = 1 / exposure.unit if "flux" in name else "" m = Map.from_geom(geom=geom, data=np.nan, unit=unit) m.data[j, i] = [_[name.replace("flux", "norm")] for _ in results] if "flux" in name: m.data *= self._flux_estimator.flux_ref result[name] = m result["sqrt_ts"] = self.estimate_sqrt_ts(result["ts"]) if self.downsampling_factor: for name in names: order = 0 if name == "niter" else 1 result[name] = result[name].upsample( factor=self.downsampling_factor, preserve_counts=False, order=order) result[name] = result[name].crop(crop_width=pad_width) return result