Beispiel #1
0
    def __setup_data__(self):
        """Get all pixels within the ROI in this band."""

        mi,ma                  = N.asarray([self.sa.minROI,self.sa.maxROI])*(N.pi/180.)
        # note use of band sigma for consistency in photon selection!
        self.radius_in_rad = max(min((2*self.umax)**0.5*self.b.sigma(),ma),mi)
        self.wsdl             = WeightedSkyDirList(self.b,self.sd,self.radius_in_rad,False)
        self.pix_counts     = N.asarray([x.weight() for x in self.wsdl]) if len(self.wsdl) else 0.
        self.photons         = self.wsdl.counts()
        self.has_pixels     = self.photons > 0
        self.npix             = self.wsdl.total_pix()
        self.solid_angle    = self.npix*self.b.pixelArea() #ragged edge
        self.solid_angle_p = 2*N.pi*(1-N.cos(self.radius_in_rad)) #solid angle for a pure circle
Beispiel #2
0
    def __call__(self, band, nside, index, ps_dir):
        """Return an array of fractional overlap for a point source at location skydir."""

        # pick an nside commensurate with PSF and that will fit evenly within base pixel
        scale = band.psf.parent_psf.scale_func[band.ct](band.e) / 5
        sample_nside = (np.pi / 3)**0.5 / scale
        p0 = np.log(sample_nside / nside) / np.log(2)
        last_overlap = None
        overlaps = []
        for addon in [0]:
            sample_nside = min(8192, nside * 2**(int(p0) + addon))
            geo = (4 * np.pi / 12 / sample_nside**2)
            #print sample_nside,index

            # temporary code for testing!
            if True:
                from skymaps import Band
                band.b = Band(sample_nside)

            wsdl = WeightedSkyDirList(band.b, nside, index, True)
            vals = np.empty(len(wsdl))
            band.psf.cpsf.wsdl_val(vals, ps_dir, wsdl)
            overlap = vals.sum() * geo
            #print overlap
            overlaps.append(overlap)
            #if last_overlap is not None:
            #    new_est = (2*last_overlap+4*overlap)/6
            #    print 'Updated estimate: ',new_est
            last_overlap = overlap
        #print ('%.6f\t'*(len(overlaps)))%(tuple(overlaps))
        return overlaps[-1], sample_nside
Beispiel #3
0
    def setup_from_roi(self, hr, factor):

        band1 = hr.band
        band2 = Band(int(round(band1.nside() * factor)))
        rd = band1.dir(hr.index)

        # get pixels within a radius 10% greater than the base Healpix diagonal dimension
        radius = (np.pi / (3 * band1.nside()**2))**0.5 * 2**0.5 * 1.1
        wsdl = WeightedSkyDirList(band2, rd, radius, True)

        # then cut down to just the pixels inside the base Healpixel
        inds = np.asarray([band1.index(x) for x in wsdl])
        mask = inds == hr.index
        dirs = [wsdl[i] for i in xrange(len(mask)) if mask[i]]
        inds = np.asarray([band2.index(x) for x in dirs]).astype(int)

        # sanity check
        if abs(float(mask.sum()) / factor**2 - 1) > 0.01:
            print 'Warning: number of pixels found does not agree with expectations!'

        # calculate TS for each sub-pixel
        tsc = TSCalc(hr.roi)
        if self.bright_sources is not None:
            ts_vals = self.ts_vals = [deque(), deque(), deque()]
            bsm = np.asarray([
                x.source_name in self.bright_sources for x in hr.roi.psm.models
            ])
            for d in dirs:
                ts_vals[0].append(tsc(d))
                ts_vals[1].append(tsc(d, repeat_diffuse=True))
                ts_vals[2].append(
                    tsc(d, repeat_diffuse=True, bright_source_mask=bsm))
        else:
            self.ts_vals = ts_vals = [deque(), deque()]
            for d in dirs:
                ts_vals[0].append(tsc(d))
                ts_vals[1].append(tsc(d, repeat_diffuse=True))

        self.band1 = band1
        self.band2 = band2

        sorting = np.argsort(inds)
        self.inds = inds[sorting]
        for i in range(len(ts_vals)):
            ts_vals[i] = np.asarray(ts_vals[i])[sorting]

        self.previous_index = -1
        self.index = hr.index
        self.mode = 0
        self.ts = self.ts_vals[0]
Beispiel #4
0
    def extended_source_counts(self, extended_model):
        if type(extended_model) not in [
                ROIExtendedModel, ROIExtendedModelAnalytic
        ]:
            raise Exception("Unknown extended model.")

        roi = self.roi
        sm = extended_model.extended_source.model

        extended_counts = np.zeros_like(self.bin_centers_rad)

        for band, smaller_band in zip(self.selected_bands, self.smaller_bands):

            extended_model.set_state(smaller_band)

            if type(extended_model) == ROIExtendedModel:

                nside = RadialModel.get_nside(self.size, self.npix)

                temp_band = Band(nside)
                wsdl = WeightedSkyDirList(temp_band, self.center,
                                          np.radians(self.size), True)
                vals = extended_model._pix_value(wsdl)

                rvals = np.empty(len(wsdl), dtype=float)
                PythonUtilities.arclength(rvals, wsdl, self.center)

                # get average value in each ring by averaging values.
                fraction = np.histogram(rvals,weights=vals,bins=np.sqrt(self.bin_edges_rad))[0]/\
                           np.histogram(rvals,bins=np.sqrt(self.bin_edges_rad))[0]

                # multiply intensities by solid angle in ring
                fraction *= RadialModel.solid_angle_cone(np.radians(
                    self.size)) / self.npix

            elif type(extended_model) == ROIExtendedModelAnalytic:

                fraction = np.empty_like(self.bin_centers_rad)

                for i, (theta_min,
                        theta_max) in enumerate(self.theta_pairs_rad):

                    fraction[i]=extended_model._overlaps(self.center,band,theta_max) - \
                             extended_model._overlaps(self.center,band,theta_min)

            # total counts from source * fraction of PDF in ring = model predictions in each ring.
            extended_counts += band.expected(sm) * fraction

        return extended_counts
Beispiel #5
0
    def setup_from_roi(self, hr, factor):

        band1 = hr.band
        band2 = Band(int(round(band1.nside() * factor)))
        rd = band1.dir(hr.index)

        # get pixels within a radius 10% greater than the base Healpix diagonal dimension
        radius = (np.pi / (3 * band1.nside()**2))**0.5 * 2**0.5 * 1.1
        wsdl = WeightedSkyDirList(band2, rd, radius, True)

        # then cut down to just the pixels inside the base Healpixel
        inds = np.asarray([band1.index(x) for x in wsdl])
        mask = inds == hr.index
        dirs = [wsdl[i] for i in xrange(len(mask)) if mask[i]]
        inds = np.asarray([band2.index(x) for x in dirs]).astype(int)

        # sanity check
        if abs(float(mask.sum()) / factor**2 - 1) > 0.01:
            print 'Warning: number of pixels found does not agree with expectations!'

        # loop through the bands and image pixels to calculate the KDE
        from libpointlike import DoubleVector
        #dv = DoubleVector()
        rvs = [np.empty(len(band.wsdl), dtype=float) for band in hr.roi.bands]
        img = np.zeros(len(inds))
        weights = [
            np.asarray([x.weight() for x in b.wsdl]).astype(int)
            for b in hr.roi.bands
        ]
        for idir, mydir in enumerate(dirs):
            #print 'Processing pixel %d'%(idir)
            for iband, band in enumerate(hr.roi.bands):
                PythonUtilities.arclength(band.rvals, band.wsdl, mydir)
                img[idir] += (band.psf(rvs[iband], density=True) *
                              weights[iband]).sum()

        self.band1 = band1
        self.band2 = band2
        self.previous_index = -1

        sorting = np.argsort(inds)
        self.inds = inds[sorting]
        self.img = img[sorting]

        self.index = hr.index
Beispiel #6
0
    def otf_source_counts(self, bg):

        roi = self.roi

        mo = bg.smodel

        background_counts = np.zeros_like(self.bin_centers_rad)

        for band, smaller_band in zip(self.selected_bands, self.smaller_bands):

            ns, bg_points, bg_vector = ROIDiffuseModel_OTF.sub_energy_binning(
                band, bg.nsimps)

            nside = RadialModel.get_nside(self.size, self.npix)

            temp_band = Band(nside)
            wsdl = WeightedSkyDirList(temp_band, self.center,
                                      np.radians(self.size), True)

            ap_evals = np.empty([len(self.bin_centers_rad), len(bg_points)])

            for ne, e in enumerate(bg_points):

                bg.set_state(e, band.ct, smaller_band)

                rvals = np.empty(len(wsdl), dtype=float)
                PythonUtilities.arclength(rvals, wsdl, self.center)
                vals = bg._pix_value(wsdl)

                # get average value in each ring by averaging values.
                ap_evals[:,ne] = np.histogram(rvals,weights=vals,bins=np.sqrt(self.bin_edges_rad))[0]/\
                                 np.histogram(rvals,bins=np.sqrt(self.bin_edges_rad))[0]

            # multiply intensities by solid angle in ring
            ap_evals *= RadialModel.solid_angle_cone(np.radians(
                self.size)) / self.npix

            ap_evals *= bg_vector
            mo_evals = mo(bg_points)
            background_counts += (ap_evals * mo_evals).sum(axis=1)

        return background_counts
Beispiel #7
0
 def set_dir_cache(self, band, roi_dir, radius):
     self.cache_wsdl = WeightedSkyDirList(band.b, roi_dir, radius, True)
     self.cache_diffs = np.empty(len(self.cache_wsdl))
     self.cache_hash = hash(band)
Beispiel #8
0
class ROIBand(object):
    """Wrap a Band object, and provide additional functionality for likelihood."""

    ADJUST_MEAN = True  # provide a "static" interface for functionality below

    def init(self):
        self.umax = 50
        self.nsp_simps = 16
        self.bracketing_function = None
        self.phase_factor = 1.

    def __init__(self, band, spectral_analysis, skydir, **kwargs):
        """
        band: a skymaps.Band object
        spectral_analysis: needed for exposure, psf.band_psf, minROI, maxROI 
        skydir a SkyDir object, used to select data from the Band
        """

        self.init()
        self.__dict__.update(**kwargs)
        self.b = band
        self.emin, self.emax = band.emin(), band.emax()
        self.e = (self.emin * self.emax)**0.5
        self.sa = spectral_analysis
        self.sd = skydir
        self.ec = self.ct = band.event_class() & 1  # note change and mask
        self.exp = self.sa.exposure.exposure[self.ct]

        self.__setup_data__()
        self.__setup_sp_simps__()

        self.psf = self.sa.psf.band_psf(self, adjust_mean=ROIBand.ADJUST_MEAN)

        # this is sneaky but should work just fine
        if self.bracketing_function is not None:
            self.phase_factor *= self.bracketing_function(self.e, self.ec)

    def __setup_data__(self):
        """Get all pixels within the ROI in this band."""

        mi, ma = N.asarray([self.sa.minROI, self.sa.maxROI]) * (N.pi / 180.)
        # note use of band sigma for consistency in photon selection!
        self.radius_in_rad = max(
            min((2 * self.umax)**0.5 * self.b.sigma(), ma), mi)
        self.wsdl = WeightedSkyDirList(self.b, self.sd, self.radius_in_rad,
                                       False)
        self.pix_counts = N.asarray([x.weight() for x in self.wsdl]) if len(
            self.wsdl) else 0.
        self.photons = self.wsdl.counts()
        self.has_pixels = self.photons > 0
        self.npix = self.wsdl.total_pix()
        self.solid_angle = self.npix * self.b.pixelArea()  #ragged edge
        self.solid_angle_p = 2 * N.pi * (1 - N.cos(self.radius_in_rad)
                                         )  #solid angle for a pure circle

    def __setup_sp_simps__(self):
        """Cache factors for quickly evaluating the counts under a given spectral model."""

        self.sp_points = sp = N.logspace(N.log10(self.emin),
                                         N.log10(self.emax),
                                         self.nsp_simps + 1)
        exp_points = N.asarray(self.exp.vector_value(self.sd,
                                                     DoubleVector(sp)))
        simps_weights  = (N.log(sp[-1]/sp[0])/(3.*self.nsp_simps)) * \
                              N.asarray([1.] + ([4.,2.]*(self.nsp_simps/2))[:-1] + [1.])
        self.sp_vector = sp * exp_points * simps_weights

    ### the following functions added to for functionality best performed by an ROIband
    ### they are not used by the (old) likelihood computation
    def pixels_from_psf(self, skydir):
        """ return pixel array of predicted values given the PSF and direction
        """
        rvals = np.empty(len(self.wsdl), dtype=float)
        self.psf.cpsf.wsdl_val(rvals, skydir, self.wsdl)  #from C++: sets rvals
        return rvals * self.b.pixelArea()

    def exposure_integral(self, model_function, axis=None):
        """ return integral over exposure for function of differential flux """
        return (model_function(self.sp_points) * self.sp_vector).sum(axis=axis)

    def psf_overlap(self, skydir):
        """ return the overlap of the associated PSF at the given position with this ROI
        """
        return PsfOverlap()(self, self.sd, skydir)

    def exposure(self, skydir, energy=None):
        """ return the exposure at the position and the given energy, or (default) the central energy"""
        return self.exp.value(skydir, energy if energy is not None else self.e)

    ################################################################################

    def reload_data(self, band):
        """For Monte Carlo, when everything stays same but realization of data."""
        self.b = band
        self.__setup_data__()

    def expected(self, model):
        """Integrate the passed spectral model over the exposure and return expected counts."""
        return (model(self.sp_points) * self.sp_vector).sum()

    def gradient(self, model):
        """Calculate the gradient of a spectral model (wrt its parameters) integrated over the exposure."""
        return (model.gradient(self.sp_points) * self.sp_vector).sum(axis=1)

    def bandLikelihood(self, parameters, *args):
        """Implement a model independent likelihood for the number of counts of a particular source.
            Other sources (diffuse, neighboring point sources) are treated as fixed."""

        # N.B. -- it is assumed that new_counts *does not* include exposure ratio
        # this is the easiest way to deal with the exposure ratio, but it must be explicitly set if the *counts*
        # are used elsewhere.  This comes up in, e.g., saving the values for use in localization.
        which = args[0] if len(args) > 0 else 0
        new_counts = parameters[0] * self.er[which]

        old_counts = self.ps_counts[which]

        tot_term = (self.bg_all_counts + self.ps_all_counts +
                    self.overlaps[which] *
                    (new_counts - old_counts)) * self.phase_factor

        pix_term = (
            self.pix_counts *
            N.log(self.bg_all_pix_counts + self.ps_all_pix_counts +
                  self.ps_pix_counts[:, which] *
                  (new_counts - old_counts))).sum() if self.has_pixels else 0.

        return tot_term - pix_term

    def logLikelihood(self):
        """ Return the (negative) log likelihood for this band.  Certain members
            of this object are set externally and are required here, specifically
            ps_all_counts, ps_all_pix_counts, bg_all_counts, and bg_all_pix_counts.
        
        """
        tot = (self.bg_all_counts + self.ps_all_counts) * self.phase_factor

        pix = (self.pix_counts * N.log(self.bg_all_pix_counts + self.ps_all_pix_counts)).sum()\
              if self.has_pixels else 0

        return tot - pix