Esempio n. 1
0
def sim_evlist(flux=.1,
               obstime=.5,
               arf=None,
               rmf=None,
               extra=None,
               output_filename_base=None,
               write_pha=False,
               do_graphical_output=True,
               loglevel='INFO') :
    # Time it!
    t_1 = time.clock()

    # Configure logging
    numeric_level = getattr(logging, loglevel.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % loglevel)
    logging.basicConfig(level=numeric_level,
                        format='%(asctime)s - %(levelname)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')

    # Welcome user, print out basic information on package versions
    logging.info('This is {0} (pyfact v{1})'.format(os.path.split(__file__)[1], pf.__version__))
    logging.info('We are running with numpy v{0}, scipy v{1}, and pyfits v{2}'.format(
        np.__version__, scipy.__version__, pyfits.__version__
        ))

    #---------------------------------------------------------------------------

    logging.info('Exposure: {0} h'.format(obstime))
    obstime *= 3600. #observation time in seconds
    
    obj_ra, obj_dec = 0., .5
    pnt_ra, pnt_dec = 0., 0.
    t_min = 24600.

    objcosdec = np.cos(obj_dec * np.pi / 180.)

    #---------------------------------------------------------------------------
    # Read ARF, RMF, and extra file

    logging.info('ARF: {0}'.format(arf))
    ea, ea_erange = pf.arf_to_np(pyfits.open(arf)[1])

    # DEBUG
    #ea /= irf_data[:,4]
    
    rm, rm_erange, rm_ebounds, rm_minprob = None, ea_erange, None, None
    if rmf :
        logging.info('RMF: {0}'.format(rmf))
        rm, rm_erange, rm_ebounds, rm_minprob = pf.rmf_to_np(pyfits.open(rmf))

    if extra :
        logging.info('Extra file: {0}'.format(extra))
        extraf = pyfits.open(extra)
        logging.info('Using effective area with 80% containment from extra file')
        ea = extraf['EA80'].data.field('VAL') / .8 # 100% effective area
        ea_erange = 10. ** np.hstack([extraf['EA80'].data.field('BIN_LO'), extraf['EA80'].data.field('BIN_HI')[-1]])
    else :
        logging.info('Assuming energy independent 80% cut efficiency for ARF file.')
        ea /= .80
        
    #---------------------------------------------------------------------------
    # Signal

    #log_e_cen = (irf_data[:,0] + irf_data[:,1]) / 2.
    e_cen = 10. ** ((np.log10(ea_erange[1:]*ea_erange[:-1])) / 2.)

    ea_loge_step_mean = np.log10(ea_erange[1:]/ea_erange[:-1]).mean().round(4)
    D('ea_loge_step_mean = {0}', ea_loge_step_mean)

    # DEBUG
    #ea_s, e_cen_s = ea, e_cen

    # Resample effective area to increase precision
    if ea_loge_step_mean > .1 :
        elog10step = .05
        logging.info('Resampling effective area in log10(EA) vs log10(E) (elog10step = {0})'.format(elog10step))
        ea_spl = scipy.interpolate.UnivariateSpline(e_cen, np.log10(ea), s=0, k=1)
        e_cen = 10. ** np.arange(np.log10(e_cen[0]), np.log10(e_cen[-1]), step=elog10step)
        ea = 10. ** ea_spl(e_cen)
    
    # DEBUG plot
    #plt.loglog(e_cen_s, ea_s, )
    #plt.loglog(e_cen, ea, '+')
    #plt.show()

    func_pl = lambda x, p: p[0] * x ** (-p[1])
    flux_f =  lambda x : func_pl(x, (3.45E-11 * flux, 2.63))

    f_test = scipy.interpolate.UnivariateSpline(e_cen, ea * flux_f(e_cen) * 1E4, s=0, k=1) # m^2 > cm^2

    log_e_steps = np.log10(rm_erange)

    # Calculate event numbers for the RMF bins
    def get_int_rate(emin, emax) :
        if emin < e_cen[0] or emax > e_cen[-1] :
            return 0.
        else :
            return f_test.integral(emin, emax)
    int_rate = np.array([get_int_rate(10. ** el, 10. ** eh) for (el, eh) in zip(log_e_steps[:-1], log_e_steps[1:])])
    # Sanity
    int_rate[int_rate < 0.] = 0.

    # DEBUG
    #int_rate_s = int_rate

    if rmf :
        D('Photon rate before RM = {0}', np.sum(int_rate))
        # Apply energy distribution matrix
        int_rate = np.dot(int_rate, rm)
        D('Photon rate after RM = {0}', np.sum(int_rate))

    # DEBUG plots
    #plt.figure(1)
    #plt.semilogy(log_e_steps[:-1], int_rate_s, 'o', label='PRE RMF')
    ##plt.plot(log_e_steps[:-1], int_rate_s, 'o', label='PRE RMF')
    #if rmf :
    #    plt.semilogy(np.log10(rm_ebounds[:-1]), int_rate, '+', label='POST RMF')
    #plt.ylim(1E-6,1.)
    #plt.legend()
    #plt.show()
    ##sys.exit(0)

    # Calculate cumulative event numbers
    int_all = np.sum(int_rate)
    int_rate = np.cumsum(int_rate)

    if rmf :
        #log_e_steps = (np.log10(rm_ebounds[1:]) + np.log10(rm_ebounds[:-1])) / 2.
        log_e_steps = np.log10(rm_ebounds)

    # Filter out low and high values to avoid spline problems at the edges
    istart = np.sum(int_rate == 0.) - 1
    if istart < 0 :
        istart = 0
    istop = np.sum(int_rate / int_all > 1. - 1e-4) # This value dictates the dynamic range at the high energy end

    D('istart = {0}, istop = {1}', istart, istop)

    # DEBUG plots
    #plt.plot(int_rate[istart:-istop] / int_all, log_e_steps[istart + 1:-istop], '+')
    #plt.show()

    # DEBUG plots
    #plt.hist(int_rate[istart:-istop] / int_all)
    #plt.show()

    ev_gen_f = scipy.interpolate.UnivariateSpline(
        int_rate[istart:-istop] / int_all,
        log_e_steps[istart + 1:-istop],
        s=0,
        k=1
        )

    ## DEBUG plot
    #plt.plot(np.linspace(0.,1.,100), ev_gen_f(np.linspace(0.,1.,100)), 'o')
    #plt.show()

    # Test event generator function
    n_a_t = 100.
    a_t = ev_gen_f(np.linspace(0.,1., n_a_t))
    D('Test ev_gen_f, (v = 0 / #v) = {0}, (v = NaN / #v) = {1}',
      np.sum(a_t == 0.) / n_a_t, np.sum(np.isnan(a_t)) / n_a_t)

    if (np.sum(a_t == 0.) / n_a_t > 0.05) or (np.sum(np.isnan(a_t)) / n_a_t > .05) :
        raise Exception('Could not generate event generator function for photons. Try to decrease the upper cut-off value in the code.')

    # Calculate total number of photon events
    n_events = int_all * obstime

    logging.debug('Number of photons : {0}'.format(n_events))

    # Generate energy event list
    evlist_e = ev_gen_f(np.random.rand(n_events))
    
    
    # Sanity
    D('Number of photons with E = NaN : {0}', np.sum(np.isnan(evlist_e)))
    evlist_e[np.isnan(evlist_e)] = 0.

    ## DEBUG plot
    #plt.figure(1)
    #plt.hist(evlist_e, range=[-2.,2.], bins=20)
    ##plt.show()
    #sys.exit(0)

    #------------------------------------------------------
    # Apply PSF

    # Broken power law fit function, normalized at break energy
    bpl = lambda p,x : np.where(x < p[0], p[1] * (x / p[0]) ** -p[2],  p[1] * (x / p[0]) ** -p[3])
    evlist_psf = None
    if extra :
        d = extraf['ANGRES68'].data
        g = scipy.interpolate.UnivariateSpline((d.field('BIN_LO') + d.field('BIN_HI')) / 2., d.field('VAL'), s=0, k=1)
        evlist_psf = g(evlist_e)
    else :
        psf_p1 = [1.1, 5.5E-2, .42, .19] # Fit from SubarrayE_IFAE_50hours_20101102
        evlist_psf = bpl(psf_p1, 10. ** evlist_e)
        logging.warning('Using dummy PSF extracted from SubarrayE_IFAE_50hours_20101102')
    
    evlist_dec = obj_dec + np.random.randn(n_events) * evlist_psf
    evlist_ra =  obj_ra + np.random.randn(n_events) * evlist_psf / objcosdec

    evlist_t = t_min + obstime * np.random.rand(n_events) / 86400.

    #---------------------------------------------------------------------------
    # Background

    #plt.figure(1)

    p_rate_area, log_e_cen = None, None
    if extra :
        d = extraf['BGRATED'].data
        p_rate_area = d.field('VAL')
        log_e_cen = (d.field('BIN_LO') + d.field('BIN_HI')) / 2
        #g = scipy.interpolate.UnivariateSpline((d.field('BIN_LO') + d.field('BIN_HI')) / 2., d.field('VAL'), s=0, k=1)
    else :        
        logging.warning('Using dummy background rate extracted from SubarrayE_IFAE_50hours_20101102')
        bgrate_p1 = [9., 5.E-4, 1.44, .49] # Fit from SubarrayE_IFAE_50hours_20101102
        log_e_cen = np.linspace(-1.5, 2., 35.)
        p_rate_area = bpl(bgrate_p1, 10. ** log_e_cen)
        p_rate_area[log_e_cen < -1.] = .4

    # DEBUG plot
    plt.semilogy(log_e_cen, p_rate_area)
    plt.show()

    p_rate_total = np.sum(p_rate_area)

    ev_gen_f = scipy.interpolate.UnivariateSpline(
        np.cumsum(p_rate_area) / np.sum(p_rate_area),
        log_e_cen,
        s=0,
        k=1
        )

    cam_acc = lambda p, x: p[0] * x ** 0. * (1. + (x / p[1]) ** p[2]) ** ((0. + p[3]) / p[2])
    cam_acc_par = (1.,1.7, 6., -5.5)

    r_steps = np.linspace(0.001, 4., 150)
    int_cam_acc = np.zeros(150)
    for i, r in enumerate(r_steps) :
        int_cam_acc[i] = scipy.integrate.quad(lambda x: cam_acc(cam_acc_par, x) * x * 2. * np.pi, 0., r)[0]

    n_events_bg = int(p_rate_total * obstime * int_cam_acc[-1])

    logging.debug('Number of protons : {0}'.format(n_events_bg))

    tplt_multi = 5
    evlist_bg_e = ev_gen_f(np.random.rand(n_events_bg * (tplt_multi + 1)))

    ev_gen_f2 = scipy.interpolate.UnivariateSpline(
        int_cam_acc / scipy.integrate.quad(lambda x: cam_acc(cam_acc_par, x) * 2. * x * np.pi, 0., 4.)[0],
        r_steps,
        s=0,
        k=1
        )

    evlist_bg_r = ev_gen_f2(np.random.rand(n_events_bg * (tplt_multi + 1)))

    r_max = 4.
    #evlist_bg_r = np.sqrt(np.random.rand(n_events_bg * (tplt_multi + 1))) * r_max
    rnd = np.random.rand(n_events_bg * (1 + tplt_multi))
    evlist_bg_rx = np.sqrt(rnd) * evlist_bg_r * np.where(np.random.randint(2, size=(n_events_bg * (tplt_multi + 1))) == 0, -1., 1.)
    evlist_bg_ry = np.sqrt(1. - rnd) * evlist_bg_r * np.where(np.random.randint(2, size=(n_events_bg * (tplt_multi + 1))) == 0, -1., 1.)

    #evlist_bg_sky_r = np.sqrt(np.random.rand(n_events_bg * (tplt_multi + 1))) * r_max
    #evlist_bg_sky_r = ev_gen_f2(np.random.rand(n_events_bg * (tplt_multi + 1)))
    rnd = np.random.rand(n_events_bg * (tplt_multi + 1))
    evlist_bg_ra = np.sin(2. * np.pi * rnd) * evlist_bg_r / objcosdec
    evlist_bg_dec = np.cos(2. * np.pi * rnd)  * evlist_bg_r

    #plt.hist(evlist_bg_rx ** 2. + evlist_bg_ry**2., bins=50)

    #print float(n_events_bg * (tplt_multi + 1)) / np.sum(p_rate_area) / 86400.
    evlist_bg_t = t_min + obstime *  np.random.rand(n_events_bg * (tplt_multi + 1)) / 86400.

    #---------------------------------------------------------------------------
    # Plots & debug
    
    plt.figure(3)

    objra, objdec = 0., 0.
    H, xedges, yedges = np.histogram2d(
        np.append(evlist_bg_dec, evlist_dec),
        np.append(evlist_bg_ra, evlist_ra),
        bins=[100, 100],
        range=[[objra - 3., objra + 3.], [objdec - 3., objdec + 3.]]
        )
    extent = [yedges[0], yedges[-1], xedges[-1], xedges[0]]
    plt.imshow(H, extent=extent, interpolation='nearest')
    cb = plt.colorbar()
    cb.set_label('Number of events')

    plt.xlabel('RA (deg)')
    plt.ylabel('Dec (deg)')

    test_r = np.sqrt(evlist_bg_ra ** 2. + evlist_bg_dec ** 2.)
    logging.debug('Number of BG events in a circle of area 1 deg^2 = {0}'.format(np.sum(test_r[0:n_events_bg] < np.sqrt(1. / np.pi))))
    logging.debug('Expected number of BG event per area 1 deg^2 = {0}'.format(p_rate_total * obstime))

    obj_r = np.sqrt(((obj_ra - evlist_ra) / objcosdec) ** 2. + (obj_dec - evlist_dec) ** 2.)

    thetamax_on, thetamax_off = .1, .22
    non = np.sum(obj_r < thetamax_on) + np.sum(test_r[0:n_events_bg] < thetamax_on)
    noff = np.sum(test_r[0:n_events_bg] < thetamax_off)
    alpha = thetamax_on ** 2. / thetamax_off ** 2.

    logging.info('N_ON = {0}, N_OFF = {1}, ALPHA = {2}, SIGN = {3}'.format(
        non, noff, alpha, pf.get_li_ma_sign(non, noff, alpha)))

    plt.figure(2)
    plt.hist(obj_r ** 2., bins=30)

    #---------------------------------------------------------------------------
    # Output to file
    
    if output_filename_base:
        logging.info('Writing eventlist to file {0}.eventlist.fits'.format(output_filename_base))
        
        newtable = pyfits.new_table(
            pyfits.ColDefs([
                pyfits.Column(name='TIME', format='1E', unit='deg', array=np.append(evlist_t, evlist_bg_t)),
                pyfits.Column(name='RA', format='1E', unit='deg', array=np.append(evlist_ra, evlist_bg_ra)),
                pyfits.Column(name='DEC', format='1E', unit='deg', array=np.append( evlist_dec, evlist_bg_dec)),
                pyfits.Column(name='DETX', format='1E', unit='deg', array=np.append(np.zeros(n_events), evlist_bg_rx)),
                pyfits.Column(name='DETY', format='1E', unit='deg', array=np.append(np.ones(n_events) * .5, evlist_bg_ry)),
                pyfits.Column(name='ENERGY', format='1E', unit='tev', array=10. ** np.append(evlist_e,evlist_bg_e)),
                pyfits.Column(name='HIL_MSW', format='1E', array=np.append(np.zeros(n_events + n_events_bg),
                                                                           5. * np.ones(n_events_bg * tplt_multi))),
                pyfits.Column(name='HIL_MSL', format='1E', array=np.append(np.zeros(n_events + n_events_bg),
                                                                           5. * np.ones(n_events_bg * tplt_multi))) 
                ])
            )
        #newtable = pyfits.new_table(coldefs_new)
        dstart = datetime.datetime(2011, 1, 1, 0, 0) # date/time of start of the first observation
        dstop = dstart + datetime.timedelta(seconds=obstime) # date/time of end of the last observation
        dbase = datetime.datetime(2011, 1, 1)

        hdr = newtable.header
        
        hdr.update('RA_OBJ' , obj_ra, 'Target position RA [deg]')
        hdr.update('DEC_OBJ', obj_dec, 'Target position dec [deg]')
        hdr.update('RA_PNT', pnt_ra, 'Observation position RA [deg]')
        hdr.update('DEC_PNT', pnt_dec, 'Observation position dec [deg]')
        hdr.update('EQUINOX ', 2000.0, 'Equinox of the object')
        hdr.update('RADECSYS', 'FK5', 'Co-ordinate frame used for equinox')
        hdr.update('CREATOR', 'pfsim v{0}'.format(pf.__version__) , 'Program')
        hdr.update('DATE', datetime.datetime.today().strftime('%Y-%m-%d'), 'FITS file creation date (yyyy-mm-dd)')
        hdr.update('TELESCOP', 'CTASIM', 'Instrument name')
        hdr.update('EXTNAME', 'EVENTS' , 'HESARC standard')
        hdr.update('DATE_OBS', dstart.strftime('%Y-%m-%d'), 'Obs. start date (yy-mm-dd)')
        hdr.update('TIME_OBS', dstart.strftime('%H:%M:%S'), 'Obs. start time (hh:mm::ss)')
        hdr.update('DATE_END', dstop.strftime('%Y-%m-%d'), 'Obs. stop date (yy-mm-dd)')
        hdr.update('TIME_END', dstop.strftime('%H:%M:%S'), 'Obs. stop time (hh:mm::ss)')
        hdr.update('TSTART', 0., 'Mission time of start of obs [s]')
        hdr.update('TSTOP', obstime, 'Mission time of end of obs [s]')
        hdr.update('MJDREFI', int(pf.date_to_mjd(dstart)), 'Integer part of start MJD [s] ')
        hdr.update('MJDREFF', pf.date_to_mjd(dstart) - int(pf.date_to_mjd(dstart)), 'Fractional part of start MJD')
        hdr.update('TIMEUNIT', 'days' , 'Time unit of MJD')
        hdr.update('TIMESYS', 'TT', 'Terrestrial Time')
        hdr.update('TIMEREF', 'local', '')
        hdr.update('TELAPSE', obstime, 'Diff of start and end times')
        hdr.update('ONTIME', obstime, 'Tot good time (incl deadtime)') # No deadtime assumed
        hdr.update('LIVETIME', obstime, 'Deadtime=ONTIME/LIVETIME') # No deadtime assumed
        hdr.update('DEADC', 1., 'Deadtime fraction') # No deadtime assumed
        hdr.update('TIMEDEL', 1., 'Time resolution')
        hdr.update('EUNIT', 'TeV', 'Energy unit')
        hdr.update('EVTVER', 'v1.0.0', 'Event-list version number')
        #hdr.update('', , '')

        # Write eventlist to file
        newtable.writeto('{0}.eventlist.fits'.format(output_filename_base))

        if write_pha :
            logging.info('Writing PHA to file {0}.pha.fits'.format(output_filename_base))
            # Prepare data
            dat, t = np.histogram(10. ** evlist_e, bins=rm_ebounds)
            dat = np.array(dat, dtype=float)
            dat_err = np.sqrt(dat)
            chan = np.arange(len(dat))
            # Data to PHA
            tbhdu = pf.np_to_pha(counts=dat, stat_err=dat_err, channel=chan, exposure=obstime, obj_ra=obj_ra, obj_dec=obj_dec,
                                 quality=np.where((dat == 0), 1, 0),
                                 dstart=dstart, dstop=dstop, dbase=dbase, creator='pfsim', version=pf.__version__,
                                 telescope='CTASIM')
            tbhdu.header.update('ANCRFILE', os.path.basename(arf), 'Ancillary response file (ARF)')
            if rmf :
                tbhdu.header.update('RESPFILE', os.path.basename(rmf), 'Redistribution matrix file (RMF)')

            # Write PHA to file
            tbhdu.writeto('{0}.pha.fits'.format(output_filename_base))

    #----------------------------------------
    # Time it!
    t_2 = time.clock()
    logging.info('Execution took {0}'.format(pf.get_nice_time(t_2 - t_1)))

    logging.info('Thank you for choosing {0}. Have a great day!'.format(os.path.split(__file__)[1]))

    #----------------------------------------
    if do_graphical_output:
        plt.show()
Esempio n. 2
0
def create_sky_map(input_file_name,
                   skymap_size=5.,
                   skymap_bin_size=0.05,
                   r_overs=.125,
                   ring_bg_radii=None,
                   template_background=None,
                   skymap_center=None,
                   write_output=False,
                   fov_acceptance=False,
                   do_graphical_output=True,
                   loglevel='INFO') :
    # Time it!
    t_1 = time.clock()

    # Configure logging
    numeric_level = getattr(logging, loglevel.upper(), None)
    if not isinstance(numeric_level, int):
        raise ValueError('Invalid log level: %s' % loglevel)
    logging.basicConfig(level=numeric_level,
                        format='%(asctime)s - %(levelname)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')

    # Welcome user, print out basic information on package versions
    logging.info('This is {0} (pyfact v{1})'.format(os.path.split(__file__)[1], pf.__version__))
    logging.info('We are running with numpy v{0}, scipy v{1}, and pyfits v{2}'.format(
        np.__version__, scipy.__version__, pyfits.__version__
        ))

    #---------------------------------------------------------------------------
    # Loop over the file list, calculate quantities, & fill histograms

    # Skymap definition
    #skymap_size, skymap_bin_size = 6., 0.05
    rexdeg = .3

    # Intialize some variables
    skycenra, skycendec, pntra, pntdec = None, None, None, None
    if skymap_center :
        skycenra, skycendec = eval(skymap_center)
        logging.info('Skymap center: RA {0}, Dec {1}'.format(skycenra, skycendec))

    ring_bg_r_min, ring_bg_r_max = .3, .7
    if ring_bg_radii :
        ring_bg_r_min, ring_bg_r_max = eval(ring_bg_radii)

    if r_overs > ring_bg_r_min :
        logging.warning('Oversampling radius is larger than the inner radius chosen for the ring BG: {0} > {1}'.format(r_overs, ring_bg_r_min))

    logging.info('Skymap size         : {0} deg'.format(skymap_size))
    logging.info('Skymap bin size     : {0} deg'.format(skymap_bin_size))
    logging.info('Oversampling radius : {0} deg'.format(r_overs))
    logging.info('Ring BG radius      : {0} - {1} deg'.format(ring_bg_r_min, ring_bg_r_max))

    skymap_nbins, sky_dec_min, sky_dec_max, objcosdec, sky_ra_min, sky_ra_max = 0, 0., 0., 0., 0., 0.
    sky_hist, acc_hist, extent = None, None, None
    tpl_had_hist, tpl_acc_hist = None, None
    sky_ex_reg, sky_ex_reg_map = None, None

    telescope, object_ = 'NONE', 'NONE'

    firstloop = True

    exposure = 0.

    # Read in input file, can be individual fits or bankfile
    logging.info('Opening input file ..')

    def get_filelist(input_file_name) :
        # Check if we are dealing with a single file or a bankfile
        # and create/read in the file list accordingly
        try :
            f = pyfits.open(input_file_name)
            f.close()
            file_list = [input_file_name]
        except :
            # We are dealing with a bankfile
            logging.info('Reading files from bankfile {0}'.format(input_file_name))
            file_list = np.loadtxt(input_file_name, dtype='S', usecols=[0])
        return file_list

    file_list = get_filelist(input_file_name)

    tpl_file_list = None

    # Read in template files
    if template_background :
        tpl_file_list = get_filelist(template_background)
        if len(file_list) != len(tpl_file_list) :
            logging.warning('Different number of signal and template background files. Switching off template background analysis.')
            template_background = None

    # Main loop over input files
    for i, file_name in enumerate(file_list) :

        logging.info('Processing file {0}'.format(file_name))

        def get_evl(file_name) :
            # Open fits file
            hdulist = pyfits.open(file_name)
            # Access header of second extension
            hdr = hdulist['EVENTS'].header
            # Access data of first extension
            tbdata = hdulist['EVENTS'].data
            # Calculate some useful quantities and add them to the table
            # Distance from the camera (FOV) center
            camdist = np.sqrt(tbdata.field('DETX') ** 2. + tbdata.field('DETY') ** 2.)
            camdist_col = pyfits.Column(name='XCAMDIST', format='1E', unit='deg', array=camdist)
            # Add new columns to the table
            coldefs_new = pyfits.ColDefs([camdist_col])
            #coldefs_new = pyfits.ColDefs([camdist_col, detx_col, dety_col])
            newtable = pyfits.new_table(hdulist[1].columns + coldefs_new)
            # New table data
            return hdulist, hdr, newtable.data

        hdulist, ex1hdr, tbdata = get_evl(file_name)

        # Read in eventlist for template background
        tpl_hdulist, tpl_tbdata = None, None
        if template_background :
            tpl_hdulist, tpl_hdr, tpl_tbdata = get_evl(tpl_file_list[i])

        #---------------------------------------------------------------------------
        # Intialize & GTI

        objra, objdec = ex1hdr['RA_OBJ'], ex1hdr['DEC_OBJ']
        if firstloop :
            # If skymap center is not set, set it to the object position of the first run
            if skycenra == None or skycendec == None :
                skycenra, skycendec = objra, objdec
                logging.debug('Setting skymap center to skycenra, skycendec = {0}, {1}'.format(skycenra, skycendec))
            if 'TELESCOP' in ex1hdr :
                telescope = ex1hdr['TELESCOP']
                logging.debug('Setting TELESCOP to {0}'.format(telescope))
            if 'OBJECT' in ex1hdr :
                object_ = ex1hdr['OBJECT']
                logging.debug('Setting OBJECT to {0}'.format(object_))

        mgit, tpl_mgit = np.ones(len(tbdata), dtype=np.bool), None
        if template_background :
            tpl_mgit = np.ones(len(tpl_tbdata), dtype=np.bool)
        try :
            # Note: according to the eventlist format document v1.0.0 Section 10
            # "The times are expressed in the same units as in the EVENTS
            # table (seconds since mission start in terresterial time)."
            for gti in hdulist['GTI'].data :
                mgit *= (tbdata.field('TIME') >= gti[0]) * (tbdata.field('TIME') <= gti[1])
                if template_background :
                    tpl_mgit *= (tpl_tbdata.field('TIME') >= gti[0]) * (tpl_tbdata.field('TIME') <= gti[1])
        except :
            logging.warning('File does not contain a GTI extension')
        
        #---------------------------------------------------------------------------
        #  Handle exclusion region

        # If no exclusion regions are given, use the object position from the first run
        if sky_ex_reg == None :
            sky_ex_reg = [pf.SkyCircle(pf.SkyCoord(objra, objdec), rexdeg)]
            logging.info('Setting exclusion region to object position (ra={0}, dec={1}, r={2}'.format(objra, objdec, rexdeg))

        pntra, pntdec = ex1hdr['RA_PNT'], ex1hdr['DEC_PNT']
        obj_cam_dist = pf.SkyCoord(skycenra, skycendec).dist(pf.SkyCoord(pntra, pntdec))

        exposure_run = ex1hdr['LIVETIME']
        exposure += exposure_run

        logging.info('RUN Start date/time : {0} {1}'.format(ex1hdr['DATE_OBS'], ex1hdr['TIME_OBS']))
        logging.info('RUN Stop date/time  : {0} {1}'.format(ex1hdr['DATE_END'], ex1hdr['TIME_END']))
        logging.info('RUN Exposure        : {0:.2f} [s]'.format(exposure_run))
        logging.info('RUN Pointing pos.   : RA {0:.4f} [deg], Dec {1:.4f} [deg]'.format(pntra, pntdec))
        logging.info('RUN Obj. cam. dist. : {0:.4f} [deg]'.format(obj_cam_dist))
        
        # Cut out source region for acceptance fitting
        exmask = None
        for sc in sky_ex_reg :
            if exmask :
                exmask *= sc.c.dist(pf.SkyCoord(tbdata.field('RA'), tbdata.field('DEC'))) < sc.r
            else :
                exmask = sc.c.dist(pf.SkyCoord(tbdata.field('RA'), tbdata.field('DEC'))) < sc.r
        exmask = np.invert(exmask)

        photbdata = tbdata[exmask * mgit]
        if len(photbdata) < 10 :
            logging.warning('Less then 10 events found in file {0} after exclusion region cuts'.format(file_name))

        hadtbdata = None
        if template_background :
            exmask = None
            for sc in sky_ex_reg :
                if exmask :
                    exmask *= sc.c.dist(pf.SkyCoord(tpl_tbdata.field('RA'), tpl_tbdata.field('DEC'))) < sc.r
                else :
                    exmask = sc.c.dist(pf.SkyCoord(tpl_tbdata.field('RA'), tpl_tbdata.field('DEC'))) < sc.r
            exmask = np.invert(exmask)
            hadtbdata = tpl_tbdata[exmask * tpl_mgit]

        #---------------------------------------------------------------------------
        # Calculate camera acceptance
        
        n, bins, nerr, r, r_a, ex_a, fitter = pf.get_cam_acc(
            photbdata.field('XCAMDIST'),
            exreg=[(sc.r, sc.c.dist(pf.SkyCoord(pntra, pntdec))) for sc in sky_ex_reg],
            fit=True,
            )

        # DEBUG
        if logging.root.level is logging.DEBUG :
            fitter.print_results()
            # DEBUG plot
            plt.errorbar(r, n / r_a / (1. - ex_a), nerr / r_a / (1. - ex_a))
            plt.plot(r, fitter.fitfunc(fitter.results[0], r))
            plt.show()

        had_acc, had_n, had_fit = None, None, None
        if template_background :
            had_acc = pf.get_cam_acc(
                hadtbdata.field('XCAMDIST'),
                exreg=[(sc.r, sc.c.dist(pf.SkyCoord(pntra, pntdec))) for sc in sky_ex_reg],
                fit=True
                )
            had_n, had_fit = had_acc[0], had_acc[6]
            logging.debug('Camera acceptance hadrons fit probability: {0}'.format(had_fit.prob))

        # !!! All photons including the exclusion regions
        photbdata = tbdata[mgit]
        if template_background :
            hadtbdata = tpl_tbdata[tpl_mgit]
        if len(photbdata) < 10 :
            logging.warning('Less then 10 events found in file {0} after GTI cut.'.format(file_name))

        tpl_acc_cor_use_interp = True
        tpl_acc_f, tpl_acc = None, None
        if template_background :
            if tpl_acc_cor_use_interp :
                tpl_acc_f = scipy.interpolate.UnivariateSpline(r, n.astype(float) / had_n.astype(float), s=0, k=1)
            else :
                tpl_acc_f = lambda r: fitter.fitfunc(p1, r) / had_fit.fitfunc(had_fit.results[0], r)
            tpl_acc = tpl_acc_f(hadtbdata.field('XCAMDIST'))
            m = hadtbdata.field('XCAMDIST') > r[-1]
            tpl_acc[m] = tpl_acc_f(r[-1])
            m = hadtbdata.field('XCAMDIST') < r[0]
            tpl_acc[m] = tpl_acc_f(r[0])

        #---------------------------------------------------------------------------
        # Skymap - definitions/calculation

        # Object position in the sky
        if firstloop :
            #skycenra, objdec, skymap_size = ex1hdr['RA_OBJ'], ex1hdr['DEC_OBJ'], 6.
            #if skycenra == None or objdec == None :
            #    skycenra, objdec = ex1hdr['RA_OBJ'], ex1hdr['DEC_OBJ']

            # Calculate skymap limits
            skymap_nbins = int(skymap_size / skymap_bin_size)
            sky_dec_min, sky_dec_max = skycendec - skymap_size / 2., skycendec + skymap_size / 2.
            objcosdec = np.cos(skycendec * np.pi / 180.)
            sky_ra_min, sky_ra_max = skycenra - skymap_size / 2. / objcosdec, skycenra + skymap_size / 2. / objcosdec

            logging.debug('skymap_nbins = {0}'.format(skymap_nbins))
            logging.debug('sky_dec_min, sky_dec_max = {0}, {1}'.format(sky_dec_min, sky_dec_max))
            logging.debug('sky_ra_min, sky_ra_max = {0}, {1}'.format(sky_ra_min, sky_ra_max))

        # Create sky map (i.e. bin events)
        # NOTE: In histogram2d the first axes is the vertical (y, DEC) the 2nd the horizontal axes (x, RA)
        sky = np.histogram2d(x=photbdata.field('DEC     '), y=photbdata.field('RA      '),
                             bins=[skymap_nbins, skymap_nbins],
                             range=[[sky_dec_min, sky_dec_max], [sky_ra_min, sky_ra_max]])

        if firstloop :
            # Just used to have the x-min/max, y-min/max saved
            H, xedges, yedges = sky
            # NOTE: The zero point of the histogram 2d is at the lower left corner while
            #       the pyplot routine imshow takes [0,0] at the upper left corner (i.e.
            #       we have to invert the 1st axis before plotting, see below).
            #       Also, imshow uses the traditional 1st axes = x = RA, 2nd axes = y = DEC
            #       notation for the extend keyword
            #extent = [yedges[0], yedges[-1], xedges[-1], xedges[0]]
            extent = [yedges[0], yedges[-1], xedges[0], xedges[-1]]
            sky_hist = sky[0]

            sky_ex_reg_map = pf.get_exclusion_region_map(sky_hist,
                                                         (sky_ra_min, sky_ra_max),
                                                         (sky_dec_min, sky_dec_max),
                                                         sky_ex_reg)
        else :
            sky_hist += sky[0]

        # Calculate camera acceptance
        dec_a = np.linspace(sky_dec_min, sky_dec_max, skymap_nbins + 1)
        ra_a = np.linspace(sky_ra_min, sky_ra_max, skymap_nbins + 1)
        xx, yy = np.meshgrid((ra_a[:-1] + ra_a[1:]) / 2. - pntra, (dec_a[:-1] + dec_a[1:]) / 2. - pntdec)
        rr = np.sqrt(xx ** 2. + yy ** 2.)
        p1 = fitter.results[0]
        acc = fitter.fitfunc(p1, rr) / fitter.fitfunc(p1, .01)
        m = rr > 4.
        acc[m] = fitter.fitfunc(p1, 4.) / fitter.fitfunc(p1, .01)
        if not fov_acceptance :
            logging.debug('Do _not_ apply FoV acceptance correction')
            acc = acc * 0. + 1.

        # DEBUG plot
        #plt.imshow(acc[::-1], extent=extent, interpolation='nearest')
        #plt.colorbar()
        #plt.title('acc_bg_overs')
        #plt.show()
        
        if firstloop :
            acc_hist = acc * exposure_run # acc[0] before
        else :
            acc_hist += acc * exposure_run # acc[0] before

        if template_background :
            # Create hadron event like map for template background
            tpl_had = np.histogram2d(x=hadtbdata.field('DEC     '), y=hadtbdata.field('RA      '),
                                     bins=[skymap_nbins, skymap_nbins],
                                     #weights=1./accept,
                                     range=[[sky_dec_min, sky_dec_max], [sky_ra_min, sky_ra_max]])
            if firstloop :
                tpl_had_hist = tpl_had[0]
            else :
                tpl_had_hist += tpl_had[0]

            # Create acceptance map for template background
            tpl_acc = np.histogram2d(x=hadtbdata.field('DEC     '), y=hadtbdata.field('RA      '),
                                     bins=[skymap_nbins, skymap_nbins],
                                     weights=tpl_acc,
                                     range=[[sky_dec_min, sky_dec_max], [sky_ra_min, sky_ra_max]])
            if firstloop :
                tpl_acc_hist = tpl_acc[0]
            else :
                tpl_acc_hist += tpl_acc[0]

        # Close fits file
        hdulist.close()
        if tpl_hdulist :
            tpl_hdulist.close()

        # Clean up memory
        newtable = None
        gc.collect()

        firstloop = False

    #---------------------------------------------------------------------------
    # Calculate final skymaps

    logging.info('Processing final sky maps')

    # Calculate oversampled skymap, ring background, excess, and significances
    sc = pf.get_sky_mask_circle(r_overs, skymap_bin_size)
    sr = pf.get_sky_mask_ring(ring_bg_r_min, ring_bg_r_max, skymap_bin_size)
    acc_hist /= exposure

    logging.info('Calculating oversampled event map ..')
    sky_overs, sky_overs_alpha = pf.oversample_sky_map(sky_hist, sc)

    logging.info('Calculating oversampled ring background map ..')
    sky_bg_ring, sky_bg_ring_alpha = pf.oversample_sky_map(sky_hist, sr, sky_ex_reg_map)

    logging.info('Calculating oversampled event acceptance map ..')
    acc_overs, acc_overs_alpha = pf.oversample_sky_map(acc_hist, sc)

    logging.info('Calculating oversampled ring background acceptance map ..')
    acc_bg_overs, acc_bg_overs_alpha = pf.oversample_sky_map(acc_hist, sr, sky_ex_reg_map)

    rng_alpha = acc_hist / acc_bg_overs # camera acceptance
    rng_exc = sky_hist - sky_bg_ring * rng_alpha
    rng_sig = pf.get_li_ma_sign(sky_hist, sky_bg_ring, rng_alpha)

    rng_alpha_overs = acc_overs / acc_bg_overs # camera acceptance
    rng_exc_overs = sky_overs - sky_bg_ring * rng_alpha_overs
    rng_sig_overs = pf.get_li_ma_sign(sky_overs, sky_bg_ring, rng_alpha_overs)

    tpl_had_overs, tpl_sig_overs, tpl_exc_overs, tpl_alpha_overs = None, None, None, None

    if template_background :

        logging.info('Calculating oversampled template background map ..')
        tpl_had_overs, tpl_had_overs_alpha = pf.oversample_sky_map(tpl_had_hist, sc)

        logging.info('Calculating oversampled template acceptance map ..')
        tpl_acc_overs, tpl_acc_overs_alpha = pf.oversample_sky_map(tpl_acc_hist, sc)
        
        tpl_exc_overs = sky_overs - tpl_acc_overs
        tpl_alpha_overs = tpl_acc_overs / tpl_had_overs
        tpl_sig_overs = pf.get_li_ma_sign(sky_overs, tpl_had_overs, tpl_alpha_overs)

    #---------------------------------------------------------------------------
    # Write results to file

    if write_output :

        logging.info('Writing result to file ..')

        rarange, decrange = (sky_ra_min, sky_ra_max), (sky_dec_min, sky_dec_max)

        outfile_base_name = 'skymap_ring'
        outfile_data = {
            '_ev.fits': sky_hist,
            '_ac.fits': acc_hist,
            '_ev_overs.fits': sky_overs,
            '_bg_overs.fits': sky_bg_ring,
            '_si_overs.fits': rng_sig_overs,
            '_ex_overs.fits': rng_exc_overs,
            '_al_overs.fits': rng_alpha_overs,
            '_si.fits': rng_sig,
            '_ex.fits': rng_exc,
            '_al.fits': rng_alpha
            }
        outfile_base_name = pf.unique_base_file_name(outfile_base_name, outfile_data.keys())

        for ext, data in outfile_data.iteritems() :
            pf.map_to_primaryhdu(data, rarange, decrange, author='PyFACT pfmap',
                                 object_=object_, telescope=telescope).writeto(outfile_base_name + ext)

        if template_background :
            outfile_base_name = 'skymap_template'
            outfile_data = {
                '_bg.fits': tpl_had_hist,
                '_ac.fits': tpl_acc_hist,
                '_bg_overs.fits': tpl_had_overs,
                '_si_overs.fits': tpl_sig_overs,
                '_ex_overs.fits': tpl_exc_overs,
                '_al_overs.fits': tpl_alpha_overs,
                #'_si.fits': rng_sig,
                #'_ex.fits': rng_exc,
                #'_al.fits': rng_alpha
                }
            outfile_base_name = pf.unique_base_file_name(outfile_base_name, outfile_data.keys())

            for ext, data in outfile_data.iteritems() :
                pf.map_to_primaryhdu(data, rarange, decrange, author='PyFACT pfmap',
                                     object_=object_, telescope=telescope).writeto(outfile_base_name + ext)

        logging.info('The output files can be found in {0}'.format(os.getcwd()))

    #---------------------------------------------------------------------------
    # Plot results

    if has_matplotlib and do_graphical_output :

        import matplotlib
        logging.info('Plotting results (matplotlib v{0})'.format(matplotlib.__version__))

        plot_skymap(sky_overs, rng_exc_overs, rng_sig_overs, sky_bg_ring, rng_alpha_overs, 'Ring BG overs.',
                    sky_ra_min, sky_ra_max, sky_dec_min, sky_dec_max, objcosdec, r_overs, extent,
                    skycenra, skycendec,
                    ring_bg_r_min, ring_bg_r_max, sign_hist_r_max = 2.)

        if template_background :
            plot_skymap(sky_overs, tpl_exc_overs, tpl_sig_overs, tpl_had_overs, tpl_alpha_overs,
                        'Template BG overs.',
                        sky_ra_min, sky_ra_max, sky_dec_min, sky_dec_max, objcosdec, r_overs, extent,
                        skycenra, skycendec, sign_hist_r_max = 2.)

    #----------------------------------------
    # Time it!
    t_2 = time.clock()
    logging.info('Execution took {0}'.format(pf.get_nice_time(t_2 - t_1)))

    logging.info('Thank you for choosing {0}. Have a great day!'.format(os.path.split(__file__)[1]))

    #----------------------------------------
    plt.show()