def make_srcmap_old(psf, spatial_model, sigma, npix=500, xpix=0.0, ypix=0.0, cdelt=0.01, rebin=1, psf_scale_fn=None): """Compute the source map for a given spatial model. Parameters ---------- psf : `~fermipy.irfs.PSFModel` spatial_model : str Spatial model. sigma : float Spatial size parameter for extended models. xpix : float Source position in pixel coordinates in X dimension. ypix : float Source position in pixel coordinates in Y dimension. rebin : int Factor by which the original map will be oversampled in the spatial dimension when computing the model. psf_scale_fn : callable Function that evaluates the PSF scaling function. Argument is energy in MeV. """ if rebin > 1: npix = npix * rebin xpix = xpix * rebin + (rebin - 1.0) / 2. ypix = ypix * rebin + (rebin - 1.0) / 2. cdelt = cdelt / rebin if spatial_model == 'RadialGaussian': k = utils.make_cgauss_kernel(psf, sigma, npix, cdelt, xpix, ypix, psf_scale_fn) elif spatial_model == 'RadialDisk': k = utils.make_cdisk_kernel(psf, sigma, npix, cdelt, xpix, ypix, psf_scale_fn) elif spatial_model == 'PointSource': k = utils.make_psf_kernel(psf, npix, cdelt, xpix, ypix, psf_scale_fn) else: raise Exception('Unsupported spatial model: %s', spatial_model) if rebin > 1: k = utils.sum_bins(k, 1, rebin) k = utils.sum_bins(k, 2, rebin) k *= psf.exp[:, np.newaxis, np.newaxis] * np.radians(cdelt)**2 return k
def calc_counts(skydir, ltc, event_class, event_types, egy_bins, cth_bins, fn, npts=1): """Calculate the expected counts vs. true energy and incidence angle for a source with spectral parameterization ``fn``. Parameters ---------- skydir : `~astropy.coordinate.SkyCoord` ltc : `~fermipy.irfs.LTCube` egy_bins : `~numpy.ndarray` Bin edges in observed energy in MeV. cth_bins : `~numpy.ndarray` Bin edges in cosine of the true incidence angle. npts : int Number of points by which to oversample each energy bin. """ #npts = int(np.ceil(32. / bins_per_dec(egy_bins))) egy_bins = np.exp(utils.split_bin_edges(np.log(egy_bins), npts)) exp = calc_exp(skydir, ltc, event_class, event_types, egy_bins, cth_bins) dnde = fn.dnde(egy_bins) cnts = loglog_quad(egy_bins, exp * dnde[:, None], 0) cnts = sum_bins(cnts, 0, npts) return cnts
def shift_to_coords(self, pix, fill_value=np.nan): """Create a new map that is shifted to the pixel coordinates ``pix``.""" pix_offset = self.get_offsets(pix) dpix = np.zeros(len(self.shape) - 1) for i in range(len(self.shape) - 1): x = self.rebin * (pix[i] - pix_offset[i + 1]) + (self.rebin - 1.0) / 2. dpix[i] = x - self._pix_ref[i] pos = [ pix_offset[i] + self.shape[i] // 2 for i in range(self.data.ndim) ] s0, s1 = utils.overlap_slices(self.shape_out, self.shape, pos) k = np.zeros(self.data.shape) for i in range(k.shape[0]): k[i] = shift(self._data_spline[i], dpix, cval=np.nan, order=2, prefilter=False) for i in range(1, len(self.shape)): k = utils.sum_bins(k, i, self.rebin) k0 = np.ones(self.shape_out) * fill_value if k[s1].size == 0 or k0[s0].size == 0: return k0 k0[s0] = k[s1] return k0
def shift_to_coords(self, pix, fill_value=np.nan): """Create a new map that is shifted to the pixel coordinates ``pix``.""" pix_offset = self.get_offsets(pix) dpix = np.zeros(len(self.shape) - 1) for i in range(len(self.shape) - 1): x = self.rebin * (pix[i] - pix_offset[i + 1] ) + (self.rebin - 1.0) / 2. dpix[i] = x - self._pix_ref[i] pos = [pix_offset[i] + self.shape[i] // 2 for i in range(self.data.ndim)] s0, s1 = utils.overlap_slices(self.shape_out, self.shape, pos) k = np.zeros(self.data.shape) for i in range(k.shape[0]): k[i] = shift(self._data_spline[i], dpix, cval=np.nan, order=2, prefilter=False) for i in range(1, len(self.shape)): k = utils.sum_bins(k, i, self.rebin) k0 = np.ones(self.shape_out) * fill_value if k[s1].size == 0 or k0[s0].size == 0: return k0 k0[s0] = k[s1] return k0
def calc_counts_edisp(skydir, ltc, event_class, event_types, egy_bins, cth_bins, fn, nbin=64, npts=1): """Calculate the expected counts vs. observed energy and true incidence angle for a source with spectral parameterization ``fn``. Parameters ---------- skydir : `~astropy.coordinate.SkyCoord` ltc : `~fermipy.irfs.LTCube` egy_bins : `~numpy.ndarray` Bin edges in observed energy in MeV. cth_bins : `~numpy.ndarray` Bin edges in cosine of the true incidence angle. npts : int Number of points by which to oversample each energy bin. """ #npts = int(np.ceil(32. / bins_per_dec(egy_bins))) # Split energy bins egy_bins = np.exp(utils.split_bin_edges(np.log(egy_bins), npts)) etrue_bins = 10**np.linspace(1.0, 6.5, nbin * 5.5 + 1) drm = calc_drm(skydir, ltc, event_class, event_types, egy_bins, cth_bins, nbin=nbin) cnts_etrue = calc_counts(skydir, ltc, event_class, event_types, etrue_bins, cth_bins, fn) cnts = np.sum(cnts_etrue[None, :, :] * drm[:, :, :], axis=1) cnts = sum_bins(cnts, 0, npts) return cnts
def make_srcmap_old(psf, spatial_model, sigma, npix=500, xpix=0.0, ypix=0.0, cdelt=0.01, rebin=1, psf_scale_fn=None): """Compute the source map for a given spatial model. Parameters ---------- psf : `~fermipy.irfs.PSFModel` spatial_model : str Spatial model. sigma : float Spatial size parameter for extended models. xpix : float Source position in pixel coordinates in X dimension. ypix : float Source position in pixel coordinates in Y dimension. rebin : int Factor by which the original map will be oversampled in the spatial dimension when computing the model. psf_scale_fn : callable Function that evaluates the PSF scaling function. Argument is energy in MeV. """ if rebin > 1: npix = npix * rebin xpix = xpix * rebin + (rebin - 1.0) / 2. ypix = ypix * rebin + (rebin - 1.0) / 2. cdelt = cdelt / rebin if spatial_model == 'RadialGaussian': k = utils.make_cgauss_kernel(psf, sigma, npix, cdelt, xpix, ypix, psf_scale_fn) elif spatial_model == 'RadialDisk': k = utils.make_cdisk_kernel(psf, sigma, npix, cdelt, xpix, ypix, psf_scale_fn) elif spatial_model == 'PointSource': k = utils.make_psf_kernel(psf, npix, cdelt, xpix, ypix, psf_scale_fn) else: raise Exception('Unsupported spatial model: %s', spatial_model) if rebin > 1: k = utils.sum_bins(k, 1, rebin) k = utils.sum_bins(k, 2, rebin) k *= psf.exp[:, np.newaxis, np.newaxis] * np.radians(cdelt) ** 2 return k
def calc_counts_edisp(skydir, ltc, event_class, event_types, egy_bins, cth_bins, fn, nbin=16, npts=1): """Calculate the expected counts vs. observed energy and true incidence angle for a source with spectral parameterization ``fn``. Parameters ---------- skydir : `~astropy.coordinate.SkyCoord` ltc : `~fermipy.irfs.LTCube` egy_bins : `~numpy.ndarray` Bin edges in observed energy in MeV. cth_bins : `~numpy.ndarray` Bin edges in cosine of the true incidence angle. nbin : int Number of points per decade with which to sample true energy. npts : int Number of points by which to oversample each reconstructed energy bin. """ #npts = int(np.ceil(32. / bins_per_dec(egy_bins))) # Split energy bins egy_bins = np.exp(utils.split_bin_edges(np.log(egy_bins), npts)) etrue_bins = 10**np.linspace(1.0, 6.5, np.int(np.ceil(nbin * 5.5 + 1))) drm = calc_drm(skydir, ltc, event_class, event_types, egy_bins, cth_bins, nbin=nbin) cnts_etrue = calc_counts(skydir, ltc, event_class, event_types, etrue_bins, cth_bins, fn) cnts = np.sum(cnts_etrue[None, :, :] * drm[:, :, :], axis=1) cnts = sum_bins(cnts, 0, npts) return cnts
def calc_drm(skydir, ltc, event_class, event_types, egy_bins, cth_bins, nbin=64): """Calculate the detector response matrix.""" npts = int(np.ceil(128. / bins_per_dec(egy_bins))) egy_bins = np.exp(utils.split_bin_edges(np.log(egy_bins), npts)) etrue_bins = 10**np.linspace(1.0, 6.5, nbin * 5.5 + 1) egy = 10**utils.edge_to_center(np.log10(egy_bins)) egy_width = utils.edge_to_width(egy_bins) etrue = 10**utils.edge_to_center(np.log10(etrue_bins)) edisp = create_avg_edisp(skydir, ltc, event_class, event_types, egy, etrue, cth_bins) edisp = edisp * egy_width[:, None, None] edisp = sum_bins(edisp, 0, npts) return edisp
def calc_drm(skydir, ltc, event_class, event_types, egy_bins, cth_bins, nbin=64): """Calculate the detector response matrix.""" npts = int(np.ceil(128. / bins_per_dec(egy_bins))) egy_bins = np.exp(utils.split_bin_edges(np.log(egy_bins), npts)) etrue_bins = 10**np.linspace(1.0, 6.5, np.int(np.ceil(nbin * 5.5 + 1))) egy = 10**utils.edge_to_center(np.log10(egy_bins)) egy_width = utils.edge_to_width(egy_bins) etrue = 10**utils.edge_to_center(np.log10(etrue_bins)) edisp = create_avg_edisp(skydir, ltc, event_class, event_types, egy, etrue, cth_bins) edisp = edisp * egy_width[:, None, None] edisp = sum_bins(edisp, 0, npts) return edisp
def compute_norm(sig, bkg, ts_thresh, min_counts, sum_axes=None, bkg_fit=None, rebin_axes=None): """Solve for the normalization of the signal distribution at which the detection test statistic (twice delta-loglikelihood ratio) is >= ``ts_thresh`` AND the number of signal counts >= ``min_counts``. This function uses the Asimov method to calculate the median expected TS when the model for the background is fixed (no uncertainty on the background amplitude). Parameters ---------- sig : `~numpy.ndarray` Array of signal amplitudes in counts. bkg : `~numpy.ndarray` Array of background amplitudes in counts. ts_thresh : float Test statistic threshold. min_counts : float Counts threshold. sum_axes : list Axes over which the source test statistic should be summed. By default the summation will be performed over all dimensions. bkg_fit : `~numpy.ndarray` Array of background amplitudes in counts for the fitting model. If None then the fit model will be equal to the data model. """ if sum_axes is None: sum_axes = np.arange(sig.ndim) sig = np.expand_dims(sig, -1) bkg = np.expand_dims(bkg, -1) sig_sum = np.apply_over_axes(np.sum, sig, sum_axes) bkg_sum = np.apply_over_axes(np.sum, bkg, sum_axes) bkg_fit_sum = None if bkg_fit is not None: bkg_fit = np.expand_dims(bkg_fit, -1) bkg_fit_sum = np.apply_over_axes(np.sum, bkg_fit, sum_axes) sig_rebin = sig bkg_rebin = bkg bkg_fit_rebin = bkg_fit if rebin_axes: sig_rebin = sig.copy() bkg_rebin = bkg.copy() if bkg_fit is not None: bkg_fit_rebin = bkg_fit.copy() for dim, rebin in zip(sum_axes, rebin_axes): sig_rebin = sum_bins(sig_rebin, dim, rebin) bkg_rebin = sum_bins(bkg_rebin, dim, rebin) if bkg_fit is not None: bkg_fit_rebin = sum_bins(bkg_fit_rebin, dim, rebin) # Find approx solution using coarse binning and summed arrays sig_scale = 10**np.linspace(0.0, 10.0, 51) * (min_counts / sig_sum) vals_approx = _solve_norm(sig_rebin, bkg_rebin, ts_thresh, min_counts, sig_scale, sum_axes, bkg_fit_rebin) # Refine solution using an interval (0.1,10) around approx # solution sig_scale = (10**np.linspace(0.0, 1.0, 21) * np.fmax(0.333 * vals_approx[..., None], min_counts / sig_sum)) vals = _solve_norm(sig, bkg, ts_thresh, min_counts, sig_scale, sum_axes, bkg_fit) #sig_scale = 10**np.linspace(0.0, 10.0, 101)*(min_counts / sig_sum) # vals = _solve_norm(sig, bkg, ts_thresh, min_counts, sig_scale2, # sum_axes, bkg_fit) return vals