Esempio n. 1
0
    def _test_python(self):
        """
        Test ctcubemask from Python
        """
        # Set-up ctcubemask
        mask = ctools.ctcubemask()
        mask['inobs'] = self._cntcube
        mask['regfile'] = self._exclusion
        mask['outcube'] = 'ctcubemask_py1.fits'
        mask['ra'] = 83.63
        mask['dec'] = 22.01
        mask['rad'] = 2.0
        mask['emin'] = 0.1
        mask['emax'] = 100.0
        mask['logfile'] = 'ctcubemask_py1.log'
        mask['chatter'] = 2

        # Run ctcubemask tool
        mask.logFileOpen()  # Make sure we get a log file
        mask.run()
        mask.save()

        # Check result file
        self._check_result_file('ctcubemask_py1.fits')

        # Return
        return
Esempio n. 2
0
    def test_functional(self):
        """
        Test ctcubemask functionnality.
        """
        # Set-up ctcubemask
        mask = ctools.ctcubemask()
        mask["inobs"].filename(self.cntmap_name)
        mask["regfile"].filename(self.regfile)
        mask["outcube"].filename("filtered_cntmap.fits")
        mask["ra"].real(83.63)
        mask["dec"].real(22.01)
        mask["rad"].real(2.0)
        mask["emin"].real(0.1)
        mask["emax"].real(100.0)
        
        # Run tool
        self.test_try("Run ctcubemask")
        try:
            mask.run()
            self.test_try_success()
        except:
            self.test_try_failure("Exception occured in ctcubemask.")

        # Save counts cube
        self.test_try("Save counts cube")
        try:
            mask.save()
            self.test_try_success()
        except:
            self.test_try_failure("Exception occured in saving counts cube.")

        # Return
        return
Esempio n. 3
0
    def _masked_cube(self,
                     cube,
                     ra,
                     dec,
                     rad,
                     emin='INDEF',
                     emax='INDEF',
                     regfile='NONE'):
        """
        Mask an event cube and returns the masked cube

        Parameters
        ----------
        cube : `~gammalib.GCTAEventCube`
            Event cube
        ra : float (str 'INDEF' for no selection on direction)
            Right Ascension (deg)
        dec : float (str 'INDEF' for no selection on direction)
            Declination (deg)
        rad : float (str 'INDEF' for no selection on direction)
            Radius (deg)
        emin : float (str 'INDEF' for no selection on energy)
            Minimum energy (TeV)
        emax : float (str 'INDEF' for no selection on energy)
            Maximum energy (TeV)

        Returns
        -------
        cube : `~gammalib.GCTAEventCube`
            Event cube
        """
        # Turn cube into observation container to feed to ctcubemask
        obs = gammalib.GCTAObservation()
        obs.events(cube)
        obs_cont = gammalib.GObservations()
        obs_cont.append(obs)

        # Use ctcubemask to mask event cube pixels
        cubemask = ctools.ctcubemask(obs_cont)
        cubemask['ra'] = ra
        cubemask['dec'] = dec
        cubemask['rad'] = rad
        cubemask['emin'] = emin
        cubemask['emax'] = emax
        cubemask['regfile'] = regfile
        cubemask.run()

        # Extract copy of cube from observation container (copy is needed to
        # avoid memory leaks in SWIG)
        cube = cubemask.obs()[0].events().copy()

        # Return cube
        return cube
Esempio n. 4
0
def mask_cube():
    """
    Compute a mask cube.
    http://cta.irap.omp.eu/ctools/users/reference_manual/ctcubemask.html

    Parameters
    ----------

    Outputs
    --------
    """

    maskcube = ctools.ctcubemask()
    maskcube.execute()

    return maskcube
Esempio n. 5
0
    def _select_obs(self, emin, emax):
        """
        Select observations for energy interval

        Parameters
        ----------
        emin : `~gammalib.GEnergy()`
            Minimum energy
        emax : `~gammalib.GEnergy()`
            Maximum energy

        Returns
        -------
        obs : `~gammalib.GObservations`
            Observation container
        """
        # Use ctcubemask for binned analysis
        if self._binned_mode:

            # Write header
            self._log_header3(gammalib.EXPLICIT, 'Filtering cube')

            # Select layers
            cubemask = ctools.ctcubemask(self.obs())
            cubemask['regfile'] = 'NONE'
            cubemask['ra'] = 'UNDEFINED'
            cubemask['dec'] = 'UNDEFINED'
            cubemask['rad'] = 'UNDEFINED'
            cubemask['emin'] = emin.TeV()
            cubemask['emax'] = emax.TeV()

            # If chatter level is verbose and debugging is requested then
            # switch also on the debug model in ctcubemask
            if self._logVerbose() and self._logDebug():
                cubemask['debug'] = True

            # Select layers
            cubemask.run()

            # Set new binned observation
            obs = cubemask.obs().copy()

        # Use ...
        elif self._onoff_mode:

            # Write header
            self._log_header3(gammalib.EXPLICIT, 'Filtering PHA, ARF and RMF')

            # Initialise observation container
            obs = gammalib.GObservations()

            # Loop over all input observations and select energy bins for
            # all On/Off observations
            for run in self.obs():
                if run.classname() == 'GCTAOnOffObservation':
                    obs.append(self._select_onoff_obs(run, emin, emax))

            # Append models
            obs.models(self.obs().models())

        # Use ctselect for unbinned analysis
        else:

            # Write header
            self._log_header3(gammalib.EXPLICIT, 'Selecting events')

            # Select events
            select = ctools.ctselect(self.obs())
            select['ra'] = 'UNDEFINED'
            select['dec'] = 'UNDEFINED'
            select['rad'] = 'UNDEFINED'
            select['emin'] = emin.TeV()
            select['emax'] = emax.TeV()
            select['tmin'] = 'UNDEFINED'
            select['tmax'] = 'UNDEFINED'

            # If chatter level is verbose and debugging is requested then
            # switch also on the debug model in ctselect
            if self._logVerbose() and self._logDebug():
                select['debug'] = True

            # Run ctselect
            select.run()

            # Retrieve observation
            obs = select.obs().copy()

        # Return observation container
        return obs
Esempio n. 6
0
    def _test_python(self):
        """
        Test ctcubemask from Python
        """
        # Allocate ctcubemask
        mask = ctools.ctcubemask()

        # Check that empty ctcubemask tool holds an empty observation
        self._check_obs(mask.obs(), nobs=0)

        # Check that saving saves an empty counts cube
        mask['outcube'] = 'ctcubemask_py0.fits'
        mask['logfile'] = 'ctcubemask_py0.log'
        mask.logFileOpen()
        mask.save()
        self.test_assert(not os.path.isfile('ctcubemask_py0.fits'),
             'Check that no counts cube has been created')

        # Check that clearing does not lead to an exception or segfault
        mask.clear()

        # Now set ctcubemask parameters
        mask['inobs']   = self._cntcube
        mask['regfile'] = self._exclusion
        mask['ra']      = 83.63
        mask['dec']     = 22.01
        mask['rad']     = 1.0
        mask['emin']    = 1.0
        mask['emax']    = 100.0
        mask['outcube'] = 'ctcubemask_py1.fits'
        mask['logfile'] = 'ctcubemask_py1.log'
        mask['chatter'] = 2

        # Run ctcubemask tool
        mask.logFileOpen()   # Make sure we get a log file
        mask.run()
        mask.save()

        # Check result file
        self._check_result_file('ctcubemask_py1.fits')

        # Set-up ctcubemask without exclusion regions but tighter energy
        # selection
        mask = ctools.ctcubemask()
        mask['inobs']   = self._cntcube
        mask['regfile'] = 'NONE'
        mask['ra']      = 83.63
        mask['dec']     = 22.01
        mask['rad']     = 3.0
        mask['emin']    = 2.0
        mask['emax']    = 10.0
        mask['outcube'] = 'ctcubemask_py2.fits'
        mask['logfile'] = 'ctcubemask_py2.log'
        mask['chatter'] = 3
        mask['publish'] = True

        # Execute ctcubemask tool
        mask.logFileOpen()   # Make sure we get a log file
        mask.execute()

        # Check result file
        self._check_result_file('ctcubemask_py2.fits', events=65)

        # Copy ctcubemask tool
        cpy_mask = mask.copy()

        # Check that ctcubemask tool holds one observation
        self._check_obs(cpy_mask.obs())

        # Execute copy of ctcubemask tool again, now with a higher chatter
        # level than before
        cpy_mask['outcube'] = 'ctcubemask_py3.fits'
        cpy_mask['logfile'] = 'ctcubemask_py3.log'
        cpy_mask['chatter'] = 4
        cpy_mask.execute()

        # Check result file
        self._check_result_file('ctcubemask_py3.fits', events=65)

        # Clear ctcubemask tool
        cpy_mask.clear()

        # Check that empty ctcubemask tool holds an empty observation
        self._check_obs(cpy_mask.obs(), nobs=0)

        # Prepare observation container
        obs = self._obs_mixed()
        obs.models(gammalib.GModels(self._model))

        # Set-up ctcubemask from observation container, don't perform any
        # energy selection
        mask = ctools.ctcubemask(obs)
        mask['regfile'] = 'NONE'
        mask['ra']      = 83.63
        mask['dec']     = 22.01
        mask['rad']     = 1.0
        mask['emin']    = 'NONE'
        mask['emax']    = 'NONE'
        mask['outcube'] = 'ctcubemask_py4.xml'
        mask['logfile'] = 'ctcubemask_py4.log'
        mask['chatter'] = 3

        # Execute ctcubemask tool
        mask.logFileOpen()   # Make sure we get a log file
        mask.execute()

        # Check result file
        self._check_result_file('filtered_crab_cntmap.fits', events=132)

        # Return
        return
Esempio n. 7
0
    def _select_obs(self, emin, emax):
        """
        Select observations for energy interval

        Parameters
        ----------
        emin : `~gammalib.GEnergy()`
            Minimum energy
        emax : `~gammalib.GEnergy()`
            Maximum energy

        Returns
        -------
        obs : `~gammalib.GObservations`
            Observation container
        """
        # Use ctcubemask for binned analysis
        if self._binned_mode:

            # Write header
            self._log_header3(gammalib.EXPLICIT, 'Filtering cube')

            # Select layers
            cubemask            = ctools.ctcubemask(self.obs())
            cubemask['regfile'] = 'NONE'
            cubemask['ra']      = 'UNDEFINED'
            cubemask['dec']     = 'UNDEFINED'
            cubemask['rad']     = 'UNDEFINED'
            cubemask['emin']    = emin.TeV()
            cubemask['emax']    = emax.TeV()
            cubemask.run() 

            # Set new binned observation
            obs = cubemask.obs().copy()

        # Use ...
        elif self._onoff_mode:

            # Write header
            self._log_header3(gammalib.EXPLICIT, 'Filtering PHA, ARF and RMF')

            # Initialise observation container
            obs = gammalib.GObservations()

            # Loop over all input observations and select energy bins for
            # all On/Off observations
            for run in self.obs():
                if run.classname() == 'GCTAOnOffObservation':
                    obs.append(self._select_onoff_obs(run, emin, emax))

            # Append models
            obs.models(self.obs().models())

        # Use ctselect for unbinned analysis
        else:

            # Write header
            self._log_header3(gammalib.EXPLICIT, 'Selecting events')

            # Select events
            select = ctools.ctselect(self.obs())
            select['ra']   = 'UNDEFINED'
            select['dec']  = 'UNDEFINED'
            select['rad']  = 'UNDEFINED'
            select['emin'] = emin.TeV()
            select['emax'] = emax.TeV()
            select['tmin'] = 'UNDEFINED'
            select['tmax'] = 'UNDEFINED'
            select.run()

            # Retrieve observation
            obs = select.obs().copy()

        # Return observation container
        return obs
Esempio n. 8
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()

        #  Write input parameters into logger
        if self.logTerse():
            self.log_parameters()
            self.log("\n")

        # Write spectral binning into header
        if self.logTerse():
            self.log("\n")
            self.log.header1("Spectral binning")
            if self.m_binned_mode:
                cube_ebounds = self.obs[0].events().ebounds()
                self.log.parformat("Counts cube energy range")
                self.log(str(cube_ebounds.emin()))
                self.log(" - ")
                self.log(str(cube_ebounds.emax()))
                self.log("\n")
            for i in range(self.m_ebounds.size()):
                self.log.parformat("Bin "+str(i+1))
                self.log(str(self.m_ebounds.emin(i)))
                self.log(" - ")
                self.log(str(self.m_ebounds.emax(i)))
                self.log("\n")

        # Write observation into logger
        if self.logTerse():
            self.log("\n")
            self.log.header1("Observation")
            self.log(str(self.obs))
            self.log("\n")

        # Write header
        if self.logTerse():
            self.log("\n")
            self.log.header1("Adjust model parameters")

        # Adjust model parameters dependent on input user parameters
        for model in self.obs.models():

            # Set TS flag for all models to false.
            # Source of interest will be set to true later
            model.tscalc(False)

            # Log model name
            if self.logExplicit():
                self.log.header3(model.name())

            # Deal with the source of interest    
            if model.name() == self.m_srcname:
                for par in model:
                    if par.is_free() and self.logExplicit():
                        self.log(" Fixing \""+par.name()+"\"\n")
                    par.fix()
                normpar = model.spectral()[0]
                if normpar.is_fixed() and self.logExplicit():
                    self.log(" Freeing \""+normpar.name()+"\"\n")
                normpar.free()
                if self.m_calc_ts:
                    model.tscalc(True)

            elif self.m_fix_bkg and not model.classname() == "GModelSky":
                for par in model:
                    if par.is_free() and self.logExplicit():
                        self.log(" Fixing \""+par.name()+"\"\n")
                    par.fix()

            elif self.m_fix_srcs and model.classname() == "GModelSky":
                for par in model:
                    if par.is_free() and self.logExplicit():
                        self.log(" Fixing \""+par.name()+"\"\n")
                    par.fix()

        # Write header
        if self.logTerse():
            self.log("\n")
            self.log.header1("Generate spectrum")  
            self.log(str(self.m_ebounds))    

        # Initialise FITS Table with extension "SPECTRUM"
        table = gammalib.GFitsBinTable(self.m_ebounds.size())
        table.extname("SPECTRUM")

        # Add Header for compatibility with gammalib.GMWLSpectrum
        table.card("INSTRUME", "CTA", "Name of Instrument")
        table.card("TELESCOP", "CTA", "Name of Telescope")

        # Create FITS table columns
        energy       = gammalib.GFitsTableDoubleCol("Energy", self.m_ebounds.size())
        energy_low   = gammalib.GFitsTableDoubleCol("ed_Energy", self.m_ebounds.size())
        energy_high  = gammalib.GFitsTableDoubleCol("eu_Energy", self.m_ebounds.size())
        flux         = gammalib.GFitsTableDoubleCol("Flux", self.m_ebounds.size())
        flux_err     = gammalib.GFitsTableDoubleCol("e_Flux", self.m_ebounds.size())
        TSvalues     = gammalib.GFitsTableDoubleCol("TS", self.m_ebounds.size())
        ulim_values  = gammalib.GFitsTableDoubleCol("UpperLimit", self.m_ebounds.size())
        Npred_values = gammalib.GFitsTableDoubleCol("Npred", self.m_ebounds.size())
        energy.unit("TeV")
        energy_low.unit("TeV")
        energy_high.unit("TeV")
        flux.unit("erg/cm2/s")
        flux_err.unit("erg/cm2/s")
        ulim_values.unit("erg/cm2/s")

        # Loop over energy bins
        for i in range(self.m_ebounds.size()):

            # Log information
            if self.logExplicit():
                self.log("\n")
                self.log.header2("Energy bin "+str(i+1))

            # Get energy boundaries
            emin      = self.m_ebounds.emin(i)
            emax      = self.m_ebounds.emax(i)
            elogmean  = self.m_ebounds.elogmean(i)
            elogmean2 = elogmean.MeV() * elogmean.MeV()    

            # Store energy as TeV
            energy[i] = elogmean.TeV()

            # Store energy errors
            energy_low[i]  = (elogmean - emin).TeV()
            energy_high[i] = (emax - elogmean).TeV()

            # use ctselect for unbinned analysis
            if not self.m_binned_mode:
                
                # Log information
                if self.logExplicit():
                    self.log.header3("Selecting events")
    
                # Select events
                select = ctools.ctselect(self.obs)
                select["emin"] = emin.TeV()    
                select["emax"] = emax.TeV() 
                select["tmin"] = "UNDEFINED"
                select["tmax"] = "UNDEFINED"
                select["rad"]  = "UNDEFINED"
                select["ra"]   = "UNDEFINED"
                select["dec"]  = "UNDEFINED"
                select.run()  
    
                # Retrieve observation
                obs = select.obs()

            # use ctcubemask for binned analysis
            else:

                # Header
                if self.logExplicit():
                    self.log.header3("Filtering cube")

                # Select layers
                cubemask            = ctools.ctcubemask(self.obs)
                cubemask["regfile"] = "NONE"
                cubemask["ra"]      = "UNDEFINED"
                cubemask["dec"]     = "UNDEFINED"
                cubemask["rad"]     = "UNDEFINED"
                cubemask["emin"]    = emin.TeV() 
                cubemask["emax"]    = emax.TeV()
                cubemask.run() 
                
                # Set new binned observation
                obs = cubemask.obs()

            # Header
            if self.logExplicit():
                self.log.header3("Performing fit")

            # Likelihood
            like          = ctools.ctlike(obs)
            like["edisp"] = self.m_edisp
            like.run()

            # Skip bin if no event was present
            if like.obs().logL() == 0.0:

                # Log information
                if self.logExplicit():
                    self.log("No event in this bin. ")
                    self.log("Likelihood is zero. ")
                    self.log("Bin is skipped.")

                # Set all values to 0
                flux[i]         = 0.0
                flux_err[i]     = 0.0
                TSvalues[i]     = 0.0
                ulim_values[i]  = 0.0
                Npred_values[i] = 0.0
                continue

            # Get results
            fitted_models = like.obs().models()
            source        = fitted_models[self.m_srcname]

            # Calculate Upper Limit            
            ulimit_value = -1.0
            if self.m_calc_ulimit:

                # Logging information
                if self.logExplicit():
                    self.log.header3("Computing upper limit")

                # Create upper limit object  
                ulimit = ctools.ctulimit(like.obs())
                ulimit["srcname"] = self.m_srcname
                ulimit["eref"]    = elogmean.TeV()

                # Try to run upper limit and catch exceptions
                try:
                    ulimit.run()
                    ulimit_value = ulimit.diff_ulimit()
                except:
                    if self.logExplicit():
                        self.log("Upper limit calculation failed.")
                    ulimit_value = -1.0

            # Get TS value
            TS = -1.0
            if self.m_calc_ts:
                TS = source.ts() 

            # Compute Npred value (only works for unbinned analysis)
            Npred = 0.0
            if not self.m_binned_mode:
                for observation in like.obs():
                    Npred += observation.npred(source)  

            # Get differential flux    
            fitted_flux = source.spectral().eval(elogmean,gammalib.GTime())

            # Compute flux error
            parvalue  = source.spectral()[0].value()
            rel_error = source.spectral()[0].error() / parvalue        
            e_flux    = fitted_flux * rel_error

            # Set values for storage
            TSvalues[i] = TS

            # Set npred values 
            Npred_values[i] = Npred

            # Convert fluxes to nuFnu
            flux[i]     = fitted_flux * elogmean2 * gammalib.MeV2erg
            flux_err[i] = e_flux      * elogmean2 * gammalib.MeV2erg
            if ulimit_value > 0.0:
                ulim_values[i] = ulimit_value * elogmean2 * gammalib.MeV2erg

            # Log information
            if self.logTerse(): 
                self.log("\n")
                self.log.parformat("Bin "+str(i+1))
                self.log(str(flux[i]))
                self.log(" +/- ")
                self.log(str(flux_err[i]))
                if self.m_calc_ulimit and ulim_values[i] > 0.0:
                    self.log(" [< "+str(ulim_values[i])+"]")
                self.log(" erg/cm2/s")
                if self.m_calc_ts and TSvalues[i] > 0.0:
                    self.log(" (TS = "+str(TS)+")")

        # Append filled columns to fits table    
        table.append(energy)
        table.append(energy_low)
        table.append(energy_high)
        table.append(flux)
        table.append(flux_err)
        table.append(TSvalues)
        table.append(ulim_values)
        table.append(Npred_values)

        # Create the FITS file now
        self.fits = gammalib.GFits()
        self.fits.append(table)

        # Return
        return
Esempio n. 9
0
    def _test_python(self):
        """
        Test ctcubemask from Python
        """
        # Allocate ctcubemask
        mask = ctools.ctcubemask()

        # Check that empty ctcubemask tool holds an empty observation
        self._check_obs(mask.obs(), nobs=0)

        # Check that saving saves an empty counts cube
        mask['outcube'] = 'ctcubemask_py0.fits'
        mask['logfile'] = 'ctcubemask_py0.log'
        mask.logFileOpen()
        mask.save()
        self.test_assert(not os.path.isfile('ctcubemask_py0.fits'),
             'Check that no counts cube has been created')

        # Check that clearing does not lead to an exception or segfault
        mask.clear()

        # Now set ctcubemask parameters
        mask['inobs']   = self._cntcube
        mask['regfile'] = self._exclusion
        mask['ra']      = 83.63
        mask['dec']     = 22.01
        mask['rad']     = 2.0
        mask['emin']    = 0.1
        mask['emax']    = 100.0
        mask['outcube'] = 'ctcubemask_py1.fits'
        mask['logfile'] = 'ctcubemask_py1.log'
        mask['chatter'] = 2

        # Run ctcubemask tool
        mask.logFileOpen()   # Make sure we get a log file
        mask.run()
        mask.save()

        # Check result file
        self._check_result_file('ctcubemask_py1.fits')

        # Set-up ctcubemask without exclusion regions but tighter energy
        # selection
        mask = ctools.ctcubemask()
        mask['inobs']   = self._cntcube
        mask['regfile'] = 'NONE'
        mask['ra']      = 83.63
        mask['dec']     = 22.01
        mask['rad']     = 3.0
        mask['emin']    = 1.0
        mask['emax']    = 100.0
        mask['outcube'] = 'ctcubemask_py2.fits'
        mask['logfile'] = 'ctcubemask_py2.log'
        mask['chatter'] = 3
        mask['publish'] = True

        # Execute ctcubemask tool
        mask.logFileOpen()   # Make sure we get a log file
        mask.execute()

        # Check result file
        self._check_result_file('ctcubemask_py2.fits', events=573)

        # Copy ctcubemask tool
        cpy_mask = mask.copy()

        # Check that ctcubemask tool holds one observation
        self._check_obs(cpy_mask.obs())

        # Execute copy of ctcubemask tool again, now with a higher chatter
        # level than before
        cpy_mask['outcube'] = 'ctcubemask_py3.fits'
        cpy_mask['logfile'] = 'ctcubemask_py3.log'
        cpy_mask['chatter'] = 4
        cpy_mask.execute()

        # Check result file
        self._check_result_file('ctcubemask_py3.fits', events=573)

        # Clear ctcubemask tool
        cpy_mask.clear()

        # Check that empty ctcubemask tool holds an empty observation
        self._check_obs(cpy_mask.obs(), nobs=0)

        # Prepare observation container
        obs = self._obs_mixed()
        obs.models(gammalib.GModels(self._model))

        # Set-up ctcubemask from observation container, don't perform any
        # energy selection
        mask = ctools.ctcubemask(obs)
        mask['regfile'] = 'NONE'
        mask['ra']      = 83.63
        mask['dec']     = 22.01
        mask['rad']     = 3.0
        mask['emin']    = 'NONE'
        mask['emax']    = 'NONE'
        mask['outcube'] = 'ctcubemask_py4.xml'
        mask['logfile'] = 'ctcubemask_py4.log'
        mask['chatter'] = 3

        # Execute ctcubemask tool
        mask.logFileOpen()   # Make sure we get a log file
        mask.execute()

        # Check result file
        self._check_result_file('filtered_crab_cntmap.fits', events=5542)

        # Return
        return
Esempio n. 10
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()

        # Write spectral binning into header
        if self._logTerse():
            self._log("\n")
            self._log.header1("Spectral binning")
            if self._binned_mode:
                cube_ebounds = self._obs[0].events().ebounds()
                self._log.parformat("Counts cube energy range")
                self._log(str(cube_ebounds.emin()))
                self._log(" - ")
                self._log(str(cube_ebounds.emax()))
                self._log("\n")
            for i in range(self._ebounds.size()):
                self._log.parformat("Bin " + str(i + 1))
                self._log(str(self._ebounds.emin(i)))
                self._log(" - ")
                self._log(str(self._ebounds.emax(i)))
                self._log("\n")

        # Write observation into logger
        if self._logTerse():
            self._log("\n")
            self._log.header1("Observation")
            self._log(str(self._obs))
            self._log("\n")

        # Write header
        if self._logTerse():
            self._log("\n")
            self._log.header1("Adjust model parameters")

        # Adjust model parameters dependent on input user parameters
        for model in self._obs.models():

            # Set TS flag for all models to false.
            # Source of interest will be set to true later
            model.tscalc(False)

            # Log model name
            if self._logExplicit():
                self._log.header3(model.name())

            # Deal with the source of interest
            if model.name() == self._srcname:
                for par in model:
                    if par.is_free() and self._logExplicit():
                        self._log(" Fixing \"" + par.name() + "\"\n")
                    par.fix()
                normpar = model.spectral()[0]
                if normpar.is_fixed() and self._logExplicit():
                    self._log(" Freeing \"" + normpar.name() + "\"\n")
                normpar.free()
                if self._calc_ts:
                    model.tscalc(True)

            elif self._fix_bkg and not model.classname() == "GModelSky":
                for par in model:
                    if par.is_free() and self._logExplicit():
                        self._log(" Fixing \"" + par.name() + "\"\n")
                    par.fix()

            elif self._fix_srcs and model.classname() == "GModelSky":
                for par in model:
                    if par.is_free() and self._logExplicit():
                        self._log(" Fixing \"" + par.name() + "\"\n")
                    par.fix()

        # Write header
        if self._logTerse():
            self._log("\n")
            self._log.header1("Generate spectrum")
            self._log(str(self._ebounds))

        # Initialise FITS Table with extension "SPECTRUM"
        table = gammalib.GFitsBinTable(self._ebounds.size())
        table.extname("SPECTRUM")

        # Add Header for compatibility with gammalib.GMWLSpectrum
        table.card("INSTRUME", "CTA", "Name of Instrument")
        table.card("TELESCOP", "CTA", "Name of Telescope")

        # Create FITS table columns
        nrows = self._ebounds.size()
        energy = gammalib.GFitsTableDoubleCol("Energy", nrows)
        energy_low = gammalib.GFitsTableDoubleCol("ed_Energy", nrows)
        energy_high = gammalib.GFitsTableDoubleCol("eu_Energy", nrows)
        flux = gammalib.GFitsTableDoubleCol("Flux", nrows)
        flux_err = gammalib.GFitsTableDoubleCol("e_Flux", nrows)
        TSvalues = gammalib.GFitsTableDoubleCol("TS", nrows)
        ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", nrows)
        Npred_values = gammalib.GFitsTableDoubleCol("Npred", nrows)
        energy.unit("TeV")
        energy_low.unit("TeV")
        energy_high.unit("TeV")
        flux.unit("erg/cm2/s")
        flux_err.unit("erg/cm2/s")
        ulim_values.unit("erg/cm2/s")

        # Loop over energy bins
        for i in range(nrows):

            # Log information
            if self._logExplicit():
                self._log("\n")
                self._log.header2("Energy bin " + str(i + 1))

            # Get energy boundaries
            emin = self._ebounds.emin(i)
            emax = self._ebounds.emax(i)
            elogmean = self._ebounds.elogmean(i)
            elogmean2 = elogmean.MeV() * elogmean.MeV()

            # Store energy as TeV
            energy[i] = elogmean.TeV()

            # Store energy errors
            energy_low[i] = (elogmean - emin).TeV()
            energy_high[i] = (emax - elogmean).TeV()

            # Use ctselect for unbinned analysis
            if not self._binned_mode:

                # Log information
                if self._logExplicit():
                    self._log.header3("Selecting events")

                # Select events
                select = ctools.ctselect(self._obs)
                select["emin"] = emin.TeV()
                select["emax"] = emax.TeV()
                select["tmin"] = "UNDEFINED"
                select["tmax"] = "UNDEFINED"
                select["rad"] = "UNDEFINED"
                select["ra"] = "UNDEFINED"
                select["dec"] = "UNDEFINED"
                select.run()

                # Retrieve observation
                obs = select.obs()

            # Use ctcubemask for binned analysis
            else:

                # Header
                if self._logExplicit():
                    self._log.header3("Filtering cube")

                # Select layers
                cubemask = ctools.ctcubemask(self._obs)
                cubemask["regfile"] = "NONE"
                cubemask["ra"] = "UNDEFINED"
                cubemask["dec"] = "UNDEFINED"
                cubemask["rad"] = "UNDEFINED"
                cubemask["emin"] = emin.TeV()
                cubemask["emax"] = emax.TeV()
                cubemask.run()

                # Set new binned observation
                obs = cubemask.obs()

            # Header
            if self._logExplicit():
                self._log.header3("Performing fit")

            # Likelihood
            like = ctools.ctlike(obs)
            like["edisp"] = self._edisp
            like.run()

            # Skip bin if no event was present
            if like.obs().logL() == 0.0:

                # Log information
                if self._logExplicit():
                    self._log("No event in this bin. ")
                    self._log("Likelihood is zero. ")
                    self._log("Bin is skipped.")

                # Set all values to 0
                flux[i] = 0.0
                flux_err[i] = 0.0
                TSvalues[i] = 0.0
                ulim_values[i] = 0.0
                Npred_values[i] = 0.0
                continue

            # Get results
            fitted_models = like.obs().models()
            source = fitted_models[self._srcname]

            # Calculate Upper Limit
            ulimit_value = -1.0
            if self._calc_ulimit:

                # Logging information
                if self._logExplicit():
                    self._log.header3("Computing upper limit")

                # Create upper limit object
                ulimit = ctools.ctulimit(like.obs())
                ulimit["srcname"] = self._srcname
                ulimit["eref"] = elogmean.TeV()

                # Try to run upper limit and catch exceptions
                try:
                    ulimit.run()
                    ulimit_value = ulimit.diff_ulimit()
                except:
                    if self._logExplicit():
                        self._log("Upper limit calculation failed.")
                    ulimit_value = -1.0

            # Get TS value
            TS = -1.0
            if self._calc_ts:
                TS = source.ts()

            # Compute Npred value (only works for unbinned analysis)
            Npred = 0.0
            if not self._binned_mode:
                for observation in like.obs():
                    Npred += observation.npred(source)

            # Get differential flux
            fitted_flux = source.spectral().eval(elogmean)

            # Compute flux error
            parvalue = source.spectral()[0].value()
            rel_error = source.spectral()[0].error() / parvalue
            e_flux = fitted_flux * rel_error

            # Set values for storage
            TSvalues[i] = TS

            # Set npred values
            Npred_values[i] = Npred

            # Convert fluxes to nuFnu
            flux[i] = fitted_flux * elogmean2 * gammalib.MeV2erg
            flux_err[i] = e_flux * elogmean2 * gammalib.MeV2erg
            if ulimit_value > 0.0:
                ulim_values[i] = ulimit_value * elogmean2 * gammalib.MeV2erg

            # Log information
            if self._logTerse():
                self._log("\n")
                self._log.parformat("Bin " + str(i + 1))
                self._log(str(flux[i]))
                self._log(" +/- ")
                self._log(str(flux_err[i]))
                if self._calc_ulimit and ulim_values[i] > 0.0:
                    self._log(" [< " + str(ulim_values[i]) + "]")
                self._log(" erg/cm2/s")
                if self._calc_ts and TSvalues[i] > 0.0:
                    self._log(" (TS = " + str(TS) + ")")

        # Append filled columns to fits table
        table.append(energy)
        table.append(energy_low)
        table.append(energy_high)
        table.append(flux)
        table.append(flux_err)
        table.append(TSvalues)
        table.append(ulim_values)
        table.append(Npred_values)

        # Create the FITS file now
        self._fits = gammalib.GFits()
        self._fits.append(table)

        # Return
        return