def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2, amplitude=1e5 / u.TeV, reference=0.1 * u.TeV) self.bkg_model = PowerLawSpectralModel(index=3, amplitude=1e4 / u.TeV, reference=0.1 * u.TeV) self.alpha = 0.1 random_state = get_random_state(23) npred = self.source_model.integral(binning[:-1], binning[1:]) source_counts = random_state.poisson(npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) # Currently it's necessary to specify a lifetime self.src.livetime = 1 * u.s npred_bkg = self.bkg_model.integral(binning[:-1], binning[1:]) bkg_counts = random_state.poisson(npred_bkg) off_counts = random_state.poisson(npred_bkg * 1.0 / self.alpha) self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_counts) self.off = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=off_counts)
def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2, amplitude=1e5 * u.Unit("cm-2 s-1 TeV-1"), reference=0.1 * u.TeV) bkg_model = PowerLawSpectralModel(index=3, amplitude=1e4 * u.Unit("cm-2 s-1 TeV-1"), reference=0.1 * u.TeV) self.alpha = 0.1 random_state = get_random_state(23) npred = self.source_model.integral(binning[:-1], binning[1:]).value source_counts = random_state.poisson(npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) self.src.livetime = 1 * u.s self.aeff = EffectiveAreaTable.from_constant(binning, "1 cm2") npred_bkg = bkg_model.integral(binning[:-1], binning[1:]).value bkg_counts = random_state.poisson(npred_bkg) off_counts = random_state.poisson(npred_bkg * 1.0 / self.alpha) self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_counts) self.off = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=off_counts)
def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2.1, amplitude=1e5 / u.TeV / u.s, reference=0.1 * u.TeV) self.livetime = 100 * u.s bkg_rate = np.ones(self.nbins) / u.s bkg_expected = bkg_rate * self.livetime self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_expected) random_state = get_random_state(23) self.npred = (self.source_model.integral(binning[:-1], binning[1:]) * self.livetime) self.npred += bkg_expected source_counts = random_state.poisson(self.npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) self.dataset = SpectrumDataset( model=self.source_model, counts=self.src, livetime=self.livetime, background=self.bkg, )
def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2.1, amplitude=1e5 * u.Unit("cm-2 s-1 TeV-1"), reference=0.1 * u.TeV) self.livetime = 100 * u.s aeff = EffectiveAreaTable.from_constant(binning, "1 cm2") bkg_rate = np.ones(self.nbins) / u.s bkg_expected = (bkg_rate * self.livetime).to_value("") self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_expected) random_state = get_random_state(23) flux = self.source_model.integral(binning[:-1], binning[1:]) self.npred = (flux * aeff.data.data[0] * self.livetime).to_value("") self.npred += bkg_expected source_counts = random_state.poisson(self.npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) self.dataset = SpectrumDataset( model=self.source_model, counts=self.src, aeff=aeff, livetime=self.livetime, background=self.bkg, )
def setup(self): etrue = np.logspace(-1, 1, 10) * u.TeV self.e_true = etrue ereco = np.logspace(-1, 1, 5) * u.TeV elo = ereco[:-1] ehi = ereco[1:] self.e_reco = ereco self.aeff = EffectiveAreaTable(etrue[:-1], etrue[1:], np.ones(9) * u.cm**2) self.edisp = EDispKernel.from_diagonal_response(etrue, ereco) data = np.ones(elo.shape) data[-1] = 0 # to test stats calculation with empty bins self.on_counts = CountsSpectrum(elo, ehi, data) self.off_counts = CountsSpectrum(elo, ehi, np.ones(elo.shape) * 10) start = u.Quantity([0], "s") stop = u.Quantity([1000], "s") time_ref = Time("2010-01-01 00:00:00.0") self.gti = GTI.create(start, stop, time_ref) self.livetime = self.gti.time_sum self.dataset = SpectrumDatasetOnOff( counts=self.on_counts, counts_off=self.off_counts, aeff=self.aeff, edisp=self.edisp, livetime=self.livetime, acceptance=np.ones(elo.shape), acceptance_off=np.ones(elo.shape) * 10, name="test", gti=self.gti, )
def make_observation_list(): """obs with dummy IRF""" nbin = 3 energy = np.logspace(-1, 1, nbin + 1) * u.TeV livetime = 2 * u.h data_on = np.arange(nbin) dataoff_1 = np.ones(3) dataoff_2 = np.ones(3) * 3 dataoff_1[1] = 0 dataoff_2[1] = 0 on_vector = CountsSpectrum(energy_lo=energy[:-1], energy_hi=energy[1:], data=data_on) off_vector1 = CountsSpectrum(energy_lo=energy[:-1], energy_hi=energy[1:], data=dataoff_1) off_vector2 = CountsSpectrum(energy_lo=energy[:-1], energy_hi=energy[1:], data=dataoff_2) aeff = EffectiveAreaTable.from_constant(energy, "1 cm2") edisp = EDispKernel.from_gauss(e_true=energy, e_reco=energy, sigma=0.2, bias=0) time_ref = Time("2010-01-01") gti1 = make_gti({ "START": [5, 6, 1, 2], "STOP": [8, 7, 3, 4] }, time_ref=time_ref) gti2 = make_gti({"START": [14], "STOP": [15]}, time_ref=time_ref) obs1 = SpectrumDatasetOnOff( counts=on_vector, counts_off=off_vector1, aeff=aeff, edisp=edisp, livetime=livetime, mask_safe=np.ones(on_vector.energy.nbin, dtype=bool), acceptance=1, acceptance_off=2, name="1", gti=gti1, ) obs2 = SpectrumDatasetOnOff( counts=on_vector, counts_off=off_vector2, aeff=aeff, edisp=edisp, livetime=livetime, mask_safe=np.ones(on_vector.energy.nbin, dtype=bool), acceptance=1, acceptance_off=4, name="2", gti=gti2, ) obs_list = [obs1, obs2] return obs_list
def setup(self): etrue = np.logspace(-1, 1, 10) * u.TeV self.e_true = etrue ereco = np.logspace(-1, 1, 5) * u.TeV elo = ereco[:-1] ehi = ereco[1:] self.aeff = EffectiveAreaTable(etrue[:-1], etrue[1:], np.ones(9) * u.cm**2) self.edisp = EnergyDispersion.from_diagonal_response(etrue, ereco) data = np.ones(elo.shape) data[-1] = 0 # to test stats calculation with empty bins self.on_counts = CountsSpectrum(elo, ehi, data) self.off_counts = CountsSpectrum(elo, ehi, np.ones(elo.shape) * 10) self.livetime = 1000 * u.s self.dataset = SpectrumDatasetOnOff( counts=self.on_counts, counts_off=self.off_counts, aeff=self.aeff, edisp=self.edisp, livetime=self.livetime, acceptance=np.ones(elo.shape), acceptance_off=np.ones(elo.shape) * 10, obs_id="test", )
def _counts_spectrum(self, ebounds): from gammapy.spectrum import CountsSpectrum if not ebounds: ebounds = self._default_plot_ebounds() spec = CountsSpectrum(energy_lo=ebounds[:-1], energy_hi=ebounds[1:]) spec.fill_energy(self.energy) return spec
def get_spectrum(self, region=None, func=np.nansum): """Extract spectrum in a given region. The spectrum can be computed by summing (or, more generally, applying ``func``) along the spatial axes in each energy bin. This occurs only inside the ``region``, which by default is assumed to be the whole spatial extension of the map. Parameters ---------- region: `~regions.Region` Region (pixel or sky regions accepted). func : numpy.ufunc Function to reduce the data. Returns ------- spectrum : `~gammapy.spectrum.CountsSpectrum` Spectrum in the given region. """ from gammapy.spectrum import CountsSpectrum energy_axis = self.geom.get_axis_by_name("energy") if region: mask = self.geom.region_mask([region]) data = self.data[mask].reshape(energy_axis.nbin, -1) data = func(data, axis=1) else: data = func(self.data, axis=(1, 2)) edges = energy_axis.edges return CountsSpectrum(data=data, energy_lo=edges[:-1], energy_hi=edges[1:], unit=self.unit)
def test_fake(self): """Test the fake dataset""" source_model = SkyModel(spectral_model=PowerLawSpectralModel()) dataset = SpectrumDatasetOnOff( counts=self.on_counts, counts_off=self.off_counts, models=source_model, aeff=self.aeff, livetime=self.livetime, edisp=self.edisp, acceptance=1, acceptance_off=10, ) real_dataset = dataset.copy() # Define background model counts elo = self.on_counts.energy.edges[:-1] ehi = self.on_counts.energy.edges[1:] data = np.ones(self.on_counts.data.shape) background_model = CountsSpectrum(elo, ehi, data) dataset.fake(background_model=background_model, random_state=314, name="fake") assert real_dataset.counts.data.shape == dataset.counts.data.shape assert real_dataset.counts_off.data.shape == dataset.counts_off.data.shape assert (real_dataset.counts.energy.center.mean() == dataset.counts.energy.center.mean()) assert real_dataset.acceptance.mean() == dataset.acceptance.mean() assert real_dataset.acceptance_off.mean( ) == dataset.acceptance_off.mean() assert dataset.counts_off.data.sum() == 39 assert dataset.counts.data.sum() == 5 assert dataset.name == "fake"
def sens(): etrue = np.logspace(0, 1, 21) * u.TeV elo = etrue[:-1] ehi = etrue[1:] area = np.zeros(20) + 1e6 * u.m**2 arf = EffectiveAreaTable(energy_lo=elo, energy_hi=ehi, data=area) ereco = np.logspace(0, 1, 5) * u.TeV rmf = EnergyDispersion.from_diagonal_response(etrue, ereco) bkg_array = np.ones(4) bkg_array[-1] = 1e-3 bkg = CountsSpectrum(energy_lo=ereco[:-1], energy_hi=ereco[1:], data=bkg_array, unit="s-1") sens = SensitivityEstimator(arf=arf, rmf=rmf, bkg=bkg, livetime=1 * u.h, index=2, gamma_min=20, alpha=0.2) sens.run() return sens
class TestSimpleFit: """Test fit on counts spectra without any IRFs""" def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2, amplitude=1e5 / u.TeV, reference=0.1 * u.TeV) self.bkg_model = PowerLawSpectralModel(index=3, amplitude=1e4 / u.TeV, reference=0.1 * u.TeV) self.alpha = 0.1 random_state = get_random_state(23) npred = self.source_model.integral(binning[:-1], binning[1:]) source_counts = random_state.poisson(npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) # Currently it's necessary to specify a lifetime self.src.livetime = 1 * u.s npred_bkg = self.bkg_model.integral(binning[:-1], binning[1:]) bkg_counts = random_state.poisson(npred_bkg) off_counts = random_state.poisson(npred_bkg * 1.0 / self.alpha) self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_counts) self.off = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=off_counts) def test_wstat(self): """WStat with on source and background spectrum""" on_vector = self.src.copy() on_vector.data += self.bkg.data obs = SpectrumDatasetOnOff( counts=on_vector, counts_off=self.off, acceptance=1, acceptance_off=1 / self.alpha, ) obs.model = self.source_model self.source_model.parameters.index = 1.12 fit = Fit(obs) result = fit.run() pars = self.source_model.parameters assert_allclose(pars["index"].value, 1.997342, rtol=1e-3) assert_allclose(pars["amplitude"].value, 100245.187067, rtol=1e-3) assert_allclose(result.total_stat, 30.022316, rtol=1e-3)
class TestCountsSpectrum: def setup(self): self.counts = [0, 0, 2, 5, 17, 3] self.bins = energy_logspace(1, 10, 7, "TeV") self.spec = CountsSpectrum(data=self.counts, energy_lo=self.bins[:-1], energy_hi=self.bins[1:]) def test_wrong_init(self): bins = energy_logspace(1, 10, 8, "TeV") with pytest.raises(ValueError): CountsSpectrum(data=self.counts, energy_lo=bins[:-1], energy_hi=bins[1:]) @requires_dependency("matplotlib") def test_plot(self): with mpl_plot_check(): self.spec.plot(show_energy=1 * u.TeV) with mpl_plot_check(): self.spec.plot_hist() with mpl_plot_check(): self.spec.peek() def test_io(self, tmpdir): filename = tmpdir / "test.fits" self.spec.write(filename) spec2 = CountsSpectrum.read(filename) assert_quantity_allclose(spec2.energy.edges, self.bins) def test_downsample(self): rebinned_spec = self.spec.downsample(2) assert rebinned_spec.energy.nbin == self.spec.energy.nbin / 2 assert rebinned_spec.data.shape[0] == self.spec.data.shape[0] / 2 assert rebinned_spec.total_counts == self.spec.total_counts idx = rebinned_spec.energy.coord_to_idx([2, 3, 5] * u.TeV) actual = rebinned_spec.data[idx] desired = [0, 7, 20] assert (actual == desired).all()
def spectrum_dataset(): e_true = np.logspace(0, 1, 21) * u.TeV e_reco = np.logspace(0, 1, 5) * u.TeV aeff = EffectiveAreaTable.from_constant(value=1e6 * u.m**2, energy=e_true) edisp = EDispKernel.from_diagonal_response(e_true, e_reco) data = 3600 * np.ones(4) data[-1] *= 1e-3 background = CountsSpectrum(energy_lo=e_reco[:-1], energy_hi=e_reco[1:], data=data) return SpectrumDataset(aeff=aeff, livetime="1h", edisp=edisp, background=background)
def test_spectrum_dataset_stack_diagonal_safe_mask(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp = EDispKernel.from_diagonal_response(self.src.energy.edges, self.src.energy.edges) livetime = self.livetime dataset1 = SpectrumDataset( counts=self.src.copy(), livetime=livetime, aeff=aeff, edisp=edisp, background=self.bkg.copy(), ) livetime2 = 0.5 * livetime aeff2 = EffectiveAreaTable(self.src.energy.edges[:-1], self.src.energy.edges[1:], 2 * aeff.data.data) bkg2 = CountsSpectrum( self.src.energy.edges[:-1], self.src.energy.edges[1:], data=2 * self.bkg.data, ) safe_mask2 = np.ones_like(self.src.data, bool) safe_mask2[0] = False dataset2 = SpectrumDataset( counts=self.src.copy(), livetime=livetime2, aeff=aeff2, edisp=edisp, background=bkg2, mask_safe=safe_mask2, ) dataset1.stack(dataset2) assert_allclose(dataset1.counts.data[1:], self.src.data[1:] * 2) assert_allclose(dataset1.counts.data[0], self.src.data[0]) assert dataset1.livetime == 1.5 * self.livetime assert_allclose(dataset1.background.data[1:], 3 * self.bkg.data[1:]) assert_allclose(dataset1.background.data[0], self.bkg.data[0]) assert_allclose( dataset1.aeff.data.data.to_value("m2"), 4.0 / 3 * aeff.data.data.to_value("m2"), ) assert_allclose(dataset1.edisp.pdf_matrix[1:], edisp.pdf_matrix[1:]) assert_allclose(dataset1.edisp.pdf_matrix[0], 0.5 * edisp.pdf_matrix[0])
def plot_energy_hist(self, ax=None, ebounds=None, **kwargs): """ A plot showing counts as a function of energy. Convert to a `~gammapy.spectrum.CountsSpectrum` internally """ if ebounds is None: emin = np.min(self['ENERGY'].quantity) emax = np.max(self['ENERGY'].quantity) ebounds = EnergyBounds.equal_log_spacing(emin, emax, 100) from gammapy.spectrum import CountsSpectrum spec = CountsSpectrum.from_eventlist(self, ebounds) spec.plot(ax=ax, **kwargs) return ax
def plot_energy_hist(self, ax=None, ebounds=None, **kwargs): """ A plot showing counts as a function of energy. Convert to a `~gammapy.spectrum.CountsSpectrum` internally """ if ebounds is None: emin = np.min(self['ENERGY'].quantity) emax = np.max(self['ENERGY'].quantity) ebounds = EnergyBounds.equal_log_spacing(emin, emax, 100) from gammapy.spectrum import CountsSpectrum spec = CountsSpectrum.from_eventlist(self, ebounds) spec.plot(ax=ax, **kwargs) return ax
def setup(self): self.counts = [0, 0, 2, 5, 17, 3] self.bins = energy_logspace(1, 10, 7, "TeV") self.spec = CountsSpectrum( data=self.counts, energy_lo=self.bins[:-1], energy_hi=self.bins[1:] )
def test_io(self, tmpdir): filename = tmpdir / "test.fits" self.spec.write(filename) spec2 = CountsSpectrum.read(filename) assert_quantity_allclose(spec2.energy.edges, self.bins)
class TestSpectrumDataset: """Test fit on counts spectra without any IRFs""" def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = SkyModel(spectral_model=PowerLawSpectralModel( index=2.1, amplitude=1e5 * u.Unit("cm-2 s-1 TeV-1"), reference=0.1 * u.TeV, )) self.livetime = 100 * u.s aeff = EffectiveAreaTable.from_constant(binning, "1 cm2") bkg_rate = np.ones(self.nbins) / u.s bkg_expected = (bkg_rate * self.livetime).to_value("") self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_expected) random_state = get_random_state(23) flux = self.source_model.spectral_model.integral( binning[:-1], binning[1:]) self.npred = (flux * aeff.data.data[0] * self.livetime).to_value("") self.npred += bkg_expected source_counts = random_state.poisson(self.npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) self.dataset = SpectrumDataset( models=self.source_model, counts=self.src, aeff=aeff, livetime=self.livetime, background=self.bkg, name="test", ) def test_data_shape(self): assert self.dataset.data_shape[0] == self.nbins def test_energy_range(self): energy_range = self.dataset.energy_range assert energy_range.unit == u.TeV assert_allclose(energy_range.to_value("TeV"), [0.1, 10.0]) def test_cash(self): """Simple CASH fit to the on vector""" fit = Fit([self.dataset]) result = fit.run() # assert result.success assert "minuit" in repr(result) npred = self.dataset.npred().data.sum() assert_allclose(npred, self.npred.sum(), rtol=1e-3) assert_allclose(result.total_stat, -18087404.624, rtol=1e-3) pars = result.parameters assert_allclose(pars["index"].value, 2.1, rtol=1e-2) assert_allclose(pars.error("index"), 0.00127, rtol=1e-2) assert_allclose(pars["amplitude"].value, 1e5, rtol=1e-3) assert_allclose(pars.error("amplitude"), 153.450, rtol=1e-2) def test_fake(self): """Test the fake dataset""" real_dataset = self.dataset.copy() self.dataset.fake(314) assert real_dataset.counts.data.shape == self.dataset.counts.data.shape assert real_dataset.background.data.sum( ) == self.dataset.background.data.sum() assert int(real_dataset.counts.data.sum()) == 907010 assert self.dataset.counts.data.sum() == 907331 def test_incorrect_mask(self): mask_fit = np.ones(self.nbins, dtype=np.dtype("float")) with pytest.raises(ValueError): SpectrumDataset( models=self.source_model, counts=self.src, livetime=self.livetime, mask_fit=mask_fit, background=self.bkg, ) def test_set_model(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp = EDispKernel.from_diagonal_response(self.src.energy.edges, self.src.energy.edges) dataset = SpectrumDataset(None, self.src, self.livetime, None, aeff, edisp, self.bkg) spectral_model = PowerLawSpectralModel() model = SkyModel(spectral_model=spectral_model, name="test") dataset.models = model assert dataset.models["test"] is model models = Models([model]) dataset.models = models assert dataset.models["test"] is model def test_npred_models(self): e_reco = MapAxis.from_energy_bounds("1 TeV", "10 TeV", nbin=3).edges dataset = SpectrumDataset.create(e_reco=e_reco) dataset.livetime = 1 * u.h dataset.aeff.data.data += 1e10 * u.Unit("cm2") pwl_1 = PowerLawSpectralModel(index=2) pwl_2 = PowerLawSpectralModel(index=2) model_1 = SkyModel(spectral_model=pwl_1) model_2 = SkyModel(spectral_model=pwl_2) dataset.models = Models([model_1, model_2]) npred = dataset.npred() assert_allclose(npred.data.sum(), 64.8) def test_str(self): assert "SpectrumDataset" in str(self.dataset) def test_spectrumdataset_create(self): e_reco = u.Quantity([0.1, 1, 10.0], "TeV") e_true = u.Quantity([0.05, 0.5, 5, 20.0], "TeV") empty_dataset = SpectrumDataset.create(e_reco, e_true, name="test") assert empty_dataset.name == "test" assert empty_dataset.counts.total_counts == 0 assert empty_dataset.data_shape[0] == 2 assert empty_dataset.background.total_counts == 0 assert empty_dataset.background.energy.nbin == 2 assert empty_dataset.aeff.data.axis("energy").nbin == 3 assert empty_dataset.edisp.data.axis("e_reco").nbin == 2 assert empty_dataset.livetime.value == 0 assert len(empty_dataset.gti.table) == 0 assert empty_dataset.energy_range[0] is None assert_allclose(empty_dataset.mask_safe, 0) def test_spectrum_dataset_stack_diagonal_safe_mask(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp = EDispKernel.from_diagonal_response(self.src.energy.edges, self.src.energy.edges) livetime = self.livetime dataset1 = SpectrumDataset( counts=self.src.copy(), livetime=livetime, aeff=aeff, edisp=edisp, background=self.bkg.copy(), ) livetime2 = 0.5 * livetime aeff2 = EffectiveAreaTable(self.src.energy.edges[:-1], self.src.energy.edges[1:], 2 * aeff.data.data) bkg2 = CountsSpectrum( self.src.energy.edges[:-1], self.src.energy.edges[1:], data=2 * self.bkg.data, ) safe_mask2 = np.ones_like(self.src.data, bool) safe_mask2[0] = False dataset2 = SpectrumDataset( counts=self.src.copy(), livetime=livetime2, aeff=aeff2, edisp=edisp, background=bkg2, mask_safe=safe_mask2, ) dataset1.stack(dataset2) assert_allclose(dataset1.counts.data[1:], self.src.data[1:] * 2) assert_allclose(dataset1.counts.data[0], self.src.data[0]) assert dataset1.livetime == 1.5 * self.livetime assert_allclose(dataset1.background.data[1:], 3 * self.bkg.data[1:]) assert_allclose(dataset1.background.data[0], self.bkg.data[0]) assert_allclose( dataset1.aeff.data.data.to_value("m2"), 4.0 / 3 * aeff.data.data.to_value("m2"), ) assert_allclose(dataset1.edisp.pdf_matrix[1:], edisp.pdf_matrix[1:]) assert_allclose(dataset1.edisp.pdf_matrix[0], 0.5 * edisp.pdf_matrix[0]) def test_spectrum_dataset_stack_nondiagonal_no_bkg(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp1 = EDispKernel.from_gauss(self.src.energy.edges, self.src.energy.edges, 0.1, 0.0) livetime = self.livetime dataset1 = SpectrumDataset(counts=None, livetime=livetime, aeff=aeff, edisp=edisp1, background=None) livetime2 = livetime aeff2 = EffectiveAreaTable(self.src.energy.edges[:-1], self.src.energy.edges[1:], aeff.data.data) edisp2 = EDispKernel.from_gauss(self.src.energy.edges, self.src.energy.edges, 0.2, 0.0) dataset2 = SpectrumDataset( counts=self.src.copy(), livetime=livetime2, aeff=aeff2, edisp=edisp2, background=None, ) dataset1.stack(dataset2) assert dataset1.counts is None assert dataset1.background is None assert dataset1.livetime == 2 * self.livetime assert_allclose(dataset1.aeff.data.data.to_value("m2"), aeff.data.data.to_value("m2")) assert_allclose(dataset1.edisp.get_bias(1 * u.TeV), 0.0, atol=1.2e-3) assert_allclose(dataset1.edisp.get_resolution(1 * u.TeV), 0.1581, atol=1e-2) def test_info_dict(self): info_dict = self.dataset.info_dict() assert_allclose(info_dict["n_on"], 907010) assert_allclose(info_dict["background"], 3000.0) assert_allclose(info_dict["significance"], 2924.522174) assert_allclose(info_dict["excess"], 904010) assert_allclose(info_dict["livetime"].value, 1e2) assert info_dict["name"] == "test" @requires_dependency("matplotlib") def test_peek(self): with mpl_plot_check(): self.dataset.peek() self.dataset.edisp = None with mpl_plot_check(): self.dataset.peek() @requires_dependency("matplotlib") def test_plot_fit(self): with mpl_plot_check(): self.dataset.plot_fit()
class TestSpectrumDataset: """Test fit on counts spectra without any IRFs""" def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2.1, amplitude=1e5 / u.TeV / u.s, reference=0.1 * u.TeV) self.livetime = 100 * u.s bkg_rate = np.ones(self.nbins) / u.s bkg_expected = bkg_rate * self.livetime self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_expected) random_state = get_random_state(23) self.npred = (self.source_model.integral(binning[:-1], binning[1:]) * self.livetime) self.npred += bkg_expected source_counts = random_state.poisson(self.npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) self.dataset = SpectrumDataset( model=self.source_model, counts=self.src, livetime=self.livetime, background=self.bkg, ) def test_data_shape(self): assert self.dataset.data_shape[0] == self.nbins def test_energy_range(self): energy_range = self.dataset.energy_range assert energy_range.unit == u.TeV assert_allclose(energy_range.to_value("TeV"), [0.1, 10.0]) def test_cash(self): """Simple CASH fit to the on vector""" fit = Fit(self.dataset) result = fit.run() assert result.success assert "minuit" in repr(result) npred = self.dataset.npred().data.sum() assert_allclose(npred, self.npred.sum(), rtol=1e-3) assert_allclose(result.total_stat, -18087404.624, rtol=1e-3) pars = result.parameters assert_allclose(pars["index"].value, 2.1, rtol=1e-2) assert_allclose(pars.error("index"), 0.00127, rtol=1e-2) assert_allclose(pars["amplitude"].value, 1e5, rtol=1e-3) assert_allclose(pars.error("amplitude"), 153.450, rtol=1e-2) def test_fake(self): """Test the fake dataset""" real_dataset = self.dataset.copy() self.dataset.fake(314) assert real_dataset.counts.data.shape == self.dataset.counts.data.shape assert real_dataset.background.data.sum( ) == self.dataset.background.data.sum() assert int(real_dataset.counts.data.sum()) == 907010 assert self.dataset.counts.data.sum() == 907331 def test_incorrect_mask(self): mask_fit = np.ones(self.nbins, dtype=np.dtype("float")) with pytest.raises(ValueError): SpectrumDataset( model=self.source_model, counts=self.src, livetime=self.livetime, mask_fit=mask_fit, background=self.bkg, ) def test_set_model(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp = EnergyDispersion.from_diagonal_response( self.src.energy.edges, self.src.energy.edges) dataset = SpectrumDataset(None, self.src, self.livetime, None, aeff, edisp, self.bkg) with pytest.raises(AttributeError): dataset.parameters dataset.model = self.source_model assert dataset.parameters[0] == self.source_model.parameters[0] def test_str(self): assert "SpectrumDataset" in str(self.dataset) def test_spectrumdataset_create(self): e_reco = u.Quantity([0.1, 1, 10.0], "TeV") e_true = u.Quantity([0.05, 0.5, 5, 20.0], "TeV") empty_dataset = SpectrumDataset.create(e_reco, e_true) assert empty_dataset.counts.total_counts == 0 assert empty_dataset.data_shape[0] == 2 assert empty_dataset.background.total_counts == 0 assert empty_dataset.background.energy.nbin == 2 assert empty_dataset.aeff.data.axis("energy").nbin == 3 assert empty_dataset.edisp.data.axis("e_reco").nbin == 2 assert empty_dataset.livetime.value == 0 assert len(empty_dataset.gti.table) == 0 assert empty_dataset.energy_range[0] is None assert_allclose(empty_dataset.mask_safe, 0) def test_spectrum_dataset_stack_diagonal_safe_mask(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp = EnergyDispersion.from_diagonal_response( self.src.energy.edges, self.src.energy.edges) livetime = self.livetime dataset1 = SpectrumDataset( counts=self.src.copy(), livetime=livetime, aeff=aeff, edisp=edisp, background=self.bkg.copy(), ) livetime2 = 0.5 * livetime aeff2 = EffectiveAreaTable(self.src.energy.edges[:-1], self.src.energy.edges[1:], 2 * aeff.data.data) bkg2 = CountsSpectrum( self.src.energy.edges[:-1], self.src.energy.edges[1:], data=2 * self.bkg.data, ) safe_mask2 = np.ones_like(self.src.data, bool) safe_mask2[0] = False dataset2 = SpectrumDataset( counts=self.src.copy(), livetime=livetime2, aeff=aeff2, edisp=edisp, background=bkg2, mask_safe=safe_mask2, ) dataset1.stack(dataset2) assert_allclose(dataset1.counts.data[1:], self.src.data[1:] * 2) assert_allclose(dataset1.counts.data[0], self.src.data[0]) assert dataset1.livetime == 1.5 * self.livetime assert_allclose(dataset1.background.data[1:], 3 * self.bkg.data[1:]) assert_allclose(dataset1.background.data[0], self.bkg.data[0]) assert_allclose( dataset1.aeff.data.data.to_value("m2"), 4.0 / 3 * aeff.data.data.to_value("m2"), ) assert_allclose(dataset1.edisp.pdf_matrix[1:], edisp.pdf_matrix[1:]) assert_allclose(dataset1.edisp.pdf_matrix[0], 0.5 * edisp.pdf_matrix[0]) def test_spectrum_dataset_stack_nondiagonal_no_bkg(self): aeff = EffectiveAreaTable.from_parametrization(self.src.energy.edges, "HESS") edisp1 = EnergyDispersion.from_gauss(self.src.energy.edges, self.src.energy.edges, 0.1, 0.0) livetime = self.livetime dataset1 = SpectrumDataset(counts=None, livetime=livetime, aeff=aeff, edisp=edisp1, background=None) livetime2 = livetime aeff2 = EffectiveAreaTable(self.src.energy.edges[:-1], self.src.energy.edges[1:], aeff.data.data) edisp2 = EnergyDispersion.from_gauss(self.src.energy.edges, self.src.energy.edges, 0.2, 0.0) dataset2 = SpectrumDataset( counts=self.src.copy(), livetime=livetime2, aeff=aeff2, edisp=edisp2, background=None, ) dataset1.stack(dataset2) assert dataset1.counts is None assert dataset1.background is None assert dataset1.livetime == 2 * self.livetime assert_allclose(dataset1.aeff.data.data.to_value("m2"), aeff.data.data.to_value("m2")) assert_allclose(dataset1.edisp.get_bias(1 * u.TeV), 0.0, atol=1e-3) assert_allclose(dataset1.edisp.get_resolution(1 * u.TeV), 0.1581, atol=1e-2)
def test_wrong_init(self): bins = MapAxis.from_energy_bounds(1, 10, 8, "TeV").edges with pytest.raises(ValueError): CountsSpectrum(data=self.counts, energy_lo=bins[:-1], energy_hi=bins[1:])
def setup(self): self.counts = [0, 0, 2, 5, 17, 3] self.bins = MapAxis.from_energy_bounds(1, 10, 6, "TeV").edges self.spec = CountsSpectrum(data=self.counts, energy_lo=self.bins[:-1], energy_hi=self.bins[1:])
# In[ ]: arf.data.data *= containment # ## Estimate background # # We now provide a workaround to estimate the background from the tabulated IRF in the energy bins we consider. # In[ ]: bkg_data = irfs["bkg"].evaluate_integrate(fov_lon=0 * u.deg, fov_lat=offset, energy_reco=energy_reco) bkg = CountsSpectrum(energy_reco[:-1], energy_reco[1:], data=(bkg_data * solid_angles)) # ## Compute sensitivity # # We impose a minimal number of expected signal counts of 5 per bin and a minimal significance of 3 per bin. We assume an alpha of 0.2 (ratio between ON and OFF area). # We then run the sensitivity estimator. # In[ ]: sensitivity_estimator = SensitivityEstimator(arf=arf, rmf=rmf, bkg=bkg, livetime="5h", gamma_min=5, sigma=3,
# In[ ]: arf.data.data *= containment # ## Estimate background # # We now provide a workaround to estimate the background from the tabulated IRF in the energy bins we consider. # In[ ]: bkg_data = irfs["bkg"].evaluate_integrate(fov_lon=0 * u.deg, fov_lat=offset, energy_reco=energy_reco) bkg = CountsSpectrum( energy_reco[:-1], energy_reco[1:], data=(bkg_data * solid_angles).to_value("s-1"), unit="s-1", ) # ## Compute sensitivity # # We impose a minimal number of expected signal counts of 5 per bin and a minimal significance of 3 per bin. We assume an alpha of 0.2 (ratio between ON and OFF area). # We then run the sensitivity estimator. # In[ ]: sensitivity_estimator = SensitivityEstimator(arf=arf, rmf=rmf, bkg=bkg, livetime="5h", gamma_min=5,
class TestFit: """Test fit on counts spectra without any IRFs""" def setup(self): self.nbins = 30 binning = np.logspace(-1, 1, self.nbins + 1) * u.TeV self.source_model = PowerLawSpectralModel(index=2, amplitude=1e5 * u.Unit("cm-2 s-1 TeV-1"), reference=0.1 * u.TeV) bkg_model = PowerLawSpectralModel(index=3, amplitude=1e4 * u.Unit("cm-2 s-1 TeV-1"), reference=0.1 * u.TeV) self.alpha = 0.1 random_state = get_random_state(23) npred = self.source_model.integral(binning[:-1], binning[1:]).value source_counts = random_state.poisson(npred) self.src = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=source_counts) self.src.livetime = 1 * u.s self.aeff = EffectiveAreaTable.from_constant(binning, "1 cm2") npred_bkg = bkg_model.integral(binning[:-1], binning[1:]).value bkg_counts = random_state.poisson(npred_bkg) off_counts = random_state.poisson(npred_bkg * 1.0 / self.alpha) self.bkg = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=bkg_counts) self.off = CountsSpectrum(energy_lo=binning[:-1], energy_hi=binning[1:], data=off_counts) def test_cash(self): """Simple CASH fit to the on vector""" dataset = SpectrumDataset( model=self.source_model, counts=self.src, aeff=self.aeff, livetime=self.src.livetime, ) npred = dataset.npred().data assert_allclose(npred[5], 660.5171, rtol=1e-5) stat_val = dataset.likelihood() assert_allclose(stat_val, -107346.5291, rtol=1e-5) self.source_model.parameters["index"].value = 1.12 fit = Fit([dataset]) result = fit.run() # These values are check with sherpa fits, do not change pars = result.parameters assert_allclose(pars["index"].value, 1.995525, rtol=1e-3) assert_allclose(pars["amplitude"].value, 100245.9, rtol=1e-3) def test_wstat(self): """WStat with on source and background spectrum""" on_vector = self.src.copy() on_vector.data += self.bkg.data dataset = SpectrumDatasetOnOff( counts=on_vector, counts_off=self.off, aeff=self.aeff, livetime=self.src.livetime, acceptance=1, acceptance_off=1 / self.alpha, ) dataset.model = self.source_model self.source_model.parameters.index = 1.12 fit = Fit([dataset]) result = fit.run() pars = self.source_model.parameters assert_allclose(pars["index"].value, 1.997342, rtol=1e-3) assert_allclose(pars["amplitude"].value, 100245.187067, rtol=1e-3) assert_allclose(result.total_stat, 30.022316, rtol=1e-3) def test_fit_range(self): """Test fit range without complication of thresholds""" dataset = SpectrumDatasetOnOff(counts=self.src, mask_safe=np.ones(self.src.energy.nbin, dtype=bool)) dataset.model = self.source_model assert np.sum(dataset.mask_safe) == self.nbins e_min, e_max = dataset.energy_range assert_allclose(e_max.value, 10) assert_allclose(e_min.value, 0.1) def test_likelihood_profile(self): dataset = SpectrumDataset( model=self.source_model, aeff=self.aeff, livetime=self.src.livetime, counts=self.src, mask_safe=np.ones(self.src.energy.nbin, dtype=bool), ) fit = Fit([dataset]) result = fit.run() true_idx = result.parameters["index"].value values = np.linspace(0.95 * true_idx, 1.05 * true_idx, 100) profile = fit.likelihood_profile("index", values=values) actual = values[np.argmin(profile["likelihood"])] assert_allclose(actual, true_idx, rtol=0.01)
def test_wrong_init(self): bins = energy_logspace(1, 10, 8, "TeV") with pytest.raises(ValueError): CountsSpectrum(data=self.counts, energy_lo=bins[:-1], energy_hi=bins[1:])
def test_io(self, tmp_path): self.spec.write(tmp_path / "tmp.fits") spec2 = CountsSpectrum.read(tmp_path / "tmp.fits") assert_quantity_allclose(spec2.energy.edges, self.bins)
# # In this section we will include a background component extracted from the IRF. Furthermore, we will also simulate more than one observation and fit each one individually in order to get average fit results. # In[ ]: # We assume a PowerLawSpectralModel shape of the background as well bkg_data = ( cta_irf["bkg"].evaluate_integrate( fov_lon=0 * u.deg, fov_lat=offset, energy_reco=energy ) * solid_angle * livetime ) bkg = CountsSpectrum( energy[:-1], energy[1:], data=bkg_data.to_value(""), unit="" ) # In[ ]: dataset = SpectrumDatasetOnOff( aeff=aeff, edisp=edisp, model=model_ref, livetime=livetime, acceptance=1, acceptance_off=5, )