Пример #1
0
class BandFitExtended(object):
    def __init__(self, which, energy_band, roi):
        """ extendedsource
            which             index of source
            energy_band       ROIEnergyBand object to fit.
            bands             all energy bands in roi. """

        self.energy_band = energy_band
        self.bands = self.energy_band.bands
        self.all_bands = roi.bands

        self.which = which

        self.all_mybands = roi.dsm.bgmodels[self.which].bands

        # list of the mybands corresponding to energy_bands
        self.mybands = []

        # Create a lsit of myband object corresponding to the bands
        # in the energy_band.
        for eb_band in self.bands:
            for band, myband in zip(self.all_bands, self.all_mybands):
                if eb_band == band:
                    self.mybands.append(myband)
                    break

    def bandLikelihoodExtended(self, parameters, band, myband):

        new_counts = parameters[0] * myband.er

        old_counts = band.bg_counts[self.which]

        tot_term = (band.bg_all_counts + band.ps_all_counts + myband.overlaps *
                    (new_counts - old_counts)) * band.phase_factor

        pix_term = (
            band.pix_counts *
            np.log(band.bg_all_pix_counts + band.ps_all_pix_counts +
                   myband.es_pix_counts *
                   (new_counts - old_counts))).sum() if band.has_pixels else 0.

        return tot_term - pix_term

    def energyBandLikelihoodExtended(self, parameters, m):
        m.set_parameters(parameters)
        return sum(
            self.bandLikelihoodExtended([b.expected(m)], b, mb)
            for b, mb in zip(self.bands, self.mybands))

    def normUncertaintyExtended(self):
        tot = 0
        for b, mb in zip(self.bands, self.mybands):
            if not b.has_pixels: continue
            my_pix_counts = mb.es_pix_counts * b.expected(self.m) * mb.er
            all_pix_counts = b.bg_all_pix_counts + b.ps_all_pix_counts - mb.es_pix_counts * b.bg_counts[
                self.which] + my_pix_counts
            tot += (b.pix_counts * (my_pix_counts / all_pix_counts)**2).sum()
        return tot**-0.5

    def fit(self, saveto=None):

        bad_fit = False
        self.m = PowerLaw(free=[True, False],
                          e0=(self.energy_band.emin *
                              self.energy_band.emax)**0.5)  # fix index to 2
        f = self.energyBandLikelihoodExtended

        self.fit = fmin(f,
                        self.m.get_parameters(),
                        disp=0,
                        full_output=1,
                        args=(self.m, ))

        def upper_limit():

            flux_copy = self.m[0]
            zp = self.energyBandLikelihoodExtended(np.asarray([-20]), self.m)

            # NB -- the 95% upper limit is calculated by assuming the likelihood is peaked at
            # 0 flux and finding the flux at which it has fallen by 1.35; this is a two-sided
            # 90% limit, or a one-sided 95% limit -- that's how it works, right?
            def f95(parameters):
                return abs(
                    self.energyBandLikelihoodExtended(parameters, self.m) -
                    zp - 1.35)

            # for some reason, can't get fsolve to work here.  good ol' fmin to the rescue
            self.energy_band.uflux = 10**fmin(f95,
                                              np.asarray([-11.75]),
                                              disp=0)[0]
            self.energy_band.lflux = None
            self.energy_band.flux = None

            self.m[0] = flux_copy

        # if flux below a certain level, set an upper limit
        if self.m[0] < 1e-20:
            bad_fit = True
            upper_limit()

        else:
            try:
                err = self.normUncertaintyExtended()
            except:
                bad_fit = True
                err = 0

            self.energy_band.flux = self.m[0]
            self.energy_band.uflux = self.energy_band.flux * (1 + err)
            self.energy_band.lflux = max(self.energy_band.flux * (1 - err),
                                         1e-30)

        if saveto is not None:
            for b, mb in zip(self.bands, self.mybands):
                b.__dict__[saveto] = (b.expected(self.m) *
                                      mb.er if not bad_fit else -1)

        if bad_fit:
            self.energy_band.ts = 0
        else:
            null_ll = sum(
                self.bandLikelihoodExtended([0], b, mb)
                for b, mb in zip(self.bands, self.mybands))
            alt_ll = sum(
                self.bandLikelihoodExtended([b.expected(self.m) *
                                             mb.er], b, mb)
                for b, mb in zip(self.bands, self.mybands))
            self.energy_band.ts = 2 * (null_ll - alt_ll)