def flux_points(self): """Flux points (`~gammapy.spectrum.FluxPoints`).""" table = Table() table.meta["SED_TYPE"] = "flux" table["e_min"] = self._ebounds[:-1] table["e_max"] = self._ebounds[1:] flux = self.data["Flux_Band"] flux_err = self.data["Unc_Flux_Band"] e2dnde = self.data["nuFnu"] table["flux"] = flux table["flux_errn"] = np.abs(flux_err[:, 0]) table["flux_errp"] = flux_err[:, 1] table["e2dnde"] = e2dnde table["e2dnde_errn"] = np.abs(e2dnde * flux_err[:, 0] / flux) table["e2dnde_errp"] = e2dnde * flux_err[:, 1] / flux is_ul = np.isnan(table["flux_errn"]) table["is_ul"] = is_ul # handle upper limits table["flux_ul"] = np.nan * flux_err.unit flux_ul = compute_flux_points_ul(table["flux"], table["flux_errp"]) table["flux_ul"][is_ul] = flux_ul[is_ul] table["e2dnde_ul"] = np.nan * e2dnde.unit e2dnde_ul = compute_flux_points_ul(table["e2dnde"], table["e2dnde_errp"]) table["e2dnde_ul"][is_ul] = e2dnde_ul[is_ul] # Square root of test statistic table["sqrt_ts"] = self.data["Sqrt_TS_Band"] return FluxPoints(table)
def from_dict(cls, data, **kwargs): """Create flux point dataset from dict. Parameters ---------- data : dict Dict containing data to create dataset from. Returns ------- dataset : `FluxPointsDataset` Flux point datasets. """ from gammapy.estimators import FluxPoints filename = make_path(data["filename"]) table = Table.read(filename) mask_fit = table["mask_fit"].data.astype("bool") mask_safe = table["mask_safe"].data.astype("bool") table.remove_columns(["mask_fit", "mask_safe"]) return cls( name=data["name"], data=FluxPoints(table), mask_fit=mask_fit, mask_safe=mask_safe, )
def from_dict(cls, data, models): """Create flux point dataset from dict. Parameters ---------- data : dict Dict containing data to create dataset from. models : list of `SkyModel` List of model components. Returns ------- dataset : `FluxPointsDataset` Flux point datasets. """ from gammapy.estimators import FluxPoints table = Table.read(data["filename"]) mask_fit = table["mask_fit"].data.astype("bool") mask_safe = table["mask_safe"].data.astype("bool") table.remove_columns(["mask_fit", "mask_safe"]) return cls( models=models, name=data["name"], data=FluxPoints(table), mask_fit=mask_fit, mask_safe=mask_safe, )
def flux_points_dnde(model): e_ref = [np.sqrt(10), np.sqrt(10 * 100)] * u.TeV table = Table() table.meta["SED_TYPE"] = "dnde" table["e_ref"] = e_ref table["dnde"] = model(e_ref) return FluxPoints(table)
def flux_points(self): """Differential flux points (`~gammapy.estimators.FluxPoints`).""" d = self.data table = Table() table.meta["SED_TYPE"] = "dnde" self._add_source_meta(table) valid = np.isfinite(d["sed_e_ref"].value) if valid.sum() == 0: return None table["e_ref"] = d["sed_e_ref"] table["e_min"] = d["sed_e_min"] table["e_max"] = d["sed_e_max"] table["dnde"] = d["sed_dnde"] table["dnde_err"] = d["sed_dnde_err"] table["dnde_errn"] = d["sed_dnde_errn"] table["dnde_errp"] = d["sed_dnde_errp"] table["dnde_ul"] = d["sed_dnde_ul"] # Only keep rows that actually contain information table = table[valid] # Only keep columns that actually contain information def _del_nan_col(table, colname): if np.isfinite(table[colname]).sum() == 0: del table[colname] for colname in table.colnames: _del_nan_col(table, colname) return FluxPoints(table)
def flux_points_e2dnde(model): e_ref = [np.sqrt(10), np.sqrt(10 * 100)] * u.TeV table = Table() table.meta["SED_TYPE"] = "e2dnde" table["e_ref"] = e_ref table["e2dnde"] = (model(e_ref) * e_ref**2).to("erg cm-2 s-1") return FluxPoints(table)
def test_compute_flux_points_dnde_exp(method): """ Tests against analytical result or result from gammapy.spectrum.powerlaw. """ model = ExpTestModel() e_min = [1.0, 10.0] * u.TeV e_max = [10.0, 100.0] * u.TeV table = Table() table.meta["SED_TYPE"] = "flux" table["e_min"] = e_min table["e_max"] = e_max flux = model.integral(e_min, e_max) table["flux"] = flux if method == "log_center": e_ref = np.sqrt(e_min * e_max) elif method == "table": e_ref = [2.0, 20.0] * u.TeV table["e_ref"] = e_ref elif method == "lafferty": e_ref = FluxPoints._e_ref_lafferty(model, e_min, e_max) result = FluxPoints(table).to_sed_type("dnde", model=model, method=method) # Test energy actual = result.e_ref assert_quantity_allclose(actual, e_ref, rtol=1e-8) # Test flux actual = result.table["dnde"].quantity desired = model(e_ref) assert_quantity_allclose(actual, desired, rtol=1e-8)
def flux_points(self): """Flux points (`~gammapy.estimators.FluxPoints`).""" table = Table() table.meta["SED_TYPE"] = "flux" table["e_min"] = self._energy_edges[:-1] table["e_max"] = self._energy_edges[1:] flux = self._get_flux_values("Flux") flux_err = self._get_flux_values("Unc_Flux") table["flux"] = flux table["flux_errn"] = np.abs(flux_err[:, 0]) table["flux_errp"] = flux_err[:, 1] nuFnu = self._get_flux_values("nuFnu", "erg cm-2 s-1") table["e2dnde"] = nuFnu table["e2dnde_errn"] = np.abs(nuFnu * flux_err[:, 0] / flux) table["e2dnde_errp"] = nuFnu * flux_err[:, 1] / flux is_ul = np.isnan(table["flux_errn"]) table["is_ul"] = is_ul # handle upper limits table["flux_ul"] = np.nan * flux_err.unit flux_ul = compute_flux_points_ul(table["flux"], table["flux_errp"]) table["flux_ul"][is_ul] = flux_ul[is_ul] # handle upper limits table["e2dnde_ul"] = np.nan * nuFnu.unit e2dnde_ul = compute_flux_points_ul(table["e2dnde"], table["e2dnde_errp"]) table["e2dnde_ul"][is_ul] = e2dnde_ul[is_ul] # Square root of test statistic table["sqrt_TS"] = [self.data["Sqrt_TS" + _] for _ in self._energy_edges_suffix] return FluxPoints(table)
def get_flux_points(self, position=None): """Extract flux point at a given position. Parameters --------- position : `~astropy.coordinates.SkyCoord` Position where the flux points are extracted. Returns ------- flux_points : `~gammapy.estimators.FluxPoints` Flux points object """ from gammapy.estimators import FluxPoints if position is None: position = self.geom.center_skydir data = {} for name in self._data: m = getattr(self, name) data[name] = m.to_region_nd_map(region=position, method="nearest") return FluxPoints(data, reference_model=self.reference_model, meta=self.meta.copy(), gti=self.gti)
def flux_points_flux(model): e_min = [1, 10] * u.TeV e_max = [10, 100] * u.TeV table = Table() table.meta["SED_TYPE"] = "flux" table["e_min"] = e_min table["e_max"] = e_max table["flux"] = model.integral(e_min, e_max) return FluxPoints(table)
def flux_points(self): """Flux points (`~gammapy.spectrum.FluxPoints`).""" table = Table() table.meta["SED_TYPE"] = "dnde" mask = ~np.isnan(self.data["Flux_Points_Energy"]) table["e_ref"] = self.data["Flux_Points_Energy"][mask] table["e_min"] = self.data["Flux_Points_Energy_Min"][mask] table["e_max"] = self.data["Flux_Points_Energy_Max"][mask] table["dnde"] = self.data["Flux_Points_Flux"][mask] table["dnde_errn"] = self.data["Flux_Points_Flux_Err_Lo"][mask] table["dnde_errp"] = self.data["Flux_Points_Flux_Err_Hi"][mask] table["dnde_ul"] = self.data["Flux_Points_Flux_UL"][mask] table["is_ul"] = self.data["Flux_Points_Flux_Is_UL"][mask].astype("bool") return FluxPoints(table)
def flux_points(self): """Integral flux points (`~gammapy.spectrum.FluxPoints`).""" table = Table() table.meta["SED_TYPE"] = "flux" table["e_min"] = self._ebounds[:-1] table["e_max"] = self._ebounds[1:] table["flux"] = self._get_flux_values("Flux") flux_err = self._get_flux_values("Unc_Flux") table["flux_errn"] = np.abs(flux_err[:, 0]) table["flux_errp"] = flux_err[:, 1] # handle upper limits is_ul = np.isnan(table["flux_errn"]) table["is_ul"] = is_ul table["flux_ul"] = np.nan * flux_err.unit flux_ul = compute_flux_points_ul(table["flux"], table["flux_errp"]) table["flux_ul"][is_ul] = flux_ul[is_ul] return FluxPoints(table)
def perform_casc_fit(config, geom, ps_model, llh, dataset, on_radius=Angle("0.07 deg"), B=1e-16, plot=False): """ Perform the combined cascade fit Parameters ---------- config: dict Configuration dictionary B: float B-field strength geom:`~gammapy.maps.WcsGeom` object geometry of the dataset on_radius: `~astropy.coordinates.Angle` object Radius of ON region of IACT analysis ps_model: `gammapy.model.modeling.models` object Assumed point source model llh: `simCRpropa.fermiinterp.LogLikeCubeFermi` Interpolated Fermi likelihood cube dataset: `~gammapy.datasets.SpectrumOnOffDataset` The IACT data set plot: bool If true, generate diagnostic plots Returns ------- Tuple containing -2 * ln (likelihood values) for fit on Fermi-LAT data only and combined fit """ logging.info(f" ------ B = {B:.2e} ------- ") # Load the cascade # generate a casc map with low # spatial resolution to speed up calculations ebl = EBLAbsorptionNormSpectralModel.read( filename=config['global']['gammapy_ebl_file'], redshift=config[src]['z_src']) casc_file = config['global']['casc_file'].replace( "*", "{0:.3f}".format(config[src]['z_casc']), 1) casc_file = casc_file.replace("*", "{0:.2e}".format(B)) casc_1d = CascMap.gen_from_hd5f( casc_file, skycoord=geom.center_skydir, width=2 * np.round(on_radius.value / geom.pixel_scales[0].value, 0) * geom.pixel_scales[0].value, binsz=geom.pixel_scales[0].value, ebins=21, id_detection=22, smooth_kwargs={ 'kernel': Gaussian2DKernel, 'threshold': 2., 'steps': 50 }) # Initialize the cascade model # use the best fit model for the intrinsic spectrum logging.info("Initializing cascade model ...") casc_spec = CascadeSpectralModel(casc_1d, ps_model.spectral_model.model1.copy(), ebl, on_region, rotation=config['global']['rotation'] * u.deg, tmax=config['global']['tmax'] * u.yr, bias=0. * u.dimensionless_unscaled, energy_unit_spectral_model="TeV", use_gammapy_interp=False) # Plot the total model if plot: energy_power = 2 fig = plt.figure(dpi=150) ax = fig.add_subplot(111) e_range = [1e-3, 10.] * u.TeV casc_spec.add_primary = True casc_spec.plot(ax=ax, energy_range=e_range, energy_power=energy_power) ps_model.spectral_model.plot(ax=ax, energy_range=e_range, energy_power=energy_power) casc_spec.add_primary = False casc_spec.plot(ax=ax, energy_range=e_range, energy_power=energy_power) casc_spec.add_primary = True plt.ylim(1e-15, 3e-12) ax.grid() fig.savefig(f"plots/{src:s}_B{B}_casc+ps_models.png") plt.close("all") # Perform the fit with the cascade casc_model = SkyModel(spectral_model=casc_spec, name='casc') # limit the parameter ranges and change the tolerance # index casc_model.parameters['index'].min = llh.params['Index'].min() casc_model.parameters['index'].max = llh.params['Index'].max() # amplitude casc_model.parameters[ 'amplitude'].min = casc_model.parameters['amplitude'].value / 10. casc_model.parameters[ 'amplitude'].max = casc_model.parameters['amplitude'].value * 10. # cutoff casc_model.parameters['lambda_'].min = 1. / llh.params['Cutoff'].max() casc_model.parameters['lambda_'].max = 1. / llh.params['Cutoff'].min() casc_model.parameters['lambda_'].frozen = config['global']['fix_cutoff'] # bias between Fermi and HESS casc_model.parameters['bias'].value = 0. casc_model.parameters['bias'].frozen = config['global']['fix_bias'] logging.info(f"Initial parameters:\n{casc_model.parameters.to_table()}") # interpolate the fermi likelihood for the right B field # TODO: changes necessary if more than one coherence length or theta jet used idb = np.where(llh.params["B"] == B)[0][0] llh.interp_llh( llh.params["B"][idb], # choose a B field - should match casc file llh.params["maxTurbScale"] [0], # choose a turb scale - should match casc file llh.params["th_jet"] [0], # choose a jet opening angle - should match casc file method='linear', ) # plot the interpolation if plot: fig = plt.figure(dpi=150) ax = fig.add_subplot(111) # plot a likelihood surface # choose a slice in cut off energy cut_id = 0 # build a grid of indices and norms ii, nn = np.meshgrid(llh._params["Index"], llh.log_norm_array, indexing='ij') # plot the log likehood grid dlogl = 2. * (llh._llh_grid[cut_id] - llh._llh_grid[cut_id].max()) vmin = -100. # check if most points have higher dlogl, # and if so, adjust color scaling if dlogl[dlogl > vmin].size < 10: vmin = 3. * dlogl[dlogl < 0].max() im = ax.pcolormesh(ii, nn, dlogl, cmap=cmap_name, vmin=vmin, vmax=0) ax.annotate("$E_\mathrm{{cut}} = {0:.0f}$TeV".format( llh.params["Cutoff"][cut_id]), xy=(0.05, 0.95), xycoords='axes fraction', color='w', va='top', fontsize='x-large') plt.colorbar(im, label='$\ln\mathcal{L}$') ax.tick_params(direction='out') plt.xlabel("$\Gamma$") plt.ylabel("$\log_{10}(N)$") plt.grid(color='0.7', ls=':') plt.subplots_adjust(bottom=0.2, left=0.2) fig.savefig( "plots/{1:s}_lnl_fermi_grid_Ecut{0:.0f}TeV_B{2}.png".format( llh.params["Cutoff"][cut_id], src, B)) plt.close("all") # initialize prior data set logging.info("Initializing data set with priors") prior_stack = PriorSpectrumDatasetOnOff.from_spectrum_dataset_fermi_interp( dataset, llh_fermi_interp=None) # add the interpolator to the data set prior_stack.llh_fermi_interp = llh.interp # add the reference energy at which interpolation was performed (in MeV) prior_stack.ref_energy = src_dict['spectral_pars']['Scale']['value'] prior_stack.models = casc_model logging.info("Performing combined fit") fit_casc = Fit([prior_stack]) fit_result_casc = fit_casc.run(optimize_opts=dict( print_level=2, tol=10., migrad_opts=dict(ncall=1000))) logging.info(f"Parameters after fit:\n{casc_model.parameters.to_table()}") logging.info(f"Total stat after fit {fit_result_casc.total_stat}") logging.info(f"Fermi logl after fit {prior_stack.llh_fermi}") # plot the flux points if plot: fig = plt.figure(dpi=150) ax = flux_points.plot(energy_power=2., label='data', marker='o') if not casc_model.parameters['bias'].value == 0.: fp = flux_points.table.copy() fp['e_ref'] *= 1. + casc_model.parameters['bias'].value fp['e_min'] *= 1. + casc_model.parameters['bias'].value fp['e_max'] *= 1. + casc_model.parameters['bias'].value fp_rescale = FluxPoints(fp) fp_rescale.plot(ax=ax, energy_power=2., label='data rescaled', marker='o', color='green') # plot the final model e_range = [1e-3, 10.] * u.TeV # total model casc_model.spectral_model.add_primary = True casc_model.spectral_model.plot(ax=ax, energy_range=e_range, energy_power=2, label='Total', color='k') casc_model.spectral_model.plot_error(ax=ax, energy_range=e_range, energy_power=2) # point source obs_model = casc_model.spectral_model.intrinsic_spectral_model * casc_model.spectral_model.ebl # cascade casc_model.spectral_model.add_primary = False casc_model.spectral_model.plot(ax=ax, energy_range=e_range, energy_power=2, label='Cascade', color='k', ls='-.') casc_model.spectral_model.add_primary = True if casc_model.parameters['bias'].value == 0.: obs_model.plot(ax=ax, energy_range=e_range, energy_power=2, label='Point source', color='k', ls='--') else: casc_model.parameters['bias'].value = 0. obs_model.plot(ax=ax, energy_range=e_range, energy_power=2, label='Point source', color='green', ls='--') casc_model.spectral_model.plot(ax=ax, energy_range=e_range, energy_power=2, label='Total, bias = 0', color='green', ls=':') # cascade casc_model.spectral_model.add_primary = False casc_model.spectral_model.plot(ax=ax, energy_range=e_range, energy_power=2, label='Cascade', color='green', ls='-.') casc_model.spectral_model.add_primary = True pass # fermi SED SEDPlotter.plot_sed( sed, ax=ax, ms=6., marker='.', color='C1', mec='C1', alpha=1., noline=False, band_alpha=0.2, line_alpha=0.5, band_linestyle='-', label="Fermi", flux_unit='TeV cm-2 s-1', energy_unit='TeV', print_name=False, # apply bias to fermi data, in fit it's the other way around #bias=casc_model.parameters['bias'].value ) ax.legend(loc='lower center') plt.ylim(3e-15, 2e-10) plt.xlim(1e-3, 2e1) fig.savefig(f"plots/final_fits_{src:s}_b{B}.png") plt.close("all") # plot a likelihood surface for best cut value if plot: par_amp = prior_stack.models['casc'].parameters['amplitude'] amp = np.logspace( np.log10(par_amp.value) - 1., np.log10(par_amp.value) + .5, 100) amp *= par_amp.unit par_idx = prior_stack.models['casc'].parameters['index'] idx = np.linspace(par_idx.value - .5, par_idx.value + 1., 120) idx *= par_idx.unit ii, aa = np.meshgrid(idx, amp, indexing='ij') c_stat = prior_stack.get_llh_fermi(amplitude=aa.reshape(-1), index=ii.reshape(-1), set_attribute=False).reshape( aa.shape) #ii, nn = np.meshgrid(idx, llh.log_norm_array, indexing='ij') #c_stat = np.zeros((idx.shape[0], amp.shape[0])) #for i, iidx in enumerate(idx): #for j, a in enumerate(amp): #c_stat[i,j] = prior_stack.get_llh_fermi(amplitude=a, index=iidx, set_attribute=False) #c_stat = np.zeros((idx.shape[0], llh.log_norm_array.shape[0])) #for i, iidx in enumerate(idx.value): # for j, log_norm in enumerate(llh.log_norm_array): # c_stat[i,j] = prior_stack.get_llh_fermi(amplitude=10.**log_norm * u.Unit('MeV-1 s-1 cm-2'), # index=iidx * u.dimensionless_unscaled, # set_attribute=False, # reference=prior_stack._ref_energy * u.MeV) d_cstat = 2. * (c_stat - c_stat.min()) fig = plt.figure(dpi=150) ax = fig.add_subplot(111) vmin = 0. vmax = 300. im = ax.pcolormesh(ii.value, np.log10(aa.value), d_cstat, cmap=cmap_name, vmin=vmin, vmax=vmax) #im = ax.pcolormesh(ii, nn, d_cstat, cmap=cmap_name, vmin=vmin, vmax=vmax) ax.annotate("$E_\mathrm{{cut}} = {0:.2f}$TeV, $B={1:.2e}$G".format( 1. / prior_stack.models['casc'].parameters['lambda_'].value, B), xy=(0.05, 0.95), xycoords='axes fraction', color='k', va='top', fontsize='x-large') plt.colorbar(im, label='$-2\Delta\ln\mathcal{L}$') ax.plot(par_idx.value, np.log10(par_amp.value), ms=10., marker='*', mec='k') ax.tick_params(direction='out') plt.xlabel("$\Gamma$") plt.ylabel( "$\log_{10}(N_\mathrm{H.E.S.S.}) = \log_{10}((E_{0,{Fermi}} / E_{0,\mathrm{H.E.S.S.}})^{-\Gamma}N_{Fermi})$", fontsize='medium') plt.grid(color='0.7', ls=':') plt.subplots_adjust(bottom=0.2, left=0.2) fig.savefig("plots/{0:s}_lnl_fermi_interp_B{1}.png".format(src, B)) plt.close("all") return prior_stack, prior_stack.llh_fermi, fit_result_casc.total_stat