Пример #1
0
def make_psf_map(psf, pointing, geom, max_offset, exposure_map=None):
    """Make a psf map for a single observation

    Expected axes : rad and true energy in this specific order
    The name of the rad MapAxis is expected to be 'rad'

    Parameters
    ----------
    psf : `~gammapy.irf.PSF3D`
        the PSF IRF
    pointing : `~astropy.coordinates.SkyCoord`
        the pointing direction
    geom : `~gammapy.maps.MapGeom`
        the map geom to be used. It provides the target geometry.
        rad and true energy axes should be given in this specific order.
    max_offset : `~astropy.coordinates.Angle`
        maximum offset w.r.t. fov center
    exposure_map : `~gammapy.maps.Map`, optional
        the associated exposure map.
        default is None

    Returns
    -------
    psfmap : `~gammapy.cube.PSFMap`
        the resulting PSF map
    """
    energy = geom.get_axis_by_name("energy").center

    rad_axis = geom.get_axis_by_name("theta")
    rad = Angle(rad_axis.center, unit=rad_axis.unit)

    # Compute separations with pointing position
    separations = pointing.separation(geom.to_image().get_coord().skycoord)
    valid = np.where(separations < max_offset)

    # Compute PSF values
    psf_values = psf.evaluate(offset=separations[valid],
                              energy=energy,
                              rad=rad)

    # Re-order axes to be consistent with expected geometry
    psf_values = np.transpose(psf_values, axes=(2, 0, 1))

    # TODO: this probably does not ensure that probability is properly normalized in the PSFMap
    # Create Map and fill relevant entries
    psfmap = Map.from_geom(geom, unit="sr-1")
    psfmap.data[:, :, valid[0], valid[1]] += psf_values.to_value(psfmap.unit)

    return PSFMap(psfmap, exposure_map)
Пример #2
0
    def run(self, dataset, observation):
        """Make map dataset.

        Parameters
        ----------
        dataset : `~gammapy.datasets.MapDataset`
            Reference dataset.
        observation : `~gammapy.data.Observation`
            Observation

        Returns
        -------
        dataset : `~gammapy.datasets.MapDataset`
            Map dataset.
        """
        kwargs = {"gti": observation.gti}
        kwargs["meta_table"] = self.make_meta_table(observation)
        mask_safe = Map.from_geom(dataset.counts.geom, dtype=bool)
        mask_safe.data |= True

        kwargs["mask_safe"] = mask_safe

        if "counts" in self.selection:
            counts = self.make_counts(dataset.counts.geom, observation)
            kwargs["counts"] = counts

        if "exposure" in self.selection:
            exposure = self.make_exposure(dataset.exposure.geom, observation)
            kwargs["exposure"] = exposure

        if "background" in self.selection:
            kwargs["background"] = self.make_background(
                dataset.counts.geom, observation)

        if "psf" in self.selection:
            psf = self.make_psf(dataset.psf.psf_map.geom, observation)
            kwargs["psf"] = psf

        if "edisp" in self.selection:
            if dataset.edisp.edisp_map.geom.axes[0].name.upper() == "MIGRA":
                edisp = self.make_edisp(dataset.edisp.edisp_map.geom,
                                        observation)
            else:
                edisp = self.make_edisp_kernel(dataset.edisp.edisp_map.geom,
                                               observation)

            kwargs["edisp"] = edisp

        return MapDataset(name=dataset.name, **kwargs)
Пример #3
0
    def run(self, images, niter_min=2, niter_max=10):
        """Run iterations until mask does not change (stopping condition).

        Parameters
        ----------
        images : dict
            Input sky images: counts, background, exclusion
        niter_min : int
            Minimum number of iterations, to prevent early termination of the
            algorithm.
        niter_max : int
            Maximum number of iterations after which the algorithm is
            terminated, if the termination condition (no change of mask between
            iterations) is not already satisfied.

        Returns
        -------
        images : dict
            Sky images: background, exclusion, significance
        """
        # initial mask, if not present
        if "exclusion" not in images:
            exclusion = Map.from_geom(images["counts"].geom)
            exclusion.data += 1
            images["exclusion"] = exclusion

        # initial background estimate, if not present
        if "background" not in images:
            images["background"] = self._estimate_background(
                images["counts"], images["exclusion"]
            )

        images["significance"] = self._estimate_significance(
            images["counts"], images["background"]
        )

        self.images_stack.append(images)

        for idx in range(niter_max):
            result = self.run_iteration(images)

            if self.parameters["keep_record"]:
                self.images_stack.append(result)

            if self._is_converged(result, images) and (idx >= niter_min):
                log.info(f"Exclusion mask converged after {idx} iterations.")
                break

        return result
Пример #4
0
    def estimate_kernel(self, dataset):
        """Get the convolution kernel for the input dataset.

        Convolves the model with the PSFKernel at the center of the dataset.

        Parameters
        ----------
        dataset : `~gammapy.datasets.MapDataset`
            Input dataset.

        Returns
        -------
        kernel : `Map`
            Kernel map

        """
        # TODO: further simplify the code below
        geom = dataset.counts.geom

        model = self.model.copy()
        model.spatial_model.position = geom.center_skydir

        binsz = np.mean(geom.pixel_scales)
        width_pix = self.kernel_width / binsz

        npix = round_up_to_odd(width_pix.to_value(""))

        axis = dataset.exposure.geom.axes["energy_true"]

        geom_kernel = WcsGeom.create(
            skydir=model.position, proj="TAN", npix=npix, axes=[axis], binsz=binsz
        )

        exposure = Map.from_geom(geom_kernel, unit="cm2 s1")
        exposure.data += 1.0

        # We use global evaluation mode to not modify the geometry
        evaluator = MapEvaluator(model, evaluation_mode="global")
        evaluator.update(exposure, dataset.psf, dataset.edisp, dataset.counts.geom)

        kernel = evaluator.compute_npred()
        kernel.data /= kernel.data.sum()

        if (self.kernel_width + binsz >= geom.width).any():
            raise ValueError(
                "Kernel shape larger than map shape, please adjust"
                " size of the kernel"
            )
        return kernel
Пример #5
0
def test_get_spectrum_weights():
    axis = MapAxis.from_bounds(1, 10, nbin=3, unit="TeV", name="energy")

    geom = WcsGeom.create(
        skydir=(0, 0), width=(2.5, 2.5), binsz=0.5, axes=[axis], frame="galactic"
    )

    m_int = Map.from_geom(geom, dtype="int")
    m_int.data += 1

    weights = Map.from_geom(geom, dtype="bool")
    weights.data[:, 2, 2] = True

    bad_weights = Map.from_geom(geom.to_image(), dtype="bool")

    center = SkyCoord(0, 0, frame="galactic", unit="deg")
    region = CircleSkyRegion(center=center, radius=1 * u.deg)

    spec_int = m_int.get_spectrum(region=region, weights=weights)
    assert spec_int.data.dtype == np.dtype("int")
    assert_allclose(spec_int.data.squeeze(), [1, 1, 1])

    with pytest.raises(ValueError):
        m_int.get_spectrum(region=region, weights=bad_weights)
Пример #6
0
    def from_energy_dependent_table_psf(cls, table_psf, geom=None):
        """Create PSF map from table PSF object.

        Helper function to create an allsky PSF map from
        table PSF, which does not depend on position.

        Parameters
        ----------
        table_psf : `EnergyDependentTablePSF`
            Table PSF

        Returns
        -------
        psf_map : `PSFMap`
            Point spread function map.
        """
        if geom is None:
            geom = WcsGeom.create(
                npix=(2, 1),
                proj="CAR",
                binsz=180,
                axes=[table_psf.rad_axis, table_psf.energy_axis_true])
        coords = geom.get_coord()

        # TODO: support broadcasting in .evaluate()
        data = table_psf._interpolate(
            (coords["energy_true"], coords["rad"])).to_value("sr-1")
        psf_map = Map.from_geom(geom, data=data, unit="sr-1")

        geom_exposure = geom.squash(axis_name="rad")

        data = table_psf.exposure.reshape((-1, 1, 1, 1))

        exposure_map = Map.from_geom(geom_exposure, unit="cm2 s")
        exposure_map.quantity += data
        return cls(psf_map=psf_map, exposure_map=exposure_map)
Пример #7
0
def test_downsample_onoff():
    axis = MapAxis.from_energy_bounds(1, 10, 4, unit="TeV")
    geom = WcsGeom.create(npix=(10, 10), binsz=0.05, axes=[axis])

    counts = Map.from_geom(geom, data=np.ones((4, 10, 10)))
    counts_off = Map.from_geom(geom, data=np.ones((4, 10, 10)))
    acceptance = Map.from_geom(geom, data=np.ones((4, 10, 10)))
    acceptance_off = Map.from_geom(geom, data=np.ones((4, 10, 10)))
    acceptance_off *= 2

    dataset_onoff = MapDatasetOnOff(
        counts=counts,
        counts_off=counts_off,
        acceptance=acceptance,
        acceptance_off=acceptance_off,
    )

    downsampled = dataset_onoff.downsample(2, axis_name="energy")

    assert downsampled.counts.data.shape == (2, 10, 10)
    assert downsampled.counts.data.sum() == dataset_onoff.counts.data.sum()
    assert downsampled.counts_off.data.sum(
    ) == dataset_onoff.counts_off.data.sum()
    assert_allclose(downsampled.alpha.data, 0.5)
Пример #8
0
def get_map_dataset_onoff(images, **kwargs):
    """Returns a MapDatasetOnOff"""
    mask_geom = images["counts"].geom
    mask_data = np.ones(images["counts"].data.shape, dtype=bool)
    mask_safe = Map.from_geom(mask_geom, data=mask_data)

    return MapDatasetOnOff(
        counts=images["counts"],
        counts_off=images["counts_off"],
        acceptance=images["acceptance"],
        acceptance_off=images["acceptance_off"],
        exposure=images["exposure"],
        mask_safe=mask_safe,
        **kwargs
    )
Пример #9
0
def make_psf_map(psf, pointing, geom, exposure_map=None):
    """Make a psf map for a single observation

    Expected axes : rad and true energy in this specific order
    The name of the rad MapAxis is expected to be 'rad'

    Parameters
    ----------
    psf : `~gammapy.irf.PSF3D`
        the PSF IRF
    pointing : `~astropy.coordinates.SkyCoord`
        the pointing direction
    geom : `~gammapy.maps.Geom`
        the map geom to be used. It provides the target geometry.
        rad and true energy axes should be given in this specific order.
    exposure_map : `~gammapy.maps.Map`, optional
        the associated exposure map.
        default is None

    Returns
    -------
    psfmap : `~gammapy.cube.PSFMap`
        the resulting PSF map
    """
    energy_axis = geom.get_axis_by_name("energy")
    energy = energy_axis.center

    rad_axis = geom.get_axis_by_name("theta")
    rad = rad_axis.center

    # Compute separations with pointing position
    offset = geom.separation(pointing)

    # Compute PSF values
    # TODO: allow broadcasting in PSF3D.evaluate()
    psf_values = psf._interpolate(
        (
            rad[:, np.newaxis, np.newaxis],
            offset,
            energy[:, np.newaxis, np.newaxis, np.newaxis],
        )
    )

    # TODO: this probably does not ensure that probability is properly normalized in the PSFMap
    # Create Map and fill relevant entries
    data = psf_values.to_value("sr-1")
    psfmap = Map.from_geom(geom, data=data, unit="sr-1")
    return PSFMap(psfmap, exposure_map)
Пример #10
0
    def run(self, dataset, observation):
        """Make map dataset.

        Parameters
        ----------
        dataset : `~gammapy.cube.MapDataset`
            Reference dataset.
        observation : `~gammapy.data.Observation`
            Observation

        Returns
        -------
        dataset : `~gammapy.cube.MapDataset`
            Map dataset.
        """
        kwargs = {"gti": observation.gti}

        mask_safe = Map.from_geom(dataset.counts.geom, dtype=bool)
        mask_safe.data |= True

        kwargs["mask_safe"] = mask_safe

        if "counts" in self.selection:
            counts = self.make_counts(dataset.counts.geom, observation)
            kwargs["counts"] = counts

        if "exposure" in self.selection:
            exposure = self.make_exposure(dataset.exposure.geom, observation)
            kwargs["exposure"] = exposure

        if "background" in self.selection:
            background_map = self.make_background(dataset.counts.geom,
                                                  observation)
            kwargs["models"] = BackgroundModel(
                background_map,
                name=dataset.name + "-bkg",
                datasets_names=[dataset.name],
            )

        if "psf" in self.selection:
            psf = self.make_psf(dataset.psf.psf_map.geom, observation)
            kwargs["psf"] = psf

        if "edisp" in self.selection:
            edisp = self.make_edisp(dataset.edisp.edisp_map.geom, observation)
            kwargs["edisp"] = edisp

        return MapDataset(name=dataset.name, **kwargs)
Пример #11
0
    def get_psf_kernel(self, geom, position=None, max_radius=None, factor=4):
        """Returns a PSF kernel at the given position.

        The PSF is returned in the form a WcsNDMap defined by the input Geom.

        Parameters
        ----------
        geom : `~gammapy.maps.Geom`
            Target geometry to use
        position : `~astropy.coordinates.SkyCoord`
            Target position. Should be a single coordinate. By default the
            center position is used.
        max_radius : `~astropy.coordinates.Angle`
            maximum angular size of the kernel map
        factor : int
            oversampling factor to compute the PSF

        Returns
        -------
        kernel : `~gammapy.irf.PSFKernel`
            the resulting kernel
        """
        # TODO: try to simplify...is the oversampling needed?
        if position is None:
            position = self.psf_map.geom.center_skydir

        position = self._get_nearest_valid_position(position)

        if max_radius is None:
            max_radius = np.max(self.psf_map.geom.axes["rad"].center)
            min_radius_geom = np.min(geom.width) / 2.0
            max_radius = min(max_radius, min_radius_geom)

        geom = geom.to_odd_npix(max_radius=max_radius)

        geom_upsampled = geom.upsample(factor=factor)
        rad = geom_upsampled.separation(geom.center_skydir)

        energy_axis = geom.axes["energy_true"]

        energy = energy_axis.center[:, np.newaxis, np.newaxis]
        coords = {"energy_true": energy, "rad": rad, "skycoord": position}

        data = self.psf_map.interp_by_coord(coords=coords, method="linear",)

        kernel_map = Map.from_geom(geom=geom_upsampled, data=np.clip(data, 0, np.inf))
        kernel_map = kernel_map.downsample(factor, preserve_counts=True)
        return PSFKernel(kernel_map, normalize=True)
Пример #12
0
    def plot_regions(self, ax=None, kwargs_point=None, path_effect=None, **kwargs):
        """Plot extent of the spatial models on a given wcs axis

        Parameters
        ----------
        ax : `~astropy.visualization.WCSAxes`
            Axes to plot on. If no axes are given, an all-sky wcs
            is chosen using a CAR projection
        kwargs_point : dict
            Keyword arguments passed to `~matplotlib.lines.Line2D` for plotting
            of point sources
        path_effect : `~matplotlib.patheffects.PathEffect`
            Path effect applied to artists and lines.
        **kwargs : dict
            Keyword arguments passed to `~matplotlib.artists.Artist`


        Returns
        -------
        ax : `~astropy.visualization.WcsAxes`
            WCS axes
        """
        from astropy.visualization.wcsaxes import WCSAxes

        kwargs_point = kwargs_point or {}

        if ax is None or not isinstance(ax, WCSAxes):
            ax = Map.from_geom(self.wcs_geom).plot()

        kwargs.setdefault("color", "tab:blue")
        kwargs.setdefault("fc", "None")
        kwargs_point.setdefault("marker", "*")
        kwargs_point.setdefault("markersize", 10)
        kwargs_point.setdefault("markeredgecolor", "None")
        kwargs_point.setdefault("color", kwargs["color"])

        for region in self.to_regions():
            if isinstance(region, PointSkyRegion):
                artist = region.to_pixel(ax.wcs).as_artist(**kwargs_point)
            else:
                artist = region.to_pixel(ax.wcs).as_artist(**kwargs)

            if path_effect:
                artist.set_path_effects([path_effect])

            ax.add_artist(artist)

        return ax
Пример #13
0
def test_reduce():
    # Check summing over a specific axis
    ax1 = MapAxis.from_nodes([1, 2, 3, 4], name="ax1")
    ax2 = MapAxis.from_nodes([5, 6, 7], name="ax2")
    ax3 = MapAxis.from_nodes([8, 9], name="ax3")
    geom = WcsGeom.create(npix=(5, 5), axes=[ax1, ax2, ax3])
    m1 = Map.from_geom(geom=geom)
    m1.data = np.ones(m1.data.shape)
    m2 = m1.reduce(axis_name="ax1", keepdims=True)

    assert_allclose(m2.geom.data_shape, (2, 3, 1, 5, 5))
    assert_allclose(m2.data[0][0][0][0][0], 4.0)

    m3 = m1.reduce(axis_name="ax1", keepdims=False)
    assert_allclose(m3.geom.data_shape, (2, 3, 5, 5))
    assert_allclose(m3.data[0][0][0][0], 4.0)
Пример #14
0
def test_mask_fit_modifications():
    axis = MapAxis.from_energy_bounds("0.1 TeV", "10 TeV", nbin=2)
    geom = WcsGeom.create(
        skydir=(266.40498829, -28.93617776),
        binsz=0.02,
        width=(2, 2),
        frame="icrs",
        axes=[axis],
    )
    mask_fit = Map.from_geom(geom)
    mask_fit.data = np.ones(mask_fit.data.shape, dtype=bool)
    mask_fit = mask_fit & geom.boundary_mask(width=(0.3 * u.deg, 0.1 * u.deg))
    assert np.sum(mask_fit.data[0, :, :]) == 6300
    assert np.sum(mask_fit.data[1, :, :]) == 6300
    mask_fit = mask_fit.binary_dilate(width=(0.3 * u.deg, 0.1 * u.deg))
    assert np.sum(mask_fit.data) == np.prod(mask_fit.data.shape)
Пример #15
0
def test_map_dataset_on_off_fake(geom):
    rad_axis = MapAxis(nodes=np.linspace(0.0, 1.0, 51), unit="deg", name="rad")
    energy_true_axis = geom.axes["energy"].copy(name="energy_true")

    empty_dataset = MapDataset.create(geom, energy_true_axis, rad_axis=rad_axis)
    empty_dataset = MapDatasetOnOff.from_map_dataset(
        empty_dataset, acceptance=1, acceptance_off=10.0
    )

    empty_dataset.acceptance_off.data[0, 50, 50] = 0
    background_map = Map.from_geom(geom, data=1)
    empty_dataset.fake(background_map, random_state=42)

    assert_allclose(empty_dataset.counts.data[0, 50, 50], 0)
    assert_allclose(empty_dataset.counts.data.mean(), 0.99445, rtol=1e-3)
    assert_allclose(empty_dataset.counts_off.data.mean(), 10.00055, rtol=1e-3)
Пример #16
0
def backgrounds():
    axis = MapAxis.from_edges(np.logspace(-1, 1, 3), unit=u.TeV, name="energy")
    geom = WcsGeom.create(skydir=(0, 0),
                          npix=(5, 4),
                          frame="galactic",
                          axes=[axis])
    m = Map.from_geom(geom)
    m.quantity = np.ones(geom.data_shape) * 1e-7
    background1 = TemplateNPredModel(m,
                                     name="bkg1",
                                     datasets_names="dataset-1")
    background2 = TemplateNPredModel(m,
                                     name="bkg2",
                                     datasets_names=["dataset-2"])
    backgrounds = [background1, background2]
    return backgrounds
Пример #17
0
def get_map_dataset_onoff(images, **kwargs):
    """Returns a MapDatasetOnOff"""
    mask_geom = images["counts"].geom
    mask_data = np.ones(images["counts"].data.shape, dtype=bool)
    mask_safe = Map.from_geom(mask_geom, data=mask_data)
    gti = GTI.create([0 * u.s], [1 * u.h],
                     reference_time="2010-01-01T00:00:00")

    return MapDatasetOnOff(counts=images["counts"],
                           counts_off=images["counts_off"],
                           acceptance=images["acceptance"],
                           acceptance_off=images["acceptance_off"],
                           exposure=images["exposure"],
                           mask_safe=mask_safe,
                           gti=gti,
                           **kwargs)
Пример #18
0
    def from_table_psf(cls, table_psf, geom, max_radius=None, factor=4):
        """Create a PSF kernel from a TablePSF or an EnergyDependentTablePSF on a given Geom.

        If the Geom is not an image, the same kernel will be present on all axes.

        The PSF is estimated by oversampling defined by a given factor.

        Parameters
        ----------
        table_psf : `~gammapy.irf.EnergyDependentTablePSF`
            Input table PSF
        geom : `~gammapy.maps.WcsGeom`
            Target geometry. The PSF kernel will be centered on the central pixel.
            The geometry axes should contain an axis with name "energy"
        max_radius : `~astropy.coordinates.Angle`
            Maximum radius of the PSF kernel.
        factor : int
            Oversample factor to compute the PSF

        Returns
        -------
        kernel : `~gammapy.irf.PSFKernel`
            the kernel Map with reduced geometry according to the max_radius
        """
        # TODO : use PSF containment radius if max_radius is None
        if max_radius is not None:
            geom = _make_kernel_geom(geom, max_radius)

        geom_upsampled = geom.upsample(factor=factor)
        rad = geom_upsampled.separation(geom.center_skydir)

        if isinstance(table_psf, EnergyDependentTablePSF):
            energy_axis = geom.axes["energy_true"]
            energy = energy_axis.center[:, np.newaxis, np.newaxis]
            data = table_psf.evaluate(energy=energy, rad=rad).value
        else:
            try:
                nbin = geom.axes[0].nbin
            except IndexError:
                nbin = 1
            data = table_psf.evaluate(rad=rad).value
            data = data * np.ones(nbin).reshape((-1, 1, 1))

        kernel_map = Map.from_geom(geom=geom_upsampled, data=data)
        kernel_map = kernel_map.downsample(factor, preserve_counts=True)
        return cls(kernel_map, normalize=True)
Пример #19
0
    def integrate_geom(self, geom):
        """Integrate model on `~gammapy.maps.Geom`

        Parameters
        ----------
        geom : `Geom`
            Map geometry

        Returns
        -------
        flux : `Map`
            Predicted flux map
        """
        x, y = geom.get_pix()[0:2]
        x0, y0 = self.position.to_pixel(geom.wcs)
        data = self._grid_weights(x, y, x0, y0)
        return Map.from_geom(geom=geom, data=data, unit="")
Пример #20
0
    def make_mask_safe(self, observation):
        """Make offset mask.

        Parameters
        ----------
        observation : `DataStoreObservation`
            Observation container.

        Returns
        -------
        mask : `Map`
            Mask
        """
        geom = self._cutout_geom(self.geom.to_image(), observation)
        offset = geom.separation(observation.pointing_radec)
        data = offset >= self.offset_max
        return Map.from_geom(geom, data=data)
Пример #21
0
def make_psf_map(psf, pointing, geom, exposure_map=None):
    """Make a psf map for a single observation

    Expected axes : rad and true energy in this specific order
    The name of the rad MapAxis is expected to be 'rad'

    Parameters
    ----------
    psf : `~gammapy.irf.PSF3D`
        the PSF IRF
    pointing : `~astropy.coordinates.SkyCoord`
        the pointing direction
    geom : `~gammapy.maps.Geom`
        the map geom to be used. It provides the target geometry.
        rad and true energy axes should be given in this specific order.
    exposure_map : `~gammapy.maps.Map`, optional
        the associated exposure map.
        default is None
    use_region_center: bool
        If geom is a RegionGeom, whether to just
        consider the values at the region center
        or the insted the average over the whole region

    Returns
    -------
    psfmap : `~gammapy.irf.PSFMap`
        the resulting PSF map
    """
    energy_true = geom.axes["energy_true"].center
    rad = geom.axes["rad"].center

    # Compute separations with pointing position
    offset = geom.separation(pointing)

    # Compute PSF values
    psf_values = psf.evaluate(
        energy_true=energy_true[:, np.newaxis, np.newaxis, np.newaxis],
        offset=offset,
        rad=rad[:, np.newaxis, np.newaxis],
    )

    # TODO: this probably does not ensure that probability is properly normalized in the PSFMap
    # Create Map and fill relevant entries
    data = psf_values.to_value("sr-1")
    psfmap = Map.from_geom(geom, data=data, unit="sr-1")
    return PSFMap(psfmap, exposure_map)
Пример #22
0
    def to_edisp_kernel_map(self, energy_axis):
        """Convert to map with edisp kernels

        Parameters
        ----------
        energy_axis : `~gammapy.maps.MapAxis`
            Reconstructed energy axis.

        Returns
        -------
        edisp : `~gammapy.maps.EDispKernelMap`
            Energy dispersion kernel map.
        """
        energy_axis_true = self.edisp_map.geom.axes["energy_true"]
        geom_image = self.edisp_map.geom.to_image()

        # migration value of energy bounds
        migra = energy_axis.edges / energy_axis_true.center[:, np.newaxis]

        coords = {
            "energy_true":
            energy_axis_true.center[:, np.newaxis, np.newaxis, np.newaxis],
            "migra":
            migra[:, :, np.newaxis, np.newaxis],
            "skycoord":
            geom_image.get_coord().skycoord
        }

        values = self.edisp_map.integral(axis_name="migra", coords=coords)

        axis = self.edisp_map.geom.axes.index_data("migra")
        data = np.clip(np.diff(values, axis=axis), 0, np.inf)

        geom = geom_image.to_cube([energy_axis, energy_axis_true])
        edisp_kernel_map = Map.from_geom(geom=geom,
                                         data=data.to_value(""),
                                         unit="")

        if self.exposure_map:
            geom = geom.squash(axis_name=energy_axis.name)
            exposure_map = self.exposure_map.copy(geom=geom)
        else:
            exposure_map = None

        return EDispKernelMap(edisp_kernel_map=edisp_kernel_map,
                              exposure_map=exposure_map)
Пример #23
0
    def make_counts(self, observation):
        """Make counts map.

        Parameters
        ----------
        observation : `DataStoreObservation`
            Observation container.

        Returns
        -------
        counts : `Map`
            Counts map.
        """
        geom = self._cutout_geom(self.geom, observation)
        counts = Map.from_geom(geom)
        fill_map_counts(counts, observation.events)
        return counts
Пример #24
0
    def estimate_flux_map(self, dataset):
        """Estimate flux and ts maps for single dataset

        Parameters
        ----------
        dataset : `MapDataset`
            Map dataset
        """
        maps = self.estimate_fit_input_maps(dataset=dataset)

        wrap = functools.partial(
            _ts_value,
            counts=maps["counts"].data.astype(float),
            exposure=maps["exposure"].data.astype(float),
            background=maps["background"].data.astype(float),
            kernel=maps["kernel"].data,
            norm=maps["norm"].data,
            flux_estimator=self._flux_estimator,
        )

        x, y = np.where(np.squeeze(maps["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()

        result = {}

        j, i = zip(*positions)

        geom = maps["counts"].geom.squash(axis_name="energy")

        for name in self.selection_all:
            m = Map.from_geom(geom=geom, data=np.nan, unit="")
            m.data[0, j, i] = [_[name] for _ in results]
            result[name] = m

        return result
Пример #25
0
    def estimate_kernel(self, dataset):
        """Get the convolution kernel for the input dataset.

        Convolves the model with the PSFKernel at the center of the dataset.

        Parameters
        ----------
        dataset : `~gammapy.datasets.MapDataset`
            Input dataset.

        Returns
        -------
        kernel : `Map`
            Kernel map

        """
        # TODO: further simplify the code below
        geom = dataset.exposure.geom

        model = self.model.copy()
        model.spatial_model.position = geom.center_skydir

        geom_kernel = geom.to_odd_npix(max_radius=self.kernel_width / 2)

        exposure = Map.from_geom(geom_kernel, unit="cm2 s1")
        exposure.data += 1.0

        # We use global evaluation mode to not modify the geometry
        evaluator = MapEvaluator(model, evaluation_mode="global")
        evaluator.update(
            exposure,
            dataset.psf,
            dataset.edisp,
            dataset.counts.geom,
            dataset.mask_fit,
        )

        kernel = evaluator.compute_npred()
        kernel.data /= kernel.data.sum()

        if (self.kernel_width >= geom.width).any():
            raise ValueError(
                "Kernel shape larger than map shape, please adjust"
                " size of the kernel")
        return kernel
Пример #26
0
def make_map_exposure_true_energy(pointing,
                                  livetime,
                                  aeff,
                                  geom,
                                  use_region_center=True):
    """Compute exposure map.

    This map has a true energy axis, the exposure is not combined
    with energy dispersion.

    Parameters
    ----------
    pointing : `~astropy.coordinates.SkyCoord`
        Pointing direction
    livetime : `~astropy.units.Quantity`
        Livetime
    aeff : `~gammapy.irf.EffectiveAreaTable2D`
        Effective area
    geom : `~gammapy.maps.WcsGeom`
        Map geometry (must have an energy axis)
    use_region_center: bool
        If geom is a RegionGeom, whether to just
        consider the values at the region center
        or the instead the average over the whole region

    Returns
    -------
    map : `~gammapy.maps.WcsNDMap`
        Exposure map
    """
    if not use_region_center:
        coords, weights = geom.get_wcs_coord_and_weights()
    else:
        coords, weights = geom.get_coord(sparse=True), None

    offset = coords.skycoord.separation(pointing)
    exposure = aeff.evaluate(offset=offset, energy_true=coords["energy_true"])

    data = (exposure * livetime).to("m2 s")
    meta = {"livetime": livetime, "is_pointlike": aeff.is_pointlike}

    if not use_region_center:
        data = np.average(data, axis=1, weights=weights)

    return Map.from_geom(geom=geom, data=data.value, unit=data.unit, meta=meta)
Пример #27
0
    def to_edisp_kernel_map(self, energy_axis):
        """Convert to map with edisp kernels

        Parameters
        ----------
        energy_axis : `~gammapy.maps.MapAxis`
            Reconstructed energy axis.

        Returns
        -------
        edisp : `~gammapy.maps.EDispKernelMap`
            Energy dispersion kernel map.
        """
        energy_axis_true = self.edisp_map.geom.axes["energy_true"]

        geom_image = self.edisp_map.geom.to_image()
        geom = geom_image.to_cube([energy_axis, energy_axis_true])

        coords = geom.get_coord(sparse=True, mode="edges", axis_name="energy")

        migra = coords["energy"] / coords["energy_true"]

        coords = {
            "skycoord": coords.skycoord,
            "energy_true": coords["energy_true"],
            "migra": migra,
        }

        values = self.edisp_map.integral(axis_name="migra", coords=coords)

        axis = self.edisp_map.geom.axes.index_data("migra")
        data = np.clip(np.diff(values, axis=axis), 0, np.inf)

        edisp_kernel_map = Map.from_geom(geom=geom,
                                         data=data.to_value(""),
                                         unit="")

        if self.exposure_map:
            geom = geom.squash(axis_name=energy_axis.name)
            exposure_map = self.exposure_map.copy(geom=geom)
        else:
            exposure_map = None

        return EDispKernelMap(edisp_kernel_map=edisp_kernel_map,
                              exposure_map=exposure_map)
Пример #28
0
    def run(self, dataset, observation=None):
        """Make safe data range mask.

        Parameters
        ----------
        dataset : `~gammapy.datasets.MapDataset` or `~gammapy.datasets.SpectrumDataset`
            Dataset to compute mask for.
        observation: `~gammapy.data.Observation`
            Observation to compute mask for.

        Returns
        -------
        dataset : `Dataset`
            Dataset with defined safe range mask.
        """

        if dataset.mask_safe:
            mask_safe = dataset.mask_safe.data
        else:
            mask_safe = np.ones(dataset._geom.data_shape, dtype=bool)

        if dataset.background is not None:
            # apply it first so only clipped values are removed for "bkg-peak"
            mask_safe &= self.make_mask_bkg_invalid(dataset)

        if "offset-max" in self.methods:
            mask_safe &= self.make_mask_offset_max(dataset, observation)

        if "aeff-default" in self.methods:
            mask_safe &= self.make_mask_energy_aeff_default(
                dataset, observation)

        if "aeff-max" in self.methods:
            mask_safe &= self.make_mask_energy_aeff_max(dataset)

        if "edisp-bias" in self.methods:
            mask_safe &= self.make_mask_energy_edisp_bias(dataset)

        if "bkg-peak" in self.methods:
            mask_safe &= self.make_mask_energy_bkg_peak(dataset)

        dataset.mask_safe = Map.from_geom(dataset._geom,
                                          data=mask_safe,
                                          dtype=bool)
        return dataset
Пример #29
0
    def make_counts(geom, observation):
        """Make counts map.

        Parameters
        ----------
        geom : `~gammapy.maps.Geom`
            Reference map geom.
        observation : `~gammapy.data.Observation`
            Observation container.

        Returns
        -------
        counts : `~gammapy.maps.Map`
            Counts map.
        """
        counts = Map.from_geom(geom)
        counts.fill_events(observation.events)
        return counts
Пример #30
0
    def run_fit(self, optimize_opts=None):
        """Fitting reduced datasets to model."""
        if not self.models:
            raise RuntimeError("Missing models")

        fit_settings = self.config.fit
        for dataset in self.datasets:
            if fit_settings.fit_range:
                energy_min = fit_settings.fit_range.min
                energy_max = fit_settings.fit_range.max
                geom = dataset.counts.geom
                data = geom.energy_mask(energy_min, energy_max)
                dataset.mask_fit = Map.from_geom(geom=geom, data=data)

        log.info("Fitting datasets.")
        self.fit = Fit(self.datasets)
        self.fit_result = self.fit.run(optimize_opts=optimize_opts)
        log.info(self.fit_result)