Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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