def to_edisp_kernel(self, offset, energy_true=None, energy=None): """Detector response R(Delta E_reco, Delta E_true) Probability to reconstruct an energy in a given true energy band in a given reconstructed energy band Parameters ---------- offset : `~astropy.coordinates.Angle` Offset energy_true : `~astropy.units.Quantity`, None True energy axis energy : `~astropy.units.Quantity` Reconstructed energy axis Returns ------- edisp : `~gammapy.irf.EDispKernel` Energy dispersion matrix """ offset = Angle(offset) # TODO: expect directly MapAxis here? if energy is None: energy_axis = self.axes["energy_true"].copy(name="energy") else: energy_axis = MapAxis.from_energy_edges(energy) if energy_true is None: energy_axis_true = self.axes["energy_true"] else: energy_axis_true = MapAxis.from_energy_edges( energy_true, name="energy_true", ) axes = MapAxes([energy_axis_true, energy_axis]) coords = axes.get_coord(mode="edges", axis_name="energy") # migration value of energy bounds migra = coords["energy"] / coords["energy_true"] values = self.integral( axis_name="migra", offset=offset, energy_true=coords["energy_true"], migra=migra, ) data = np.diff(values) return EDispKernel( axes=axes, data=data.to_value(""), )
def __init__(self, data, spectral_model): # TODO: Check data self.data = data if hasattr(self.data["norm"], "geom"): self.energy_axis = self.data["norm"].geom.axes["energy"] self._keys = self.data.keys() self._expand_slice = (slice(None), np.newaxis, np.newaxis) else: # Here we assume there is only one row per energy e_edges = self.data["e_min"].quantity e_edges = e_edges.insert(len(self.data), self.data["e_max"].quantity[-1]) self.energy_axis = MapAxis.from_energy_edges(e_edges) self._keys = self.data.columns self._expand_slice = slice(None) # Note that here we could use the specification from dnde_ref to build piecewise PL # But does it work beyond min and max centers? self.spectral_model = spectral_model self._available_quantities = [] for quantity in OPTIONAL_QUANTITIES: norm_quantity = f"norm_{quantity}" if norm_quantity in self._keys: self._available_quantities.append(quantity)
def run(self, dataset): """Make the profiles Parameters ---------- dataset : `~gammapy.datasets.MapDataset` or `~gammapy.datasets.MapDatasetOnOff` the dataset to use for profile extraction Returns -------- imageprofile : `~gammapy.estimators.ImageProfile` Return an image profile class containing the result """ if self.energy_edges is not None: axis = MapAxis.from_energy_edges(self.energy_edges) dataset = dataset.resample_energy_axis(energy_axis=axis) else: dataset = dataset.to_image() spectrum_datasets = self.get_spectrum_datasets(dataset) results = self.make_prof(spectrum_datasets) table = table_from_row_data(results) if isinstance(self.regions[0], RectangleSkyRegion): table.meta["PROFILE_TYPE"] = "orthogonal_rectangle" table.meta["SPECTRAL_MODEL"] = self.spectrum.to_dict() # return ImageProfile(table) return table
def test(aeff): assert aeff.data.axes["energy_true"].nbin == 96 assert aeff.data.axes["offset"].nbin == 6 assert aeff.data.data.shape == (96, 6) assert aeff.data.axes["energy_true"].unit == "TeV" assert aeff.data.axes["offset"].unit == "deg" assert aeff.data.data.unit == "m2" assert_quantity_allclose(aeff.high_threshold, 100 * u.TeV, rtol=1e-3) assert_quantity_allclose(aeff.low_threshold, 0.870964 * u.TeV, rtol=1e-3) test_val = aeff.data.evaluate(energy_true="14 TeV", offset="0.2 deg") assert_allclose(test_val.value, 683177.5, rtol=1e-3) # Test ARF export offset = 0.236 * u.deg e_axis = np.logspace(0, 1, 20) * u.TeV effareafrom2d = aeff.to_effective_area_table(offset, e_axis) energy = np.sqrt(e_axis[:-1] * e_axis[1:]) area = aeff.data.evaluate(energy_true=energy, offset=offset) energy_axis_true = MapAxis.from_energy_edges(e_axis, name="energy_true") effarea1d = EffectiveAreaTable(energy_axis_true=energy_axis_true, data=area) actual = effareafrom2d.data.evaluate(energy_true="2.34 TeV") desired = effarea1d.data.evaluate(energy_true="2.34 TeV") assert_equal(actual, desired) # Test ARF export #2 offset = 1.2 * u.deg actual = aeff.to_effective_area_table(offset=offset).data.data desired = aeff.data.evaluate(offset=offset) assert_allclose(actual.value, desired.value.squeeze(), rtol=1e-9)
def test_select_mask(models_gauss): center_sky = SkyCoord("0d", "0d") circle = CircleSkyRegion(center=center_sky, radius=1 * u.deg) axis = MapAxis.from_energy_edges(np.logspace(-1, 1, 3), unit="TeV") geom = WcsGeom.create(skydir=center_sky, width=(5, 4), axes=[axis], binsz=0.02) mask = geom.region_mask([circle]) contribute = models_gauss.select_mask(mask, margin=None, use_evaluation_region=True) assert contribute.names == ["source-1", "source-2"] inside = models_gauss.select_mask(mask, margin=None, use_evaluation_region=False) assert inside.names == ["source-1"] contribute_margin = models_gauss.select_mask(mask, margin=0.1 * u.deg, use_evaluation_region=True) assert contribute_margin.names == ["source-1", "source-2", "source-3"]
def run(self, datasets): """Estimate flux for a given energy range. Parameters ---------- datasets : list of `~gammapy.datasets.SpectrumDataset` Spectrum datasets. Returns ------- result : dict Dict with results for the flux point. """ datasets = Datasets(datasets) models = datasets.models.copy() model = self.get_scale_model(models) energy_min, energy_max = datasets.energy_ranges energy_axis = MapAxis.from_energy_edges([energy_min.min(), energy_max.max()]) with np.errstate(invalid="ignore", divide="ignore"): result = model.reference_fluxes(energy_axis=energy_axis) # convert to scalar values result = {key: value.item() for key, value in result.items()} models[self.source].spectral_model = model datasets.models = models result.update(super().run(datasets, model.norm)) return result
def lightcurve(self): """Lightcurve (`~gammapy.estimators.FluxPoints`).""" time_axis = self.data["time_axis"] tag = "Flux_History" energy_axis = MapAxis.from_energy_edges(self.energy_range) geom = RegionGeom.create(region=self.position, axes=[energy_axis, time_axis]) names = ["flux", "flux_errp", "flux_errn", "flux_ul"] maps = Maps.from_geom(geom=geom, names=names) maps["flux"].quantity = self.data[tag] maps["flux_errp"].quantity = self.data[f"Unc_{tag}"][:, 1] maps["flux_errn"].quantity = -self.data[f"Unc_{tag}"][:, 0] maps["flux_ul"].quantity = compute_flux_points_ul( maps["flux"].quantity, maps["flux_errp"].quantity ) is_ul = np.isnan(maps["flux_errn"]) maps["flux_ul"].data[~is_ul] = np.nan return FluxPoints.from_maps( maps=maps, sed_type="flux", reference_model=self.sky_model(), meta=self.flux_points_meta.copy(), )
def make_edisp_map_test(): pointing = SkyCoord(0, 0, unit="deg") energy_axis_true = MapAxis.from_energy_edges( energy_edges=[0.2, 0.7, 1.5, 2.0, 10.0] * u.TeV, name="energy_true", ) migra_axis = MapAxis(nodes=np.linspace(0.0, 3.0, 51), unit="", name="migra") offset_axis = MapAxis.from_nodes([0.0, 1.0, 2.0, 3.0] * u.deg, name="offset") edisp2d = EnergyDispersion2D.from_gauss( energy_axis_true=energy_axis_true, migra_axis=migra_axis, offset_axis=offset_axis, bias=0, sigma=0.2, ) geom = WcsGeom.create( skydir=pointing, binsz=1.0, width=5.0, axes=[migra_axis, energy_axis_true] ) aeff2d = fake_aeff2d() exposure_geom = geom.squash(axis_name="migra") exposure_map = make_map_exposure_true_energy(pointing, "1 h", aeff2d, exposure_geom) return make_edisp_map(edisp2d, pointing, geom, exposure_map)
def to_edisp_kernel(self, offset, energy_true=None, energy=None): """Detector response R(Delta E_reco, Delta E_true) Probability to reconstruct an energy in a given true energy band in a given reconstructed energy band Parameters ---------- offset : `~astropy.coordinates.Angle` Offset energy_true : `~astropy.units.Quantity`, None True energy axis energy : `~astropy.units.Quantity` Reconstructed energy axis Returns ------- edisp : `~gammapy.irf.EDispKernel` Energy dispersion matrix """ offset = Angle(offset) # TODO: expect directly MapAxis here? if energy is None: energy_axis = self.data.axes["energy_true"].copy(name="energy") else: energy_axis = MapAxis.from_energy_edges(energy) if energy_true is None: energy_axis_true = self.data.axes["energy_true"] else: energy_axis_true = MapAxis.from_energy_edges( energy_true, name="energy_true" ) data = [] for value in energy_axis_true.center: vec = self.get_response( offset=offset, energy_true=value, energy=energy_axis.edges ) data.append(vec) return EDispKernel( energy_axis=energy_axis, energy_axis_true=energy_axis_true, data=np.asarray(data), )
def to_edisp_kernel(self, offset, energy_true=None, energy=None): """Detector response R(Delta E_reco, Delta E_true) Probability to reconstruct an energy in a given true energy band in a given reconstructed energy band Parameters ---------- offset : `~astropy.coordinates.Angle` Offset energy_true : `~astropy.units.Quantity`, None True energy axis energy : `~astropy.units.Quantity` Reconstructed energy axis Returns ------- edisp : `~gammapy.irf.EDispKernel` Energy dispersion matrix """ from gammapy.makers.utils import make_edisp_kernel_map offset = Angle(offset) # TODO: expect directly MapAxis here? if energy is None: energy_axis = self.axes["energy_true"].copy(name="energy") else: energy_axis = MapAxis.from_energy_edges(energy) if energy_true is None: energy_axis_true = self.axes["energy_true"] else: energy_axis_true = MapAxis.from_energy_edges( energy_true, name="energy_true", ) pointing = SkyCoord("0d", "0d") center = pointing.directional_offset_by(position_angle=0 * u.deg, separation=offset) geom = RegionGeom.create(region=center, axes=[energy_axis, energy_axis_true]) edisp = make_edisp_kernel_map(geom=geom, edisp=self, pointing=pointing) return edisp.get_edisp_kernel()
def from_gauss(cls, e_true, migra, bias, sigma, offset, pdf_threshold=1e-6): """Create Gaussian energy dispersion matrix (`EnergyDispersion2D`). The output matrix will be Gaussian in (e_true / e_reco). The ``bias`` and ``sigma`` should be either floats or arrays of same dimension than ``e_true``. ``bias`` refers to the mean value of the ``migra`` distribution minus one, i.e. ``bias=0`` means no bias. Note that, the output matrix is flat in offset. Parameters ---------- e_true : `~astropy.units.Quantity` Bin edges of true energy axis migra : `~astropy.units.Quantity` Bin edges of migra axis bias : float or `~numpy.ndarray` Center of Gaussian energy dispersion, bias sigma : float or `~numpy.ndarray` RMS width of Gaussian energy dispersion, resolution offset : `~astropy.units.Quantity` Bin edges of offset pdf_threshold : float, optional Zero suppression threshold """ e_true = Quantity(e_true) # erf does not work with Quantities energy_axis_true = MapAxis.from_energy_edges(e_true, interp="log", name="energy_true") true2d, migra2d = np.meshgrid(energy_axis_true.center, migra) migra2d_lo = migra2d[:-1, :] migra2d_hi = migra2d[1:, :] # Analytical formula for integral of Gaussian s = np.sqrt(2) * sigma t1 = (migra2d_hi - 1 - bias) / s t2 = (migra2d_lo - 1 - bias) / s pdf = (scipy.special.erf(t1) - scipy.special.erf(t2)) / 2 data = pdf.T[:, :, np.newaxis] * np.ones(len(offset) - 1) data[data < pdf_threshold] = 0 offset_axis = MapAxis.from_edges(offset, name="offset") migra_axis = MapAxis.from_edges(migra, name="migra") return cls(energy_axis_true=energy_axis_true, migra_axis=migra_axis, offset_axis=offset_axis, data=data)
def _get_energy_axis(self, dataset): """Energy axis""" if self.energy_edges is None: energy_axis = dataset.counts.geom.axes["energy"].squash() else: energy_axis = MapAxis.from_energy_edges(self.energy_edges) return energy_axis
def test_from_parametrization(): # Log center of this is 100 GeV area_ref = 1.65469579e07 * u.cm ** 2 axis = MapAxis.from_energy_edges([80, 125] * u.GeV, name="energy_true") area = EffectiveAreaTable2D.from_parametrization(axis, "HESS") assert_allclose(area.quantity, area_ref) assert area.unit == area_ref.unit # Log center of this is 0.1, 2 TeV area_ref = [1.65469579e07, 1.46451957e09] * u.cm * u.cm axis = MapAxis.from_energy_edges([0.08, 0.125, 32] * u.TeV, name="energy_true") area = EffectiveAreaTable2D.from_parametrization(axis, "HESS") assert_allclose(area.quantity[:, 0], area_ref) assert area.unit == area_ref.unit
def from_diagonal_response(cls, energy_true, energy=None): """Create energy dispersion from a diagonal response, i.e. perfect energy resolution This creates the matrix corresponding to a perfect energy response. It contains ones where the energy_true center is inside the e_reco bin. It is a square diagonal matrix if energy_true = e_reco. This is useful in cases where code always applies an edisp, but you don't want it to do anything. Parameters ---------- energy_true, energy : `~astropy.units.Quantity` Energy edges for true and reconstructed energy axis Examples -------- If ``energy_true`` equals ``energy``, you get a diagonal matrix:: energy_true = [0.5, 1, 2, 4, 6] * u.TeV edisp = EnergyDispersion.from_diagonal_response(energy_true) edisp.plot_matrix() Example with different energy binnings:: energy_true = [0.5, 1, 2, 4, 6] * u.TeV energy = [2, 4, 6] * u.TeV edisp = EnergyDispersion.from_diagonal_response(energy_true, energy) edisp.plot_matrix() """ from .edisp_map import get_overlap_fraction if energy is None: energy = energy_true energy_axis = MapAxis.from_energy_edges(energy) energy_axis_true = MapAxis.from_energy_edges(energy_true, name="energy_true") data = get_overlap_fraction(energy_axis, energy_axis_true) return cls( energy_axis=energy_axis, energy_axis_true=energy_axis_true, data=data, )
def test_from_diagonal_response(self): energy_axis_true = MapAxis.from_energy_edges([0.5, 1, 2, 4, 6] * u.TeV, name="energy_true") energy_axis = MapAxis.from_energy_edges([2, 4, 6] * u.TeV) edisp = EDispKernel.from_diagonal_response(energy_axis_true, energy_axis) assert edisp.pdf_matrix.shape == (4, 2) expected = [[0, 0], [0, 0], [1, 0], [0, 1]] assert_equal(edisp.pdf_matrix, expected) # Test square matrix edisp = EDispKernel.from_diagonal_response(energy_axis_true) assert_allclose(edisp.axes["energy"].edges, edisp.axes["energy_true"].edges) assert edisp.axes["energy"].unit == "TeV" assert_equal(edisp.pdf_matrix[0][0], 1) assert_equal(edisp.pdf_matrix[2][0], 0) assert edisp.pdf_matrix.sum() == 4
def bkg_2d(): """A simple Background2D test case""" energy = [0.1, 10, 1000] * u.TeV energy_axis = MapAxis.from_energy_edges(energy) offset = [0, 1, 2, 3] * u.deg offset_axis = MapAxis.from_edges(offset, name="offset") data = np.zeros((2, 3)) * u.Unit("s-1 MeV-1 sr-1") data.value[1, 0] = 2 data.value[1, 1] = 4 return Background2D(energy_axis=energy_axis, offset_axis=offset_axis, data=data,)
def run(self, dataset): """Compute correlated excess, Li & Ma significance and flux maps If a model is set on the dataset the excess map estimator will compute the excess taking into account the predicted counts of the model. Parameters ---------- dataset : `~gammapy.datasets.MapDataset` or `~gammapy.datasets.MapDatasetOnOff` input dataset Returns ------- images : dict Dictionary containing result correlated maps. Keys are: * counts : correlated counts map * background : correlated background map * excess : correlated excess map * ts : TS map * sqrt_ts : sqrt(delta TS), or Li-Ma significance map * err : symmetric error map (from covariance) * flux : flux map. An exposure map must be present in the dataset to compute flux map * errn : negative error map * errp : positive error map * ul : upper limit map """ if not isinstance(dataset, MapDataset): raise ValueError( "Unsupported dataset type. Excess map is not applicable to 1D datasets." ) if self.energy_edges is None: energy_axis = dataset.counts.geom.axes["energy"] energy_edges = u.Quantity( [energy_axis.edges[0], energy_axis.edges[-1]]) else: energy_edges = self.energy_edges axis = MapAxis.from_energy_edges(energy_edges) resampled_dataset = dataset.resample_energy_axis(energy_axis=axis, name=dataset.name) if isinstance(dataset, MapDatasetOnOff): resampled_dataset.models = dataset.models else: resampled_dataset.background = dataset.npred().resample_axis( axis=axis) resampled_dataset.models = None result = self.estimate_excess_map(resampled_dataset) return result
def map_flux_estimate(): axis = MapAxis.from_energy_edges((0.1, 1.0, 10.0), unit="TeV") nmap = WcsNDMap.create(npix=5, axes=[axis]) cols = dict() cols["norm"] = nmap.copy(data=1.0) cols["norm_err"] = nmap.copy(data=0.1) cols["norm_errn"] = nmap.copy(data=0.2) cols["norm_errp"] = nmap.copy(data=0.15) cols["norm_ul"] = nmap.copy(data=2.0) return cols
def from_constant(cls, energy, value): """Create constant value effective area. Parameters ---------- energy : `~astropy.units.Quantity` Energy binning, analytic function is evaluated at log centers value : `~astropy.units.Quantity` Effective area """ value = u.Quantity(value) energy_axis_true = MapAxis.from_energy_edges(energy, name="energy_true") return cls(axes=[energy_axis_true], data=value.value, unit=value.unit)
def from_constant(cls, energy, value): """Create constant value effective area. Parameters ---------- energy : `~astropy.units.Quantity` Energy binning, analytic function is evaluated at log centers value : `~astropy.units.Quantity` Effective area """ data = np.ones((len(energy) - 1)) * u.Quantity(value) energy_axis_true = MapAxis.from_energy_edges(energy, name="energy_true") return cls(energy_axis_true=energy_axis_true, data=data)
def test_bkg_2d_wrong_units(): energy = [0.1, 10, 1000] * u.TeV energy_axis = MapAxis.from_energy_edges(energy) offset_axis = MapAxis.from_edges([0, 1, 2], unit="deg", name="offset") wrong_unit = u.cm**2 * u.s data = np.ones((energy_axis.nbin, offset_axis.nbin)) * wrong_unit bkg2d_test = Background2D(axes=[energy_axis, offset_axis]) with pytest.raises(ValueError) as error: Background2D(axes=[energy_axis, offset_axis], data=data) assert error.match( f"Error: {wrong_unit} is not an allowed unit. {bkg2d_test.tag} requires {bkg2d_test.default_unit} data quantities." )
def region_map_flux_estimate(): axis = MapAxis.from_energy_edges((0.1, 1.0, 10.0), unit="TeV") geom = RegionGeom.create("galactic;circle(0, 0, 0.1)", axes=[axis]) maps = Maps.from_geom( geom=geom, names=["norm", "norm_err", "norm_errn", "norm_errp", "norm_ul"]) maps["norm"].data = np.array([1.0, 1.0]) maps["norm_err"].data = np.array([0.1, 0.1]) maps["norm_errn"].data = np.array([0.2, 0.2]) maps["norm_errp"].data = np.array([0.15, 0.15]) maps["norm_ul"].data = np.array([2.0, 2.0]) return maps
def table_flux_estimate(): axis = MapAxis.from_energy_edges((0.1, 1.0, 10.0), unit="TeV") cols = dict() cols["norm"] = np.array([1.0, 1.0]) cols["norm_err"] = np.array([0.1, 0.1]) cols["norm_errn"] = np.array([0.2, 0.2]) cols["norm_errp"] = np.array([0.15, 0.15]) cols["norm_ul"] = np.array([2.0, 2.0]) cols["e_min"] = axis.edges[:-1] cols["e_max"] = axis.edges[1:] table = Table(cols, names=cols.keys()) return table
def test_axes_basics(): energy_axis = MapAxis.from_energy_edges([1, 3] * u.TeV) time_ref = Time('1999-01-01T00:00:00.123456789') time_axis = TimeMapAxis(edges_min=[0, 1, 3] * u.d, edges_max=[0.8, 1.9, 5.4] * u.d, reference_time=time_ref) axes = MapAxes([energy_axis, time_axis]) assert axes.shape == (1, 3) assert axes.is_unidimensional assert not axes.is_flat assert axes.primary_axis.name == "time"
def lightcurve(self, interval="1-year"): """Lightcurve (`~gammapy.estimators.FluxPoints`). Parameters ---------- interval : {'1-year', '2-month'} Time interval of the lightcurve. Default is '1-year'. Note that '2-month' is not available for all catalogue version. """ if interval == "1-year": tag = "Flux_History" time_axis = self.data["time_axis"] tag_sqrt_ts = "Sqrt_TS_History" elif interval == "2-month": tag = "Flux2_History" if tag not in self.data: raise ValueError( "Only '1-year' interval is available for this catalogue version" ) time_axis = self.data["time_axis_2"] tag_sqrt_ts = "Sqrt_TS2_History" else: raise ValueError("Time intervals available are '1-year' or '2-month'") energy_axis = MapAxis.from_energy_edges([50, 300000] * u.MeV) geom = RegionGeom.create(region=self.position, axes=[energy_axis, time_axis]) names = ["flux", "flux_errp", "flux_errn", "flux_ul", "ts"] maps = Maps.from_geom(geom=geom, names=names) maps["flux"].quantity = self.data[tag] maps["flux_errp"].quantity = self.data[f"Unc_{tag}"][:, 1] maps["flux_errn"].quantity = -self.data[f"Unc_{tag}"][:, 0] maps["flux_ul"].quantity = compute_flux_points_ul( maps["flux"].quantity, maps["flux_errp"].quantity ) maps["ts"].quantity = self.data[tag_sqrt_ts] ** 2 return FluxPoints.from_maps( maps=maps, sed_type="flux", reference_model=self.sky_model(), meta=self.flux_points.meta.copy(), )
def test_psf_3d_wrong_units(): energy_axis = MapAxis.from_energy_edges([80, 125] * u.GeV, name="energy_true") offset_axis = MapAxis.from_edges([0, 1, 2], unit="deg", name="offset") rad_axis = MapAxis.from_edges([0, 1, 2], unit="deg", name="rad") wrong_unit = u.cm**2 * u.s data = np.ones( (energy_axis.nbin, offset_axis.nbin, rad_axis.nbin)) * wrong_unit psf3d_test = PSF3D(axes=[energy_axis, offset_axis, rad_axis]) with pytest.raises(ValueError) as error: PSF3D(axes=[energy_axis, offset_axis, rad_axis], data=data) assert error.match( f"Error: {wrong_unit} is not an allowed unit. {psf3d_test.tag} requires {psf3d_test.default_unit} data quantities." )
def test_bkg_3d_wrong_units(): energy = [0.1, 10, 1000] * u.TeV energy_axis = MapAxis.from_energy_edges(energy) fov_lon = [0, 1, 2, 3] * u.deg fov_lon_axis = MapAxis.from_edges(fov_lon, name="fov_lon") fov_lat = [0, 1, 2, 3] * u.deg fov_lat_axis = MapAxis.from_edges(fov_lat, name="fov_lat") wrong_unit = u.cm**2 * u.s data = np.ones((2, 3, 3)) * wrong_unit with pytest.raises(ValueError) as error: Background3D(axes=[energy_axis, fov_lon_axis, fov_lat_axis], data=data) assert error.match( "Error: (.*) is not an allowed unit. (.*) requires (.*) data quantities." )
def bkg_3d_custom(symmetry="constant"): if symmetry == "constant": data = np.ones((2, 3, 3)) elif symmetry == "symmetric": data = np.ones((2, 3, 3)) data[:, 1, 1] *= 2 elif symmetry == "asymmetric": data = np.indices((3, 3))[1] + 1 data = np.stack(2 * [data]) else: raise ValueError(f"Unkown value for symmetry: {symmetry}") energy_axis = MapAxis.from_energy_edges([0.1, 10, 1000] * u.TeV) fov_lon_axis = MapAxis.from_edges([-3, -1, 1, 3] * u.deg, name="fov_lon") fov_lat_axis = MapAxis.from_edges([-3, -1, 1, 3] * u.deg, name="fov_lat") return Background3D(axes=[energy_axis, fov_lon_axis, fov_lat_axis], data=data, unit=u.Unit("s-1 MeV-1 sr-1"))
def bkg_3d(): """Example with simple values to test evaluate""" energy = [0.1, 10, 1000] * u.TeV energy_axis = MapAxis.from_energy_edges(energy) fov_lon = [0, 1, 2, 3] * u.deg fov_lon_axis = MapAxis.from_edges(fov_lon, name="fov_lon") fov_lat = [0, 1, 2, 3] * u.deg fov_lat_axis = MapAxis.from_edges(fov_lat, name="fov_lat") data = np.ones((2, 3, 3)) # Axis order is (energy, fov_lon, fov_lat) # data.value[1, 0, 0] = 1 data[1, 1, 1] = 100 return Background3D(axes=[energy_axis, fov_lon_axis, fov_lat_axis], data=data, unit="s-1 GeV-1 sr-1")
def __init__(self, data, reference_spectral_model): # TODO: Check data self._data = data if hasattr(self._data["norm"], "geom"): self.energy_axis = self.data["norm"].geom.axes["energy"] self._expand_slice = (slice(None), np.newaxis, np.newaxis) else: # Here we assume there is only one row per energy e_edges = self._data["e_min"].quantity e_edges = e_edges.insert(len(self._data), self._data["e_max"].quantity[-1]) self.energy_axis = MapAxis.from_energy_edges(e_edges) self._expand_slice = slice(None) # Note that here we could use the specification from dnde_ref to build piecewise PL # But does it work beyond min and max centers? self._reference_spectral_model = reference_spectral_model