Пример #1
0
    def __init__(self, *argv):
        """
        Constructor.
        """
        # Set name
        self._name = 'csobsinfo'
        self._version = '1.1.0'

        # Initialise class members
        self._obj_dir = None
        self._compute_offset = False
        self._offsets = []
        self._zeniths = []
        self._azimuths = []
        self._pnt_ra = []
        self._pnt_dec = []
        self._ebounds = gammalib.GEbounds()
        self._gti = gammalib.GGti()

        # Initialise observation container from constructor arguments.
        self._obs, argv = self._set_input_obs(argv)

        # Initialise application by calling the appropriate class
        # constructor.
        self._init_cscript(argv)

        # Return
        return
Пример #2
0
    def __init__(self, *argv):
        """
        Constructor.
        """
        # Set name
        self._name = "cslightcrv"
        self._version = "1.1.0"

        # Initialise some members
        self._srcname = ""
        self._tbins = gammalib.GGti()
        self._stacked = False
        self._fits = gammalib.GFits()

        # Initialise observation container from constructor arguments.
        self._obs, argv = self._set_input_obs(argv)

        # Initialise script by calling the appropriate class constructor.
        self._init_cscript(argv)

        # Set logger properties
        self._log_header()
        self._log.date(True)

        # Return
        return
Пример #3
0
def createobs(ra=86.171648, dec=-1.4774586, rad=5.0,
              emin=0.1, emax=100.0, duration=360000.0, deadc=0.95,
              ):
    obs = gammalib.GCTAObservation()

    # Set pointing direction
    pntdir = gammalib.GSkyDir()
    pntdir.radec_deg(ra, dec)
    pnt = gammalib.GCTAPointing()
    pnt.dir(pntdir)
    obs.pointing(pnt)

    # Set ROI
    roi = gammalib.GCTARoi()
    instdir = gammalib.GCTAInstDir()
    instdir.dir(pntdir)
    roi.centre(instdir)
    roi.radius(rad)

    # Set GTI
    gti = gammalib.GGti()
    start = gammalib.GTime(0.0)
    stop = gammalib.GTime(duration)
    gti.append(start, stop)

    # Set energy boundaries
    ebounds = gammalib.GEbounds()
    e_min = gammalib.GEnergy()
    e_max = gammalib.GEnergy()
    e_min.TeV(emin)
    e_max.TeV(emax)
    ebounds.append(e_min, e_max)

    # Allocate event list
    events = gammalib.GCTAEventList()
    events.roi(roi)
    events.gti(gti)
    events.ebounds(ebounds)
    obs.events(events)

    # Set ontime, livetime, and deadtime correction factor
    obs.ontime(duration)
    obs.livetime(duration * deadc)
    obs.deadc(deadc)

    # Return observation
    return obs
Пример #4
0
    def __init__(self, *argv):
        """
        Constructor
        """
        # Initialise application by calling the appropriate class constructor
        self._init_csobservation(self.__class__.__name__, ctools.__version__,
                                 argv)

        # Initialise some members
        self._srcname = ''
        self._tbins = gammalib.GGti()
        self._onoff = False
        self._stacked = False
        self._fits = gammalib.GFits()

        # Return
        return
Пример #5
0
def create_lightcurve_gti(obsname, gtiname):
    """
    Generate lightcurve for GTIs

    Parameters
    ----------
    obsname : str
        Observation definition XML file
    gtiname : str
        GTI output filename
    """
    # Load observations
    obs = gammalib.GObservations(obsname)

    # Initialize GTIs
    gti = gammalib.GGti()

    # Loop over observations
    for run in obs:

        # Get start time and ontime of GTIs
        tstart = run.gti().tstart().copy()
        ontime = run.gti().ontime()

        # Set time bins
        tbin = ontime / 14.0

        # Loop over time bins
        for i in range(14):

            # Compute stop time
            tstop = tstart + tbin

            # Append GTI
            gti.append(tstart, tstop)

            # Update start time
            tstart = tstop.copy()

    # Save GTIs
    gti.save(gtiname, True)

    # Return
    return
Пример #6
0
    def __init__(self, *argv):
        """
        Constructor.
        """
        # Initialise application by calling the appropriate class constructor
        self._init_csobservation(self.__class__.__name__, ctools.__version__,
                                 argv)

        # Initialise class members
        self._obj_dir = None
        self._compute_offset = False
        self._offsets = []
        self._zeniths = []
        self._azimuths = []
        self._pnt_ra = []
        self._pnt_dec = []
        self._ebounds = gammalib.GEbounds()
        self._gti = gammalib.GGti()

        # Return
        return
Пример #7
0
    def _create_tbounds(self):
        """
        Creates light curve time bins.

        The method reads the following user parameters:
            tbinalg:  Time binning algorithm
            tbinfile: Time binning file (FITS or ASCII)
            tmin:     Start time (MJD)
            tmax:     Stop time (MJD)
            tbins:    Number of time bins

        Returns:
            Light curve bins in form of a GTI.
        """
        # Initialise Good Time Intervals
        gti = gammalib.GGti()

        # Get algorithm to use for defining the time intervals
        algorithm = self["tbinalg"].string()

        # Handle a FITS or a ASCII file for time bin definition
        if algorithm == "FILE":

            # Get the filename
            filename = self["tbinfile"].filename()

            # If the file a FITS file then load GTIs
            if filename.is_fits():
                gti.load(filename)

            # ... otherwise load file as CSV ASCII file
            csv = gammalib.GCsv(filename)
            for i in range(csv.nrows()):
                tmin = gammalib.GTime()
                tmax = gammalib.GTime()
                tmin.mjd(csv.real(i, 0))
                tmax.mjd(csv.real(i, 1))
                gti.append(tmin, tmax)

        # Handle linear time binning
        elif algorithm == "LIN":

            # Get start and stop time and number of time bins
            time_min = self["tmin"].real()
            time_max = self["tmax"].real()
            nbins = self["tbins"].integer()

            # Compute time step and setup time intervals
            time_step = (time_max - time_min) / float(nbins)
            for i in range(nbins):
                tmin = gammalib.GTime()
                tmax = gammalib.GTime()
                tmin.mjd(time_min + i * time_step)
                tmax.mjd(time_min + (i + 1) * time_step)
                gti.append(tmin, tmax)

        # Handle usage of observation GTIs
        elif algorithm == "GTI":

            # Append the GTIs of all observations
            for obs in self._obs:
                for i in range(obs.events().gti().size()):
                    gti.append(obs.events().gti().tstart(i),
                               obs.events().gti().tstop(i))

        # ... otherwise raise an exception (this should never occur)
        else:
            raise AttributeError('Paramter tbinalg="' + algorithm +
                                 '" unknown. '
                                 'Must be one of "FILE", "LIN" or "GTI".')

        # Return Good Time Intervals
        return gti
Пример #8
0
    def run(self):
        """
        Run the script

        Raises
        ------
        RuntimeError
            Invalid pointing definition file format
        """
        # Switch screen logging on in debug mode
        if self._logDebug():
            self._log.cout(True)

        # Get parameters
        self._get_parameters()

        # Write header into logger
        self._log_header1(gammalib.TERSE,
                          'Creating observation definition XML file')

        # Load pointing definition file if it is not already set. Extract
        # the number of columns and pointings
        if self._pntdef.size() == 0:
            self._pntdef = gammalib.GCsv(self['inpnt'].filename(), ',')
        ncols = self._pntdef.ncols()
        npnt = self._pntdef.nrows() - 1

        # Raise an exception if there is no header information
        if self._pntdef.nrows() < 1:
            raise RuntimeError('No header found in pointing definition file.')

        # Clear observation container
        self._obs.clear()

        # Initialise observation identifier counter
        identifier = 1

        # Extract header columns from pointing definition file and put them
        # into a list
        header = []
        for col in range(ncols):
            header.append(self._pntdef[0, col])

        # Loop over all pointings
        for pnt in range(npnt):

            # Set pointing definition CSV file row index
            row = pnt + 1

            # Create empty CTA observation
            obs = gammalib.GCTAObservation()

            # Set observation name. If no observation name was given then
            # use "None".
            if 'name' in header:
                name = self._pntdef[row, header.index('name')]
            else:
                name = self['name'].string()
            obs.name(name)

            # Set observation identifier. If no observation identified was
            # given the use the internal counter.
            if 'id' in header:
                obsid = self._pntdef[row, header.index('id')]
            else:
                obsid = '%6.6d' % identifier
                identifier += 1
            obs.id(obsid)

            # Set pointing. Either use "ra" and "dec" or "lon" and "lat".
            # If none of these pairs are given then raise an exception.
            if 'ra' in header and 'dec' in header:
                ra = float(self._pntdef[row, header.index('ra')])
                dec = float(self._pntdef[row, header.index('dec')])
                pntdir = gammalib.GSkyDir()
                pntdir.radec_deg(ra, dec)
            elif 'lon' in header and 'lat' in header:
                lon = float(self._pntdef[row, header.index('lon')])
                lat = float(self._pntdef[row, header.index('lat')])
                pntdir = gammalib.GSkyDir()
                pntdir.lb_deg(lon, lat)
            else:
                raise RuntimeError('No (ra,dec) or (lon,lat) columns '
                                   'found in pointing definition file.')
            obs.pointing(gammalib.GCTAPointing(pntdir))

            # Set response function. If no "caldb" or "irf" information is
            # provided then use the user parameter values.
            if 'caldb' in header:
                caldb = self._pntdef[row, header.index('caldb')]
            else:
                caldb = self['caldb'].string()
            if 'irf' in header:
                irf = self._pntdef[row, header.index('irf')]
            else:
                irf = self['irf'].string()
            if caldb != '' and irf != '':
                obs = self._set_irf(obs, caldb, irf)

            # Set deadtime correction factor. If no information is provided
            # then use the user parameter value "deadc".
            if 'deadc' in header:
                deadc = float(self._pntdef[row, header.index('deadc')])
            else:
                deadc = self['deadc'].real()
            obs.deadc(deadc)

            # Set Good Time Interval. If no information is provided then use
            # the user parameter values "tmin" and "duration".
            if 'tmin' in header:
                self._tmin = float(self._pntdef[row, header.index('tmin')])
            if 'duration' in header:
                duration = float(self._pntdef[row, header.index('duration')])
            else:
                duration = self['duration'].real()
            tref = gammalib.GTimeReference(self['mjdref'].real(), 's')
            tmin = self._tmin
            tmax = self._tmin + duration
            gti = gammalib.GGti(tref)
            tstart = gammalib.GTime(tmin, tref)
            tstop = gammalib.GTime(tmax, tref)
            self._tmin = tmax
            gti.append(tstart, tstop)
            obs.ontime(gti.ontime())
            obs.livetime(gti.ontime() * deadc)

            # Set Energy Boundaries. If no "emin" or "emax" information is
            # provided then use the user parameter values in case they are
            # valid.
            has_emin = False
            has_emax = False
            if 'emin' in header:
                emin = float(self._pntdef[row, header.index('emin')])
                has_emin = True
            else:
                if self['emin'].is_valid():
                    emin = self['emin'].real()
                    has_emin = True
            if 'emax' in header:
                emax = float(self._pntdef[row, header.index('emax')])
                has_emax = True
            else:
                if self['emax'].is_valid():
                    emax = self['emax'].real()
                    has_emax = True
            has_ebounds = has_emin and has_emax
            if has_ebounds:
                ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, 'TeV'),
                                            gammalib.GEnergy(emax, 'TeV'))

            # Set ROI. If no ROI radius is provided then use the user
            # parameters "rad".
            has_roi = False
            if 'rad' in header:
                rad = float(self._pntdef[row, header.index('rad')])
                has_roi = True
            else:
                if self['rad'].is_valid():
                    rad = self['rad'].real()
                    has_roi = True
            if has_roi:
                roi = gammalib.GCTARoi(gammalib.GCTAInstDir(pntdir), rad)

            # Create an empty event list
            event_list = gammalib.GCTAEventList()
            event_list.gti(gti)

            # If available, set the energy boundaries and the ROI
            if has_ebounds:
                event_list.ebounds(ebounds)
            if has_roi:
                event_list.roi(roi)

            # Attach event list to CTA observation
            obs.events(event_list)

            # Write observation into logger
            name = obs.instrument() + ' observation'
            value = 'Name="%s" ID="%s"' % (obs.name(), obs.id())
            self._log_value(gammalib.NORMAL, name, value)
            self._log_string(gammalib.EXPLICIT, str(obs) + '\n')

            # Append observation
            self._obs.append(obs)

        # Return
        return
Пример #9
0
def set_obs(pntdir, tstart=0.0, duration=1800.0, deadc=0.95, \
            emin=0.1, emax=100.0, rad=5.0, \
            irf="South_50h", caldb="prod2", id="000000"):
    """
    Set a single CTA observation.
    
    The function sets a single CTA observation containing an empty CTA
    event list. By looping over this function you can add CTA observations
    to the observation container.

    Args:
        pntdir: Pointing direction [GSkyDir]

    Kwargs:
        tstart:   Start time (seconds) (default: 0.0)
        duration: Duration of observation (seconds) (default: 1800.0)
        deadc:    Deadtime correction factor (default: 0.95)
        emin:     Minimum event energy (TeV) (default: 0.1)
        emax:     Maximum event energy (TeV) (default: 100.0)
        rad:      ROI radius used for analysis (deg) (default: 5.0)
        irf:      Instrument response function (default: "South_50h")
        caldb:    Calibration database path (default: "prod2")
        id:       Run identifier (default: "000000")
    """
    # Allocate CTA observation
    obs_cta = gammalib.GCTAObservation()

    # Set calibration database
    db = gammalib.GCaldb()
    if (gammalib.dir_exists(caldb)):
        db.rootdir(caldb)
    else:
        db.open("cta", caldb)

    # Set pointing direction
    pnt = gammalib.GCTAPointing()
    pnt.dir(pntdir)
    obs_cta.pointing(pnt)

    # Set ROI
    roi = gammalib.GCTARoi()
    instdir = gammalib.GCTAInstDir()
    instdir.dir(pntdir)
    roi.centre(instdir)
    roi.radius(rad)

    # Set GTI
    gti = gammalib.GGti()
    gti.append(gammalib.GTime(tstart), gammalib.GTime(tstart + duration))

    # Set energy boundaries
    ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, "TeV"),
                                gammalib.GEnergy(emax, "TeV"))

    # Allocate event list
    events = gammalib.GCTAEventList()
    events.roi(roi)
    events.gti(gti)
    events.ebounds(ebounds)
    obs_cta.events(events)

    # Set instrument response
    obs_cta.response(irf, db)

    # Set ontime, livetime, and deadtime correction factor
    obs_cta.ontime(duration)
    obs_cta.livetime(duration * deadc)
    obs_cta.deadc(deadc)
    obs_cta.id(id)

    # Return CTA observation
    return obs_cta
Пример #10
0
    def _create_tbounds(self):
        """
        Creates light curve time bins

        Returns
        -------
        gti : `~gammalib.GGti`
            Light curve bins in form of Good Time Intervals
        """
        # Initialise Good Time Intervals
        gti = gammalib.GGti()

        # Get algorithm to use for defining the time intervals. Possible
        # values are "FILE", "LIN" and "GTI". This is enforced at
        # parameter file level, hence no checking is needed.
        algorithm = self['tbinalg'].string()

        # If the algorithm is "FILE" then handle a FITS or an ASCII file for
        # the time bin definition
        if algorithm == 'FILE':

            # Get the filename
            filename = self['tbinfile'].filename()

            # If the file a FITS file then load GTIs directly
            if filename.is_fits():
                gti.load(filename)

            # ... otherwise load file the ASCII file as CSV file and construct
            # the GTIs from the rows of the CSV file. It is expected that the
            # CSV file has two columns containing the "START" and "STOP"
            # values of the time bins. No header row is expected.
            csv = gammalib.GCsv(filename)
            for i in range(csv.nrows()):
                tmin = gammalib.GTime()
                tmax = gammalib.GTime()
                tmin.mjd(csv.real(i, 0))
                tmax.mjd(csv.real(i, 1))
                gti.append(tmin, tmax)

        # If the algorithm is "LIN" then use a linear time binning, defined by
        # the "tmin", "tmax" and "tbins" user parameters
        elif algorithm == 'LIN':

            # Get start and stop time and number of time bins
            time_min = self['tmin'].time(ctools.time_reference)
            time_max = self['tmax'].time(ctools.time_reference)
            nbins = self['tbins'].integer()

            # Compute time step in seconds and setup the GTIs
            time_step = (time_max - time_min) / float(nbins)
            for i in range(nbins):
                tmin = time_min + i * time_step
                tmax = time_min + (i + 1) * time_step
                gti.append(tmin, tmax)

        # If the algorithm is "GTI" then extract the GTIs from the observations
        # in the container and use them for the light curve time binning
        elif algorithm == 'GTI':

            # Append the GTIs of all observations
            for obs in self.obs():
                for i in range(obs.events().gti().size()):
                    gti.append(obs.events().gti().tstart(i),
                               obs.events().gti().tstop(i))

        # Return Good Time Intervals
        return gti
Пример #11
0
    def _test_get_stacked_response(self):
        """
        Test get_stacked_response() function
        """
        # Set-up observation container
        obs = self._setup_sim()

        # Set-up counts cube
        map     = gammalib.GSkyMap('CAR','CEL',83.6331,22.0145,0.1,0.1,10,10,5)
        emin    = gammalib.GEnergy(0.1, 'TeV')
        emax    = gammalib.GEnergy(100.0, 'TeV')
        ebds    = gammalib.GEbounds(5, emin, emax)
        tmin    = gammalib.GTime(0.0)
        tmax    = gammalib.GTime(1000.0)
        gti     = gammalib.GGti(tmin, tmax)
        cntcube = gammalib.GCTAEventCube(map, ebds, gti)

        # Get stacked response
        res = obsutils.get_stacked_response(obs, cntcube, edisp=False)

        # Check result
        self.test_value(res['expcube'].cube().npix(), 100,
                        'Check number of exposure cube pixels')
        self.test_value(res['expcube'].cube().nmaps(), 91,
                        'Check number of exposure cube maps')
        self.test_value(res['psfcube'].cube().npix(), 4,
                        'Check number of PSF cube pixels')
        self.test_value(res['psfcube'].cube().nmaps(), 18200,
                        'Check number of PSF cube maps')
        self.test_value(res['bkgcube'].cube().npix(), 100,
                        'Check number of background cube pixels')
        self.test_value(res['bkgcube'].cube().nmaps(), 5,
                        'Check number of background cube maps')

        # Get stacked response with energy dispersion
        res = obsutils.get_stacked_response(obs, cntcube, edisp=True)

        # Check result
        self.test_value(res['expcube'].cube().npix(), 100,
                        'Check number of exposure cube pixels')
        self.test_value(res['expcube'].cube().nmaps(), 105,
                        'Check number of exposure cube maps')
        self.test_value(res['psfcube'].cube().npix(), 4,
                        'Check number of PSF cube pixels')
        self.test_value(res['psfcube'].cube().nmaps(), 21000,
                        'Check number of PSF cube maps')
        self.test_value(res['bkgcube'].cube().npix(), 100,
                        'Check number of background cube pixels')
        self.test_value(res['bkgcube'].cube().nmaps(), 5,
                        'Check number of background cube maps')
        self.test_value(res['edispcube'].cube().npix(), 4,
                        'Check number of energy dispersion cube pixels')
        self.test_value(res['edispcube'].cube().nmaps(), 10500,
                        'Check number of energy dispersion cube maps')

        # Set-up counts cube with large number of energy bins
        ebds    = gammalib.GEbounds(100, emin, emax)
        cntcube = gammalib.GCTAEventCube(map, ebds, gti)

        # Get stacked response with xref/yref set and large number of energy bins
        res = obsutils.get_stacked_response(obs, cntcube, edisp=False)

        # Check result
        self.test_value(res['expcube'].cube().npix(), 100,
                        'Check number of exposure cube pixels')
        self.test_value(res['expcube'].cube().nmaps(), 91,
                        'Check number of exposure cube maps')
        self.test_value(res['psfcube'].cube().npix(), 4,
                        'Check number of PSF cube pixels')
        self.test_value(res['psfcube'].cube().nmaps(), 18200,
                        'Check number of PSF cube maps')
        self.test_value(res['bkgcube'].cube().npix(), 100,
                        'Check number of background cube pixels')
        self.test_value(res['bkgcube'].cube().nmaps(), 100,
                        'Check number of background cube maps')

        # Return
        return
Пример #12
0
    def run(self):
        """
        Run the script.

        Raises
        ------
        RuntimeError
            Invalid pointing definition file format.
        """
        # Switch screen logging on in debug mode
        if self._logDebug():
            self._log.cout(True)

        # Get parameters
        self._get_parameters()

        # Write header into logger
        if self._logTerse():
            self._log('\n')
            self._log.header1('Creating observation definition XML file')

        # Load pointing definition file if it is not already set
        if self._pntdef.size() == 0:
            self._pntdef = gammalib.GCsv(self['inpnt'].filename(), ',')
        ncols = self._pntdef.ncols()
        npnt  = self._pntdef.nrows()-1

        # Throw an exception is there is no header information
        if self._pntdef.nrows() < 1:
            raise RuntimeError('No header found in pointing definition file.')

        # Clear observation container
        self._obs.clear()
        identifier = 1

        # Extract header from pointing definition file
        header = []
        for col in range(ncols):
            header.append(self._pntdef[0,col])

        # Loop over all pointings
        for pnt in range(npnt):

            # Set row index
            row = pnt + 1

            # Create CTA observation
            obs = gammalib.GCTAObservation()

            # Set observation name
            if 'name' in header:
                name = self._pntdef[row, header.index('name')]
            else:
                name = 'None'
            obs.name(name)

            # Set identifier
            if 'id' in header:
                id_ = self._pntdef[row, header.index('id')]
            else:
                id_ = '%6.6d' % identifier
                identifier += 1
            obs.id(id_)

            # Set pointing
            if 'ra' in header and 'dec' in header:
                ra     = float(self._pntdef[row, header.index('ra')])
                dec    = float(self._pntdef[row, header.index('dec')])
                pntdir = gammalib.GSkyDir()
                pntdir.radec_deg(ra,dec)
            elif 'lon' in header and 'lat' in header:
                lon    = float(self._pntdef[row, header.index('lon')])
                lat    = float(self._pntdef[row, header.index('lat')])
                pntdir = gammalib.GSkyDir()
                pntdir.lb_deg(lon,lat)
            else:
                raise RuntimeError('No (ra,dec) or (lon,lat) columns '
                                   'found in pointing definition file.')
            obs.pointing(gammalib.GCTAPointing(pntdir))

            # Set response function
            if 'caldb' in header:
                caldb = self._pntdef[row, header.index('caldb')]
            else:
                caldb = self['caldb'].string()
            if 'irf' in header:
                irf = self._pntdef[row, header.index('irf')]
            else:
                irf = self['irf'].string()
            if caldb != '' and irf != '':
                obs = self._set_response(obs, caldb, irf)

            # Set deadtime correction factor
            if 'deadc' in header:
                deadc = float(self._pntdef[row, header.index('deadc')])
            else:
                deadc = self['deadc'].real()
            obs.deadc(deadc)

            # Set Good Time Interval
            if 'duration' in header:
                duration = float(self._pntdef[row, header.index('duration')])
            else:
                duration = self['duration'].real()
            tmin       = self._tmin
            tmax       = self._tmin + duration
            gti        = gammalib.GGti(self._time_reference())
            tstart     = gammalib.GTime(tmin, self._time_reference())
            tstop      = gammalib.GTime(tmax, self._time_reference())
            self._tmin = tmax
            gti.append(tstart, tstop)
            obs.ontime(gti.ontime())
            obs.livetime(gti.ontime()*deadc)

            # Set Energy Boundaries
            has_emin = False
            has_emax = False
            if 'emin' in header:
                emin     = float(self._pntdef[row, header.index('emin')])
                has_emin = True
            else:
                if self['emin'].is_valid():
                    emin     = self['emin'].real()
                    has_emin = True
            if 'emax' in header:
                emax     = float(self._pntdef[row, header.index('emax')])
                has_emax = True
            else:
                if self['emax'].is_valid():
                    emax     = self['emax'].real()
                    has_emax = True
            has_ebounds = has_emin and has_emax
            if has_ebounds:
                ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, 'TeV'),
                                            gammalib.GEnergy(emax, 'TeV'))

            # Set ROI
            has_roi = False
            if 'rad' in header:
                rad     = float(self._pntdef[row, header.index('rad')])
                has_roi = True
            else:
                if self['rad'].is_valid():
                    rad     = self['rad'].real()
                    has_roi = True
            if has_roi:
                roi = gammalib.GCTARoi(gammalib.GCTAInstDir(pntdir), rad)

            # Create an empty event list
            list_ = gammalib.GCTAEventList()
            list_.gti(gti)

            # Set optional information
            if has_ebounds:
                list_.ebounds(ebounds)
            if has_roi:
                list_.roi(roi)

            # Attach event list to CTA observation
            obs.events(list_)

            # Write observation into logger
            if self._logExplicit():
                self._log(str(obs))
                self._log('\n')
            elif self._logTerse():
                self._log(gammalib.parformat(obs.instrument()+' observation'))
                self._log('Name="'+obs.name()+'" ')
                self._log('ID="'+obs.id()+'"\n')

            # Append observation
            self._obs.append(obs)

        # Return
        return
Пример #13
0
def set_obs(pntdir, tstart=0.0, duration=1800.0, deadc=0.98, \
            emin=0.1, emax=100.0, rad=5.0, \
            irf='South_50h', caldb='prod2', obsid='000000'):
    """
    Set a single CTA observation
    
    The function sets a single CTA observation containing an empty CTA
    event list. By looping over this function CTA observations can be
    added to the observation container.

    Parameters
    ----------
    pntdir : `~gammalib.GSkyDir`
        Pointing direction
    tstart : float, optional
        Start time (s)
    duration : float, optional
        Duration of observation (s)
    deadc : float, optional
        Deadtime correction factor
    emin : float, optional
        Minimum event energy (TeV)
    emax : float, optional
        Maximum event energy (TeV)
    rad : float, optional
        ROI radius used for analysis (deg)
    irf : str, optional
        Instrument response function
    caldb : str, optional
        Calibration database path
    obsid : str, optional
        Observation identifier

    Returns
    -------
    obs : `~gammalib.GCTAObservation`
        CTA observation
    """
    # Allocate CTA observation
    obs = gammalib.GCTAObservation()

    # Set CTA calibration database
    db = gammalib.GCaldb()
    if (gammalib.dir_exists(caldb)):
        db.rootdir(caldb)
    else:
        db.open('cta', caldb)

    # Set pointing direction for CTA observation
    pnt = gammalib.GCTAPointing()
    pnt.dir(pntdir)
    obs.pointing(pnt)

    # Set ROI
    roi = gammalib.GCTARoi()
    instdir = gammalib.GCTAInstDir()
    instdir.dir(pntdir)
    roi.centre(instdir)
    roi.radius(rad)

    # Set GTI
    gti = gammalib.GGti()
    gti.append(gammalib.GTime(tstart), gammalib.GTime(tstart + duration))

    # Set energy boundaries
    ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, 'TeV'),
                                gammalib.GEnergy(emax, 'TeV'))

    # Allocate event list
    events = gammalib.GCTAEventList()

    # Set ROI, GTI and energy boundaries for event list
    events.roi(roi)
    events.gti(gti)
    events.ebounds(ebounds)

    # Set the event list as the events for CTA observation
    obs.events(events)

    # Set instrument response for CTA observation
    obs.response(irf, db)

    # Set ontime, livetime, and deadtime correction factor for CTA observation
    obs.ontime(duration)
    obs.livetime(duration * deadc)
    obs.deadc(deadc)
    obs.id(obsid)

    # Return CTA observation
    return obs
Пример #14
0
    def run(self):
        """
        Run the script
        """
        # Switch screen logging on in debug mode
        if self._logDebug():
            self._log.cout(True)

        # Get parameters
        self._get_parameters()

        # Initialise arrays to store certain values for reuse
        # Todo, think about using a python dictionary
        self._offsets = []
        self._zeniths = []
        self._azimuths = []
        self._pnt_ra = []
        self._pnt_dec = []
        self._ebounds = gammalib.GEbounds()
        self._gti = gammalib.GGti()
        obs_names = []

        # Initialise output to be filled
        ontime = 0.0
        livetime = 0.0
        n_events = 0
        n_eventbins = 0
        n_obs_binned = 0
        n_obs_unbinned = 0

        # Write header
        if self._logTerse():
            self._log('\n')
            self._log.header1(gammalib.number('Observation', self._obs.size()))

        # Loop over observations
        for obs in self._obs:

            # Skip non-CTA observations
            if not obs.classname() == 'GCTAObservation':
                self._log('Skipping ' + obs.instrument() + ' observation\n')
                continue

            # Use observed object as observation name if name is not given
            obs_name = obs.name()
            if obs_name == '':
                obs_name = obs.object()

            # Logging
            if self._logTerse():
                self._log.header2(obs_name)

            # Retrieve observation name
            obs_names.append(obs_name)

            # Retrieve energy boundaries
            obs_bounds = obs.events().ebounds()

            # Retrieve time interval
            obs_gti = obs.events().gti()

            # Compute mean time and dead time fraction in percent
            deadfrac = (1.0 - obs.deadc()) * 100.0

            # Retrieve pointing and store Ra,Dec
            pnt_dir = obs.pointing().dir()
            self._pnt_ra.append(pnt_dir.ra_deg())
            self._pnt_dec.append(pnt_dir.dec_deg())

            # If avaliable append energy boundaries
            if obs_bounds.size() > 0:
                self._ebounds.append(obs_bounds.emin(), obs_bounds.emax())

            # Append time interval
            self._gti.append(obs_gti.tstart(), obs_gti.tstop())

            # Increment global livetime and ontime
            ontime += obs.ontime()
            livetime += obs.livetime()

            # Bookkeeping
            if obs.eventtype() == 'CountsCube':
                n_eventbins += obs.events().size()
                n_obs_binned += 1
                if self._logTerse():
                    self._log.parformat('Binned')
                    self._log('yes\n')
                    self._log.parformat('Number of bins')
                    self._log(str(obs.events().size()))
                    self._log('\n')
            else:
                n_events += obs.events().size()
                n_obs_unbinned += 1
                if self._logTerse():
                    self._log.parformat('Binned')
                    self._log('no\n')
                    self._log.parformat('Number of events')
                    self._log(str(obs.events().size()))
                    self._log('\n')

            # Retrieve zenith and azimuth and store for later use
            zenith = obs.pointing().zenith()
            azimuth = obs.pointing().azimuth()
            self._zeniths.append(zenith)
            self._azimuths.append(azimuth)

            # Optionally compute offset with respect to target direction
            if self._compute_offset:
                offset = pnt_dir.dist_deg(self._obj_dir)
                self._offsets.append(offset)
            else:
                self._offsets.append(-1.0)

            # Optionally log details
            if self._logTerse():

                # Log the observation energy range (if available)
                self._log.parformat('Energy range')
                if obs_bounds.size() == 0:
                    self._log('undefined')
                else:
                    self._log(str(obs_bounds.emin()))
                    self._log(' - ')
                    self._log(str(obs_bounds.emax()))
                self._log('\n')

                # Log observation time interval
                self._log.parformat('Time range (MJD)')
                if obs_gti.size() == 0:
                    self._log('undefined')
                else:
                    self._log(str(obs_gti.tstart().mjd()))
                    self._log(' - ')
                    self._log(str(obs_gti.tstop().mjd()))
                self._log('\n')

                # Log ontime
                self._log.parformat('Ontime')
                self._log(str(obs.ontime()))
                self._log(' s\n')

                # Log livetime
                self._log.parformat('Livetime')
                self._log(str(obs.livetime()))
                self._log(' s\n')

                # Log dead time fraction
                self._log.parformat('Deadtime fraction (%)')
                self._log('%.3f' % (deadfrac))
                self._log('\n')

                # Log pointing direction
                self._log.parformat('Pointing')
                self._log(str(pnt_dir))
                self._log('\n')

                # Optionally log offset with respect to target direction
                if self._compute_offset:
                    self._log.parformat('Offset from target')
                    self._log('%.2f' % (offset))
                    self._log(' deg\n')

                # Log Zenith and Azimuth if required
                self._log.parformat('Zenith angle')
                self._log('%.2f' % (zenith))
                self._log(' deg\n')
                self._log.parformat('Azimuth angle')
                self._log('%.2f' % (azimuth))
                self._log(' deg\n')

        # Log summary
        if self._logTerse():

            # Write header
            self._log('\n')
            self._log.header1('Summary')

            # Log general summary
            self._log.header3('Observations')
            self._log.parformat('Unbinned observations')
            self._log(str(n_obs_unbinned))
            self._log('\n')
            self._log.parformat('Binned observations')
            self._log(str(n_obs_binned))
            self._log('\n')
            self._log.header3('Events')
            self._log.parformat('Number of events')
            self._log(str(n_events))
            self._log('\n')
            self._log.parformat('Number of bins')
            self._log(str(n_eventbins))
            self._log('\n')

            # Log pointing summary
            self._log.header3('Pointings')

            # Log mean offset if possible
            if self._compute_offset:
                self._log.parformat('Mean offset angle')
                self._log('%.2f' % (sum(self._offsets) / len(self._offsets)))
                self._log(' deg\n')

            # Log mean azimuth and zenith angle
            self._log.parformat('Mean zenith angle')
            self._log('%.2f' % (sum(self._zeniths) / len(self._zeniths)))
            self._log(' deg\n')
            self._log.parformat('Mean azimuth angle')
            self._log('%.2f' % (sum(self._azimuths) / len(self._azimuths)))
            self._log(' deg\n')

            # Optionally log names of observations. Note that the set class
            # is used to extract all different observation names from the
            # list of observation names, and the set class is only available
            # from Python 2.4 on.
            if self._logExplicit() and sys.version_info >= (2, 4):
                obs_set = set(obs_names)
                for name in obs_set:
                    self._log.parformat('"' + name + '"')
                    self._log(str(obs_names.count(name)))
                    self._log('\n')
                self._log('\n')

            # Log energy range
            self._log.header3('Energy range')
            self._log.parformat('Minimum energy')
            if self._ebounds.size() == 0:
                self._log('undefined')
            else:
                self._log(str(self._ebounds.emin()))
            self._log('\n')
            self._log.parformat('Maximum energy')
            if self._ebounds.size() == 0:
                self._log('undefined')
            else:
                self._log(str(self._ebounds.emax()))
            self._log('\n')

            # Log time range
            self._log.header3('Time range')
            self._log.parformat('Start (MJD)')
            self._log(str(self._gti.tstart().mjd()))
            self._log('\n')
            self._log.parformat('Stop (MJD)')
            self._log(str(self._gti.tstop().mjd()))
            self._log('\n')

            # Log ontime and livetime in different units
            self._log.parformat('Total ontime')
            self._log('%.2f s = %.2f min = %.2f h' %
                      (ontime, ontime / 60., ontime / 3600.))
            self._log('\n')
            self._log.parformat('Total livetime')
            self._log('%.2f s = %.2f min = %.2f h' %
                      (livetime, livetime / 60.0, livetime / 3600.))
            self._log('\n')

        # Return
        return
Пример #15
0
    def run(self):
        """
        Run the script
        """
        # Switch screen logging on in debug mode
        if self._logDebug():
            self._log.cout(True)

        # Get parameters
        self._get_parameters()

        # Initialise arrays to store certain values for reuse
        # Todo, think about using a python dictionary
        self._offsets = []
        self._zeniths = []
        self._azimuths = []
        self._pnt_ra = []
        self._pnt_dec = []
        self._ebounds = gammalib.GEbounds()
        self._gti = gammalib.GGti()
        obs_names = []

        # Initialise output to be filled
        ontime = 0.0
        livetime = 0.0
        n_events = 0
        n_eventbins = 0
        n_obs_binned = 0
        n_obs_unbinned = 0

        # Write header
        if self._logTerse():
            self._log('\n')
            self._log.header1(gammalib.number('Observation',
                                              self.obs().size()))

        # Loop over observations
        for obs in self.obs():

            # Skip non-CTA observations
            if not obs.classname() == 'GCTAObservation':
                self._log('Skipping ' + obs.instrument() + ' observation\n')
                continue

            # Use observed object as observation name if name is not given
            obs_name = obs.name()
            if obs_name == '':
                obs_name = obs.object()

            # Logging
            if self._logExplicit():
                obs_id = obs.id()
                if obs_id != '':
                    log_name = obs_name + ' (ID=' + obs_id + ')'
                else:
                    log_name = obs_name
                self._log.header2(log_name)

            # Retrieve observation name
            obs_names.append(obs_name)

            # Retrieve energy boundaries
            obs_bounds = obs.events().ebounds()

            # Retrieve time interval
            obs_gti = obs.events().gti()

            # Compute mean time and dead time fraction in percent
            deadfrac = (1.0 - obs.deadc()) * 100.0

            # Retrieve pointing and store Ra,Dec
            pnt_dir = obs.pointing().dir()
            self._pnt_ra.append(pnt_dir.ra_deg())
            self._pnt_dec.append(pnt_dir.dec_deg())

            # If avaliable append energy boundaries
            if obs_bounds.size() > 0:
                self._ebounds.append(obs_bounds.emin(), obs_bounds.emax())

            # Append time interval
            self._gti.append(obs_gti.tstart(), obs_gti.tstop())

            # Increment global livetime and ontime
            ontime += obs.ontime()
            livetime += obs.livetime()

            # Bookkeeping
            if obs.eventtype() == 'CountsCube':
                n_eventbins += obs.events().size()
                n_obs_binned += 1
                is_binned = 'yes'
                is_what = 'Number of bins'
            else:
                n_events += obs.events().size()
                n_obs_unbinned += 1
                is_binned = 'no'
                is_what = 'Number of events'
            self._log_value(gammalib.EXPLICIT, 'Binned', is_binned)
            self._log_value(gammalib.EXPLICIT, is_what, obs.events().size())

            # Retrieve zenith and azimuth and store for later use
            zenith = obs.pointing().zenith()
            azimuth = obs.pointing().azimuth()
            self._zeniths.append(zenith)
            self._azimuths.append(azimuth)

            # Optionally compute offset with respect to target direction
            if self._compute_offset:
                offset = pnt_dir.dist_deg(self._obj_dir)
                self._offsets.append(offset)

            # Optionally log details
            if self._logExplicit():

                # Log the observation energy range (if available)
                self._log.parformat('Energy range')
                if obs_bounds.size() == 0:
                    self._log('undefined')
                else:
                    self._log(str(obs_bounds.emin()))
                    self._log(' - ')
                    self._log(str(obs_bounds.emax()))
                self._log('\n')

                # Log observation time interval
                self._log.parformat('Time range (MJD)')
                if obs_gti.size() == 0:
                    self._log('undefined')
                else:
                    self._log(str(obs_gti.tstart().mjd()))
                    self._log(' - ')
                    self._log(str(obs_gti.tstop().mjd()))
                self._log('\n')

            # Log observation information
            self._log_value(gammalib.EXPLICIT, 'Ontime',
                            '%.3f s' % obs.ontime())
            self._log_value(gammalib.EXPLICIT, 'Livetime',
                            '%.3f s' % obs.livetime())
            self._log_value(gammalib.EXPLICIT, 'Deadtime fraction',
                            '%.3f %%' % deadfrac)
            self._log_value(gammalib.EXPLICIT, 'Pointing', pnt_dir)

            # Optionally log offset with respect to target direction
            if self._compute_offset:
                self._log_value(gammalib.EXPLICIT, 'Offset from target',
                                '%.2f deg' % offset)

            # Log Zenith and Azimuth angles
            self._log_value(gammalib.EXPLICIT, 'Zenith angle',
                            '%.2f deg' % zenith)
            self._log_value(gammalib.EXPLICIT, 'Azimuth angle',
                            '%.2f deg' % azimuth)

        # Write summary header
        self._log_header1(gammalib.NORMAL, 'Summary')

        # Log general summary
        self._log_header3(gammalib.NORMAL, 'Observations')
        self._log_value(gammalib.NORMAL, 'Unbinned observations',
                        n_obs_unbinned)
        self._log_value(gammalib.NORMAL, 'Binned observations', n_obs_binned)
        self._log_header3(gammalib.NORMAL, 'Events')
        self._log_value(gammalib.NORMAL, 'Number of events', n_events)
        self._log_value(gammalib.NORMAL, 'Number of bins', n_eventbins)

        # Compute mean offset, azimuth and zenith angle
        if len(self._offsets) > 0:
            mean_offset = '%.2f deg' % (sum(self._offsets) /
                                        len(self._offsets))
        else:
            mean_offset = 'Unknown'
        if len(self._zeniths) > 0:
            mean_zenith = '%.2f deg' % (sum(self._zeniths) /
                                        len(self._zeniths))
        else:
            mean_zenith = 'Unknown'
        if len(self._azimuths) > 0:
            mean_azimuth = '%.2f deg' % (sum(self._azimuths) /
                                         len(self._azimuths))
        else:
            mean_azimuth = 'Unknown'

        # Log mean offset, azimuth and zenith angle
        self._log_header3(gammalib.NORMAL, 'Pointings')
        self._log_value(gammalib.NORMAL, 'Mean offset angle', mean_offset)
        self._log_value(gammalib.NORMAL, 'Mean zenith angle', mean_zenith)
        self._log_value(gammalib.NORMAL, 'Mean azimuth angle', mean_azimuth)

        # Optionally log names of observations. Note that the set class is
        # used to extract all different observation names from the list of
        # observation names, and the set class is only available from
        # Python 2.4 on.
        if sys.version_info >= (2, 4):
            obs_set = set(obs_names)
            for name in obs_set:
                self._log_value(gammalib.EXPLICIT, '"' + name + '"',
                                obs_names.count(name))

        # Get energy boundary information
        if self._ebounds.size() == 0:
            min_value = 'undefined'
            max_value = 'undefined'
        else:
            min_value = str(self._ebounds.emin())
            max_value = str(self._ebounds.emax())

        # Log energy range
        self._log_header3(gammalib.NORMAL, 'Energy range')
        self._log_value(gammalib.NORMAL, 'Minimum energy', min_value)
        self._log_value(gammalib.NORMAL, 'Maximum energy', max_value)

        # Log time range
        mjd = '%.3f - %.3f' % (self._gti.tstart().mjd(),
                               self._gti.tstop().mjd())
        utc = '%s - %s' % (self._gti.tstart().utc(), self._gti.tstop().utc())
        self._log_header3(gammalib.NORMAL, 'Time range')
        self._log_value(gammalib.NORMAL, 'MJD (days)', mjd)
        self._log_value(gammalib.NORMAL, 'UTC', utc)

        # Log ontime and livetime in different units
        on_time   = '%.2f s = %.2f min = %.2f h' % \
                    (ontime, ontime/60., ontime/3600.)
        live_time = '%.2f s = %.2f min = %.2f h' % \
                    (livetime, livetime/60., livetime/3600.)
        self._log_value(gammalib.NORMAL, 'Total ontime', on_time)
        self._log_value(gammalib.NORMAL, 'Total livetime', live_time)

        # Return
        return
Пример #16
0
    pnt = gammalib.GCTAPointing()
    pnt.dir(pntdir)
    obs.pointing(pnt)

    #Set ROI

    roi = gammalib.GCTARoi()
    instdir = gammalib.GCTAInstDir()
    instdir.dir(pntdir)
    roi.centre(instdir)
    roi.radius(rad)

    #Set GTI

    gti = gammalib.GGti()
    start = gammalib.GTime(tstart)
    stop = gammalib.GTime(tstart + duration)
    gti.append(start, stop)

    #Set Energy Boundaries
    ebounds = gammalib.GEbounds()
    e_min = gammalib.GEnergy(emin, 'TeV')
    e_max = gammalib.GEnergy(emax, 'TeV')
    ebounds.append(e_min, e_max)

    #Allocate event list
    events = gammalib.GCTAEventList(eventfile)
    obs.eventfile(eventfile)
    events.roi(roi)
    events.gti(gti)
Пример #17
0
def set(RA=83.63,
        DEC=22.01,
        tstart=0.0,
        duration=1800.0,
        deadc=0.95,
        emin=0.1,
        emax=100.0,
        rad=5.0,
        irf="cta_dummy_irf",
        caldb="$GAMMALIB/share/caldb/cta"):
    """
    Create one CTA observation

    Copied from ctools/scripts/obsutils.py and modified a bit.
    """
    # Allocate CTA observation
    obs = gammalib.GCTAObservation()

    # Set pointing direction
    pntdir = gammalib.GSkyDir()
    pntdir.radec_deg(RA, DEC)

    pnt = gammalib.GCTAPointing()
    pnt.dir(pntdir)
    obs.pointing(pnt)

    # Set ROI
    roi = gammalib.GCTARoi()
    instdir = gammalib.GCTAInstDir()
    instdir.dir(pntdir)
    roi.centre(instdir)
    roi.radius(rad)

    # Set GTI
    gti = gammalib.GGti()
    start = gammalib.GTime(tstart)
    stop = gammalib.GTime(tstart + duration)
    gti.append(start, stop)

    # Set energy boundaries
    ebounds = gammalib.GEbounds()
    e_min = gammalib.GEnergy()
    e_max = gammalib.GEnergy()
    e_min.TeV(emin)
    e_max.TeV(emax)
    ebounds.append(e_min, e_max)

    # Allocate event list
    events = gammalib.GCTAEventList()
    events.roi(roi)
    events.gti(gti)
    events.ebounds(ebounds)
    obs.events(events)

    # Set instrument response
    obs.response(irf, caldb)

    # Set ontime, livetime, and deadtime correction factor
    obs.ontime(duration)
    obs.livetime(duration * deadc)
    obs.deadc(deadc)

    # Return observation
    return obs
def show_residuals(obslist=None,
                   emin=0.2,
                   emax=20.0,
                   ebins=20,
                   npix=200,
                   binsz=0.02,
                   suffix=''):
    """
    Show residuals for all OFF observations

    Parameters
    ----------
    obslist : list of ints, optional
        Index of observations to stack
    emin : float, optional
        Minimum energy (TeV)
    emax : float, optional
        Maximum energy (TeV)
    ebins : int, optional
        Number of energy bins
    npix : int, optional
        Number of spatial pixels
    binsz : float, optional
        Spatial bin size
    suffix : str, optional
        Plot filename suffix
    """
    # Set XML filename
    obsname = '$HESSDATA/obs/obs_off.xml'

    # Generate background lookup
    generate_background_lookup(obslist=obslist, suffix=suffix)

    # If observation list is specified and suffix is empty then build suffix
    if obslist != None and suffix == '':
        for obs in obslist:
            suffix += '_%2.2d' % obs

    # Set stacked cube filenames
    cntfile = 'off_stacked_counts%s.fits' % (suffix)
    modfile = 'off_stacked_model%s.fits' % (suffix)

    # If stacked cubes exist then load them
    if os.path.isfile(cntfile) and os.path.isfile(modfile):
        cntcube_stacked = gammalib.GCTAEventCube(cntfile)
        modcube_stacked = gammalib.GCTAEventCube(modfile)

    # ... otherwise generate them
    else:

        # Define counts and model cubes for stacking
        map = gammalib.GSkyMap('TAN', 'CEL', 0.0, 0.0, -binsz, binsz, npix,
                               npix, ebins)
        ebds = gammalib.GEbounds(ebins, gammalib.GEnergy(emin, 'TeV'),
                                 gammalib.GEnergy(emax, 'TeV'))
        gti = gammalib.GGti(gammalib.GTime(0.0, 's'), gammalib.GTime(1.0, 's'))
        cntcube_stacked = gammalib.GCTAEventCube(map, ebds, gti)
        modcube_stacked = gammalib.GCTAEventCube(map, ebds, gti)

        # Load observations
        inobs = gammalib.GObservations(obsname)

        # Loop over runs in observations
        for i, run in enumerate(inobs):

            # If an observation list is defined then skip observation if it
            # is not in list
            if obslist != None:
                if i not in obslist:
                    continue

            # Build observation container with single run
            obs = gammalib.GObservations()
            obs.append(run)

            # Select events
            obs = select_events(obs, emin=emin, emax=emax)

            # Generate background model
            models = get_bkg_model(obs, suffix=suffix)

            # Attach models to observation container
            obs.models(models)

            # Create counts cube
            cntcube = create_cntcube(obs,
                                     emin=emin,
                                     emax=emax,
                                     ebins=ebins,
                                     npix=npix,
                                     binsz=binsz)

            # Create model cube
            modcube = create_modcube(obs, cntcube)

            # Stack cubes
            cntcube_stacked = stack_cube(cntcube_stacked, cntcube)
            modcube_stacked = stack_cube(modcube_stacked, modcube)

            # Stop after first run
            #break

        # Save cubes
        cntcube_stacked.save(cntfile, True)
        modcube_stacked.save(modfile, True)

    # Plot stacked cubes
    plot(cntcube_stacked, modcube_stacked, suffix=suffix)
    plot_sectors(cntcube_stacked, modcube_stacked, suffix=suffix)
    plot_radial_profiles(cntcube_stacked, modcube_stacked, suffix=suffix)

    # Return
    return
Пример #19
0
def createobs(ra=86.171648, dec=-1.4774586, rad=5.0,
              emin=0.1, emax=100.0, duration=360000.0, deadc=0.95,
              irf="South_50h", caldb="prod2"):
    """
    Create CTA observation.
    """
    # Allocate CTA observation
    obs = gammalib.GCTAObservation()

    # Set calibration database
    db = gammalib.GCaldb()
    if (gammalib.dir_exists(caldb)):
        db.rootdir(caldb)
    else:
        db.open("cta", caldb)

    # Set pointing direction
    pntdir = gammalib.GSkyDir()
    pntdir.radec_deg(ra, dec)
    pnt = gammalib.GCTAPointing()
    pnt.dir(pntdir)
    obs.pointing(pnt)

    # Set ROI
    roi     = gammalib.GCTARoi()
    instdir = gammalib.GCTAInstDir()
    instdir.dir(pntdir)
    roi.centre(instdir)
    roi.radius(rad)

    # Set GTI
    gti   = gammalib.GGti()
    start = gammalib.GTime(0.0)
    stop  = gammalib.GTime(duration)
    gti.append(start, stop)

    # Set energy boundaries
    ebounds = gammalib.GEbounds()
    e_min   = gammalib.GEnergy()
    e_max   = gammalib.GEnergy()
    e_min.TeV(emin)
    e_max.TeV(emax)
    ebounds.append(e_min, e_max)

    # Allocate event list
    events = gammalib.GCTAEventList()
    events.roi(roi)
    events.gti(gti)
    events.ebounds(ebounds)
    obs.events(events)

    # Set instrument response
    obs.response(irf, db)

    # Set ontime, livetime, and deadtime correction factor
    obs.ontime(duration)
    obs.livetime(duration*deadc)
    obs.deadc(deadc)

    # Return observation
    return obs