Esempio n. 1
0
    def run(self):
        """Performs the spectrum extraction

        Returns
        -------
        spectrum_observation : `~gammapy.spectrum.SpectrumObservation`
            Spectrum observation
        """
        self.make_empty_vectors(self.bkg_estimate)
        self.extract_counts(self.bkg_estimate)

        aeff = extract_aeff(self.exposure, self.target_position, self.energy)

        if self.containment_correction:
            containment_factor = extract_psf_containment(
                self.psf, self.on_radius, self.energy
            )
            aeff.data.data *= containment_factor

        edisp = extract_edisp(self.energy)

        self._aeff = aeff
        self._edisp = edisp

        return SpectrumObservation(
            on_vector=self._on_vector,
            aeff=self._aeff,
            off_vector=self._off_vector,
            edisp=self._edisp,
        )
Esempio n. 2
0
def check_edisp_normalisation():
    import matplotlib.pyplot as plt

    obs = SpectrumObservation.read(obs_path)
    p = obs.edisp.data.data.sum(axis=1)
    e = obs.edisp.data.axis("e_true").nodes
    plt.plot(e, p)
    plt.semilogx()
    plt.show()
Esempio n. 3
0
    def setup(self):
        self.model = PowerLaw(
            index=Quantity(2, ""), amplitude=Quantity(1e-11, "m-2 s-1 TeV-1"), reference=Quantity(1, "TeV")
        )

        # TODO: simulate known spectrum instead of using this example:
        filename = "$GAMMAPY_EXTRA/datasets/hess-crab4_pha/pha_obs23523.fits"
        self.obs = SpectrumObservation.read(filename)
        self.seg = SpectrumEnergyGroupMaker(obs=self.obs)
        ebounds = [0.3, 1.001, 3, 10.001, 30] * u.TeV
        self.seg.compute_range_safe()
        self.seg.compute_groups_fixed(ebounds=ebounds)

        self.groups = self.seg.groups
Esempio n. 4
0
    def setup(self):
        self.model = PowerLaw(
            index=Quantity(2, ''),
            amplitude=Quantity(1e-11, 'm-2 s-1 TeV-1'),
            reference=Quantity(1, 'TeV'),
        )

        # TODO: simulate known spectrum instead of using this example:
        filename = '$GAMMAPY_EXTRA/datasets/hess-crab4_pha/pha_obs23523.fits'
        self.obs = SpectrumObservation.read(filename)
        self.seg = SpectrumEnergyGroupMaker(obs=self.obs)
        ebounds = [0.3, 1.001, 3, 10.001, 30] * u.TeV
        self.seg.compute_range_safe()
        self.seg.compute_groups_fixed(ebounds=ebounds)

        self.groups = self.seg.groups
Esempio n. 5
0
def fit_gammapy():
    """
    Current results

    Parameters:

           name     value     error         unit      min max frozen
        --------- --------- --------- --------------- --- --- ------
            index 2.602e+00 1.555e-01                 nan nan  False
        amplitude 2.441e-11 3.452e-12 1 / (cm2 s TeV) nan nan  False
        reference 1.000e+00 0.000e+00             TeV nan nan   True

    Covariance:

        name/name  index   amplitude
        --------- -------- ---------
            index   0.0242  3.79e-13
        amplitude 3.79e-13  1.19e-23

    Statistic: -157.719 (cash)
    Fit Range: [1.0000000e+09 2.7825594e+10] keV


    """
    obs = SpectrumObservation.read(obs_path)
    # obs.peek()
    # plt.show()
    model = PowerLaw(
        amplitude=1.23 * 1e-11 * u.Unit("cm-2 s-1 TeV-1"),
        reference=1 * u.Unit("TeV"),
        index=2.14 * u.Unit(""),
    )

    fit = SpectrumFit(obs_list=obs, model=model, fit_range=energy_range, stat="cash")
    fit.run()
    print(fit.result[0])
    pprint(fit.__dict__)

    obs = fit.obs_list[0]
    print(obs)
    print("This is fit_gammapy")
    obs.peek()
    import matplotlib.pyplot as plt

    plt.savefig("fit_gammapy.png")
Esempio n. 6
0
 def setup(self):
     pha = gammapy_extra.filename("datasets/hess-crab4_pha/pha_obs23592.fits")
     self.obs = SpectrumObservation.read(pha)
     self.best_fit_model = models.PowerLaw(index=2 * u.Unit(''),
                                           amplitude=1e-11 * u.Unit('cm-2 s-1 TeV-1'),
                                           reference=1 * u.TeV)
     self.npred = self.obs.predicted_counts(self.best_fit_model).data.value
     self.covar_axis = ['index', 'amplitude']
     self.covar = np.diag([0.1 ** 2, 1e-12 ** 2])
     self.fit_range = [0.1, 50] * u.TeV
     self.fit_result = SpectrumFitResult(
         model=self.best_fit_model,
         covariance=self.covar,
         covar_axis=self.covar_axis,
         fit_range=self.fit_range,
         statname='wstat',
         statval=42,
         npred=self.npred,
         obs=self.obs,
     )
Esempio n. 7
0
    def run(self):
        self.make_empty_vectors(self.bkg_estimate)
        self.extract_counts(self.bkg_estimate)

        aeff = extract_aeff(self.exposure, self.target_position, self.energy)

        containment_factor = extract_psf_containment(
            self.psf, self.on_radius, self.energy
        )
        aeff.data.data *= containment_factor

        # Not the real Fermi-LAT EDISP
        # Use 5% energy resolution as approximation
        edisp = EnergyDispersion.from_gauss(
            e_true=self.energy, e_reco=self.energy, sigma=0.05, bias=0
        )

        return SpectrumObservation(
            on_vector=self._on_vector,
            aeff=aeff,
            off_vector=self._off_vector,
            edisp=edisp,
        )
Esempio n. 8
0
def check_energy_binning_effects():
    """Check how spectral fit results change with energy binnings.

    Actually this is still using the default:

    In [14]: print(analysis.extraction.observations[0].edisp)
    EnergyDispersion
    NDDataArray summary info
    e_true         : size =   108, min =  0.010 TeV, max = 301.416 TeV
    e_reco         : size =    72, min =  0.011 TeV, max = 93.804 TeV
    Data           : size =  7776, min =  0.000, max =  1.000

    But now, the fit results from SpectrumAnalysisIACT, which is just
    driving Fitspectrum, are different, almost the same as Sherpa.


    Current results

    Parameters:

           name     value     error         unit      min max frozen
        --------- --------- --------- --------------- --- --- ------
            index 2.620e+00 1.540e-01                 nan nan  False
        amplitude 3.849e-11 5.407e-12 1 / (cm2 s TeV) nan nan  False
        reference 1.000e+00 0.000e+00             TeV nan nan   True

    Covariance:

        name/name  index   amplitude
        --------- -------- ---------
            index   0.0237  5.85e-13
        amplitude 5.85e-13  2.92e-23

    Statistic: -157.166 (cash)
    Fit Range: [ 1.         27.82559402] TeV


    ???
    """
    data_store = DataStore.from_dir("data/hess")
    obs_list = data_store.obs_list([23523])
    on_region = CircleSkyRegion(conf.crab_position, conf.on_radius["hess"])
    fp_binning = [1, 10, 30] * u.TeV
    exclusion_mask = get_exclusion_mask(conf.crab_position)
    model = PowerLaw(
        amplitude=1.23 * 1e-11 * u.Unit("cm-2 s-1 TeV-1"),
        reference=1 * u.Unit("TeV"),
        index=2.14 * u.Unit(""),
    )
    cfg = dict(
        outdir=None,
        background=dict(
            on_region=on_region,
            exclusion_mask=exclusion_mask,
            # min_distance=0.1 * u.rad,
        ),
        extraction=dict(containment_correction=True),
        fit=dict(
            model=model,
            stat="cash",
            # forward_folded=True,
            fit_range=energy_range,
        ),
        fp_binning=fp_binning,
    )
    analysis = SpectrumAnalysisIACT(obs_list, cfg)
    analysis.run()
    analysis.fit.est_errors()
    print("BEFORE", analysis.fit.result[0])
    # print(analysis.extraction.observations[0].edisp)
    # pprint(analysis.fit.__dict__)
    # obs = analysis.fit.obs_list[0]
    # print(obs)
    # # import IPython; IPython.embed()
    # obs.peek()
    # import matplotlib.pyplot as plt
    # plt.savefig('check_energy_binning_effects.png')
    # print('This is check_energy_binning_effects')

    # Try to see if the I/O causes a change in results.
    analysis.extraction.observations.write("temp123")
    analysis.extraction.observations.write("temp123", use_sherpa=True)
    obs = SpectrumObservation.read("temp123/pha_obs23523.fits")
    model = PowerLaw(
        amplitude=1.23 * 1e-11 * u.Unit("cm-2 s-1 TeV-1"),
        reference=1 * u.Unit("TeV"),
        index=2.14 * u.Unit(""),
    )

    fit = SpectrumFit(obs_list=obs, model=model, fit_range=energy_range, stat="cash")
    fit.run()
    print("AFTER", fit.result[0])
"""Compute flux points

This is an example script that show how to compute flux points in Gammapy.
TODO: Refactor and add to FluxPointsComputation class or so
"""

from gammapy.spectrum import (SpectrumObservation, SpectrumFit, FluxPoints,
                              SpectrumResult)
import astropy.units as u
from astropy.table import Table
import numpy as np
import copy
import matplotlib.pyplot as plt

plt.style.use('ggplot')
obs = SpectrumObservation.read(
    '$GAMMAPY_EXTRA/datasets/hess-crab4_pha/pha_obs23523.fits')

fit = SpectrumFit(obs)
fit.run()
best_fit = copy.deepcopy(fit.result[0].fit)

# Define Flux points binning
emin = np.log10(obs.lo_threshold.to('TeV').value)
emax = np.log10(40)
binning = np.logspace(emin, emax, 8) * u.TeV

# Fix index
fit.model.gamma.freeze()

# Fit norm in bands
diff_flux = list()
Esempio n. 10
0
                                   aeff=aeff,
                                   edisp=edisp,
                                   livetime=livetime)

bkg = 0.2 * npred.data
alpha = 0.1
counts_kwargs = dict(energy=npred.energy,
                     exposure=livetime,
                     obs_id=31415,
                     creator='Simulation',
                     hi_threshold=50 * u.TeV,
                     lo_threshold=lo_threshold)

rand = get_random_state(42)

on_counts = rand.poisson(npred.data) + rand.poisson(bkg)
on_vector = PHACountsSpectrum(data=on_counts, backscal=1, **counts_kwargs)

off_counts = rand.poisson(1. / alpha * bkg)
off_vector = PHACountsSpectrum(data=off_counts,
                               backscal=1. / alpha,
                               is_bkg=True,
                               **counts_kwargs)

obs = SpectrumObservation(on_vector=on_vector,
                          off_vector=off_vector,
                          aeff=aeff,
                          edisp=edisp)

obs.write()
Esempio n. 11
0
"""Test spectrum energy grouping.
"""
import warnings
import astropy.units as u
from gammapy.spectrum import SpectrumObservation, SpectrumEnergyGroupMaker

warnings.filterwarnings('ignore')

filename = '$GAMMAPY_EXTRA/datasets/hess-crab4_pha/pha_obs23523.fits'
obs = SpectrumObservation.read(filename)
table = obs.stats_table()

seg = SpectrumEnergyGroupMaker(obs=obs)
ebounds = [0.03, 1, 3, 10, 30] * u.TeV
seg.compute_range_safe()
seg.compute_groups_fixed(ebounds=ebounds)

print('\nTable of original energy bins:')
seg.table[['energy_group_idx', 'bin_idx', 'energy_min', 'energy_max']].pprint(max_lines=-1)

print('\nTable of grouped energy bins:')
seg.groups.to_group_table().pprint(max_lines=-1)

print('\nTable of grouped energy bins:')
seg.groups.to_total_table().pprint(max_lines=-1)

print(seg)
Esempio n. 12
0
"""Test spectrum energy grouping.
"""
import warnings
import astropy.units as u
from gammapy.spectrum import SpectrumObservation, SpectrumEnergyGroupMaker

warnings.filterwarnings('ignore')

filename = '$GAMMAPY_EXTRA/datasets/hess-crab4_pha/pha_obs23523.fits'
obs = SpectrumObservation.read(filename)
table = obs.stats_table()

seg = SpectrumEnergyGroupMaker(obs=obs)
ebounds = [0.03, 1, 3, 10, 30] * u.TeV
seg.compute_range_safe()
seg.compute_groups_fixed(ebounds=ebounds)

print('\nTable of original energy bins:')
seg.table[['energy_group_idx', 'bin_idx', 'energy_min',
           'energy_max']].pprint(max_lines=-1)

print('\nTable of grouped energy bins:')
seg.groups.to_group_table().pprint(max_lines=-1)

print('\nTable of grouped energy bins:')
seg.groups.to_total_table().pprint(max_lines=-1)

print(seg)
from gammapy.spectrum import (
    SpectrumObservation,
    SpectrumFit,
    DifferentialFluxPoints,
    SpectrumFitResult,
    SpectrumResult
)
import astropy.units as u
import numpy as np
import copy
import matplotlib.pyplot as plt


plt.style.use('ggplot')
obs = SpectrumObservation.read('$GAMMAPY_EXTRA/datasets/hess-crab4_pha/pha_obs23523.fits')

fit = SpectrumFit(obs)
fit.run()
best_fit = copy.deepcopy(fit.result[0].fit)

# Define Flux points binning
emin = np.log10(obs.lo_threshold.to('TeV').value)
emax = np.log10(40)
binning = np.logspace(emin, emax, 8) * u.TeV

# Fix index
fit.model.gamma.freeze()

# Fit norm in bands
diff_flux = list()
Esempio n. 14
0
    def simulate_obs(perf,
                     target,
                     obs_param,
                     obs_id=0,
                     random_state='random-seed'):
        """Simulate observation with given parameters.

        Parameters
        ----------
        perf : `~gammapy.scripts.CTAPerf`
            CTA performance
        target : `~gammapy.scripts.Target`
            Source
        obs_param : `~gammapy.scripts.ObservationParameters`
            Observation parameters
        obs_id : `int`, optional
            Observation Id
        random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`}
            Defines random number generator initialisation.
            Passed to `~gammapy.utils.random.get_random_state`.
        """
        livetime = obs_param.livetime
        alpha = obs_param.alpha.value
        emin = obs_param.emin
        emax = obs_param.emax

        model = target.model

        # Compute expected counts
        reco_energy = perf.bkg.energy
        bkg_rate_values = perf.bkg.data.data * livetime.to('s')
        predicted_counts = CountsPredictor(model=model,
                                           aeff=perf.aeff,
                                           livetime=livetime,
                                           edisp=perf.rmf)
        predicted_counts.run()
        npred = predicted_counts.npred
        # set negative values to zero (interpolation issue)
        idx = np.where(npred.data.data < 0.)
        npred.data.data[idx] = 0

        # Randomise counts
        random_state = get_random_state(random_state)
        on_counts = random_state.poisson(npred.data.data.value)  # excess
        bkg_counts = random_state.poisson(
            bkg_rate_values.value)  # bkg in ON region
        off_counts = random_state.poisson(bkg_rate_values.value /
                                          alpha)  # bkg in OFF region

        on_counts += bkg_counts  # evts in ON region

        on_vector = PHACountsSpectrum(
            data=on_counts,
            backscal=1,
            energy_lo=reco_energy.lo,
            energy_hi=reco_energy.hi,
        )

        on_vector.livetime = livetime
        off_vector = PHACountsSpectrum(
            energy_lo=reco_energy.lo,
            energy_hi=reco_energy.hi,
            data=off_counts,
            backscal=1. / alpha,
            is_bkg=True,
        )
        off_vector.livetime = livetime

        obs = SpectrumObservation(on_vector=on_vector,
                                  off_vector=off_vector,
                                  aeff=perf.aeff,
                                  edisp=perf.rmf)
        obs.obs_id = obs_id

        # Set threshold according to the closest energy reco from bkg bins
        idx_min = np.abs(reco_energy.lo - emin).argmin()
        idx_max = np.abs(reco_energy.lo - emax).argmin()
        obs.lo_threshold = reco_energy.lo[idx_min]
        obs.hi_threshold = reco_energy.lo[idx_max]

        return obs
Esempio n. 15
0
# MODEL
model = PowerLaw(index=2.3 * u.Unit(""), amplitude=2.5 * 1e-12 * u.Unit("cm-2 s-1 TeV-1"), reference=1 * u.TeV)

# COUNTS
livetime = 2 * u.h
npred = calculate_predicted_counts(model=model, aeff=aeff, edisp=edisp, livetime=livetime)

bkg = 0.2 * npred.data
alpha = 0.1
counts_kwargs = dict(
    energy=npred.energy,
    exposure=livetime,
    obs_id=31415,
    creator="Simulation",
    hi_threshold=50 * u.TeV,
    lo_threshold=lo_threshold,
)

rand = get_random_state(42)

on_counts = rand.poisson(npred.data) + rand.poisson(bkg)
on_vector = PHACountsSpectrum(data=on_counts, backscal=1, **counts_kwargs)

off_counts = rand.poisson(1.0 / alpha * bkg)
off_vector = PHACountsSpectrum(data=off_counts, backscal=1.0 / alpha, is_bkg=True, **counts_kwargs)


obs = SpectrumObservation(on_vector=on_vector, off_vector=off_vector, aeff=aeff, edisp=edisp)

obs.write()
Esempio n. 16
0
    def simulate_obs(perf, target, obs_param, obs_id=0, random_state='random-seed'):
        """Simulate observation with given parameters.

        Parameters
        ----------
        perf : `~gammapy.scripts.CTAPerf`
            CTA performance
        target : `~gammapy.scripts.Target`
            Source
        obs_param : `~gammapy.scripts.ObservationParameters`
            Observation parameters
        obs_id : `int`, optional
            Observation Id
        random_state : {int, 'random-seed', 'global-rng', `~numpy.random.RandomState`}
            Defines random number generator initialisation.
            Passed to `~gammapy.utils.random.get_random_state`.
        """
        livetime = obs_param.livetime
        alpha = obs_param.alpha.value
        emin = obs_param.emin
        emax = obs_param.emax

        model = target.model

        # Compute expected counts
        reco_energy = perf.bkg.energy
        bkg_rate_values = perf.bkg.data.data * livetime.to('s')
        predicted_counts = CountsPredictor(model=model,
                                           aeff=perf.aeff,
                                           livetime=livetime,
                                           edisp=perf.rmf)
        predicted_counts.run()
        npred = predicted_counts.npred
        # set negative values to zero (interpolation issue)
        idx = np.where(npred.data.data < 0.)
        npred.data.data[idx] = 0

        # Randomise counts
        random_state = get_random_state(random_state)
        on_counts = random_state.poisson(npred.data.data.value)  # excess
        bkg_counts = random_state.poisson(bkg_rate_values.value)  # bkg in ON region
        off_counts = random_state.poisson(bkg_rate_values.value / alpha)  # bkg in OFF region

        on_counts += bkg_counts  # evts in ON region

        on_vector = PHACountsSpectrum(
            data=on_counts,
            backscal=1,
            energy_lo=reco_energy.lo,
            energy_hi=reco_energy.hi,
        )

        on_vector.livetime = livetime
        off_vector = PHACountsSpectrum(energy_lo=reco_energy.lo,
                                       energy_hi=reco_energy.hi,
                                       data=off_counts,
                                       backscal=1. / alpha,
                                       is_bkg=True,
                                       )
        off_vector.livetime = livetime

        obs = SpectrumObservation(on_vector=on_vector,
                                  off_vector=off_vector,
                                  aeff=perf.aeff,
                                  edisp=perf.rmf)
        obs.obs_id = obs_id

        # Set threshold according to the closest energy reco from bkg bins
        idx_min = np.abs(reco_energy.lo - emin).argmin()
        idx_max = np.abs(reco_energy.lo - emax).argmin()
        obs.lo_threshold = reco_energy.lo[idx_min]
        obs.hi_threshold = reco_energy.lo[idx_max]

        return obs
Esempio n. 17
0
                    lw=2,
                    label=name)
    ax.step(x[start:stop],
            bkg_counts[start:stop],
            where='post',
            lw=1,
            color=line.get_color(),
            alpha=0.5)
    return ax


fig, ax = plt.subplots(1, 1)

# filenames = glob.glob('./plots/data/joint_crab/spectra/magic/pha_*.fits')
filenames = glob.glob('./build/pymc_results/spectra/magic/pha_*.fits')
obs = [SpectrumObservation.read(f) for f in filenames]
plot_counts(ax, obs, 'MAGIC')

filenames = glob.glob('./build/pymc_results/spectra/fact/pha_*.fits')
obs = [SpectrumObservation.read(f) for f in filenames]
plot_counts(ax, obs, 'FACT')

filenames = glob.glob('./build/pymc_results/spectra/veritas/pha_*.fits')
obs = [SpectrumObservation.read(f) for f in filenames]
plot_counts(ax, obs, 'VERITAS')

filenames = glob.glob('./build/pymc_results/spectra/hess/pha_*.fits')
obs = [SpectrumObservation.read(f) for f in filenames]
plot_counts(ax, obs, 'H.E.S.S')

ax.legend()
Esempio n. 18
0
from gammapy.spectrum import SpectrumFit, SpectrumObservation, SpectrumFitResult
import matplotlib.pyplot as plt
import astropy.units as u

obs = SpectrumObservation.read('pha_obs31415.fits')
fit = SpectrumFit(obs)

obs.peek()
plt.savefig('observation.png')
plt.cla()

fit.run()
fit.result[0].plot_fit()
plt.savefig('debug_fit.png')

# TODO: implement properly
plt.cla()

fig = plt.figure()
ax = fig.add_subplot(111)
fit.result[0].fit.plot_butterfly(ax=ax, label='Fit result')
input_parameters = dict(index = 2.3 * u.Unit(''),
                        norm = 2.5 * 1e-12 * u.Unit('cm-2 s-1 TeV-1'),
                        reference = 1 * u.TeV)
input_parameter_errors = dict(index = 0 * u.TeV,
                              norm = 0 * u.Unit('cm-2 s-1 TeV-1'),
                              reference = 0 * u.TeV)

input_model = SpectrumFitResult(spectral_model = 'PowerLaw',
                                parameters = input_parameters,
                                parameter_errors = input_parameter_errors)
def main():
    
    #Low energy of spectral fitting range.
    lo_fit_energ = 0.1 * u.Unit('TeV')
    hi_fit_energ = 10 * u.Unit('TeV')
    
    #If you want an internal estimation of a high energy limit for the fitting range: est_hi_lim = 'yes'. If 'no' the hi_fit_energ will be used.
    est_hi_lim = 'yes'
    
    # Read ON and OFF cubes
    filename_on = 'non_cube.fits.gz' # non_cube_convolved.fits
    cube_on = SkyCube.read(filename_on)
    
    ann_filename_off = 'noff_withpuls_cube.fits.gz'
    ann_cube_off = SkyCube.read(ann_filename_off)
    circ_filename_off = 'noff_withneb_cube.fits.gz'
    circ_cube_off = SkyCube.read(circ_filename_off)
    
    # Read config and IRFs
    config = read_config('config.yaml')
    irfs = get_irfs(config)
    offset = Angle(config['selection']['offset_fov'] * u.deg)
    livetime = u.Quantity(config['pointing']['livetime']).to('second')
    alpha_obs = 1.
    binsz = config['binning']['binsz']
    aeff = irfs['aeff'].to_effective_area_table(offset = offset, energy = cube_on.energies('edges'))
    edisp = irfs['edisp'].to_energy_dispersion(offset = offset, e_true = aeff.energy.bins, e_reco = cube_on.energies('edges') )
    
    # Define circular on/off Regions parameters
    on_pos = SkyCoord(83.6333 * u.deg, 22.0144 * u.deg, frame='icrs')
    on_sizes = np.ones(20) * binsz * u.deg
    
    off_pos = SkyCoord(83.6333 * u.deg, 22.0144 * u.deg, frame='icrs')
    off_sizes = on_sizes * np.sqrt(1./alpha_obs)
    
    # Make Annular region
    on_rad_sizes = np.ones(len(on_sizes)) * 0.1 * binsz * u.deg
    off_rad_sizes = on_rad_sizes * np.sqrt(1./alpha_obs)
    widths = np.ones(len(on_sizes)) * 22 * binsz * u.deg
    out_rad_sizes = on_rad_sizes + widths
    
    ann_on_data, ann_off_data, ann_stats = make_annular_spectrum(on_pos, off_pos, on_rad_sizes, off_rad_sizes, out_rad_sizes, cube_on, ann_cube_off, alpha_obs)
    
    # Make circular region
    circ_on_data, circ_off_data, circ_stats = make_circular_spectrum(on_pos, off_pos, on_sizes, off_sizes, cube_on, circ_cube_off, alpha_obs)

    # Undo "holes" in circ/ann_stats
    if np.max(np.where(circ_stats == 1)) + 1 != circ_stats.sum():
        circ_stats[0:np.max(np.where(circ_stats == 1)) + 1][circ_stats[0:np.max(np.where(circ_stats == 1)) + 1] == 0] = 1.
    if np.max(np.where(ann_stats == 1)) + 1 != ann_stats.sum():
        ann_stats[0:np.max(np.where(ann_stats == 1)) + 1][ann_stats[0:np.max(np.where(ann_stats == 1)) + 1] == 0] = 1.
    
    # Make on/off vector
    ann_on_vector = PHACountsSpectrum(energy_lo = cube_on.energies('edges')[:-1], energy_hi= cube_on.energies('edges')[1:], data= ann_on_data['value'].data * ann_stats * u.ct, backscal = on_sizes[0].value, meta={'EXPOSURE' : livetime.value})
    circ_on_vector = PHACountsSpectrum(energy_lo = cube_on.energies('edges')[:-1], energy_hi= cube_on.energies('edges')[1:], data= circ_on_data['value'].data * circ_stats * u.ct, backscal = on_sizes[0].value, meta={'EXPOSURE' : livetime.value})
    
    
    ann_off_vector = PHACountsSpectrum(energy_lo = ann_cube_off.energies('edges')[:-1], energy_hi= ann_cube_off.energies('edges')[1:], data= ann_off_data['value'].data * ann_stats * u.ct, backscal = off_sizes[0].value, meta={'EXPOSURE' : livetime.value, 'OFFSET' : 0.3 * u.deg})
    circ_off_vector = PHACountsSpectrum(energy_lo = circ_cube_off.energies('edges')[:-1], energy_hi= circ_cube_off.energies('edges')[1:], data= circ_off_data['value'].data * circ_stats * u.ct, backscal = off_sizes[0].value, meta={'EXPOSURE' : livetime.value, 'OFFSET' : 0.3 * u.deg})

    # Make SpectrumObservation

    ann_sed_table = SpectrumObservation(on_vector = ann_on_vector, off_vector = ann_off_vector, aeff = aeff, edisp = edisp)
    circ_sed_table = SpectrumObservation(on_vector = circ_on_vector, off_vector = circ_off_vector, aeff = aeff, edisp = edisp)

    ##Debug
    #print(ann_stats)
    #print(circ_stats)
    
    # Define Spectral Model

    model2fit1 = LogParabola(amplitude=1e-11 * u.Unit('cm-2 s-1 TeV-1'), reference=1 * u.TeV, alpha=2.5 * u.Unit(''), beta=0.1 * u.Unit(''))
    model2fit2 = ExponentialCutoffPowerLaw(index = 1. * u.Unit(''), amplitude = 1e-11 * u.Unit('cm-2 s-1 TeV-1'), reference= 1 * u.TeV,  lambda_= 0. * u.Unit('TeV-1'))
    model2fit3 = PowerLaw(index= 2.5 * u.Unit(''), amplitude= 5e-11 * u.Unit('cm-2 s-1 TeV-1'), reference= 0.15 * u.TeV)

    model2fit3.parameters['amplitude'].parmin = 1e-12
    model2fit3.parameters['amplitude'].parmax = 1e-10
    
    model2fit3.parameters['index'].parmin = 2.0
    model2fit3.parameters['index'].parmax = 4.0

    #Models to fit the circular and annular observations
    models_ann_fit = [model2fit1, model2fit2, model2fit3]
    models_circ_fit = [model2fit1, model2fit2, model2fit3]
    
    # Fit
    if est_hi_lim = 'yes':
        hi_fit_energ = cube_on.energies('edges')[int(np.sum(ann_stats))]