def read(cls, filename): """Read from a FITS file. Parameters ---------- filename : `str` File containing the IRFs """ filename = str(make_path(filename)) hdu_list = fits.open(filename) aeff = EffectiveAreaTable2D.read(filename, hdu='EFFECTIVE AREA') bkg = Background3D.read(filename, hdu='BACKGROUND') edisp = EnergyDispersion2D.read(filename, hdu='ENERGY DISPERSION') psf = EnergyDependentMultiGaussPSF.read(filename, hdu='POINT SPREAD FUNCTION') if 'SENSITIVITY' in hdu_list: sensi = SensitivityTable.read(filename, hdu='SENSITIVITY') else: sensi = None return cls( aeff=aeff, bkg=bkg, edisp=edisp, psf=psf, ref_sensi=sensi, )
def background_3d_plot(irf_file_path, ax=None, hdu="BACKGROUND"): if not ax: fig = plt.figure(figsize=(10, 7), ) ax = fig.add_subplot(111, projection='3d') bkg3d = Background3D.read(irf_file_path, hdu=hdu) energy_reco = np.logspace(-2, 2, 20) * u.TeV offset_centers = _bin_center(bkg3d.data.axes[1].bins) data = bkg3d.data.data.sum(axis=0).value data = _log_data(data) vmin, vmax = -4, 1.5 data = np.clip(data, vmin, vmax) X, Y = np.meshgrid(offset_centers, offset_centers) X, Y, Z, = X.ravel(), Y.ravel(), gaussian_filter(data, sigma=0.8).ravel() surf = ax.plot_trisurf(X, Y, Z, cmap='viridis', antialiased=True) ax.zaxis.set_major_locator(mticker.FixedLocator([ -4, -3, -2, -1, 0, 1, ])) ax.zaxis.set_major_formatter(mticker.FuncFormatter(_log_tick_formatter)) ax.set_zlim([-4, 1]) return ax
def get_irfs(): filename = '$GAMMAPY_EXTRA/datasets/cta-1dc/caldb/data/cta//1dc/bcf/South_z20_50h/irf_file.fits' psf = EnergyDependentMultiGaussPSF.read(filename, hdu='POINT SPREAD FUNCTION') aeff = EffectiveAreaTable2D.read(filename, hdu='EFFECTIVE AREA') edisp = EnergyDispersion2D.read(filename, hdu='ENERGY DISPERSION') bkg = Background3D.read(filename, hdu='BACKGROUND') return dict(psf=psf, aeff=aeff, edisp=edisp, bkg=bkg)
def test_background_3d_read_gadf(): filename = "$GAMMAPY_DATA/tests/irf/bkg_3d_full_example.fits" bkg = Background3D.read(filename) data = bkg.quantity assert bkg.axes.names == ["energy", "fov_lon", "fov_lat"] assert data.shape == (20, 15, 15) assert data.unit == "s-1 MeV-1 sr-1"
def read(cls, filename): """Read from a FITS file. Parameters ---------- filename : `str` File containing the IRFs """ filename = str(make_path(filename)) hdu_list = fits.open(filename) aeff = EffectiveAreaTable2D.read(filename, hdu='EFFECTIVE AREA') bkg = Background3D.read(filename, hdu='BACKGROUND') edisp = EnergyDispersion2D.read(filename, hdu='ENERGY DISPERSION') psf = EnergyDependentMultiGaussPSF.read(filename, hdu='POINT SPREAD FUNCTION') if 'SENSITIVITY' in hdu_list: sensi = SensitivityTable.read(filename, hdu='SENSITIVITY') else: sensi = None return cls( aeff=aeff, bkg=bkg, edisp=edisp, psf=psf, ref_sensi=sensi, )
def get_irfs(config, filename): '''Get IRFs from file. Parameters ---------- config : `dict` Configuration dictionary. filename : fits file IRFs file Returns ------- irfs : `dict` IRFs dictionary. ''' offset = Angle(config['selection']['offset_fov'] * u.deg) psf_fov = EnergyDependentMultiGaussPSF.read(filename, hdu='POINT SPREAD FUNCTION') psf = psf_fov.to_energy_dependent_table_psf(theta=offset) print(' psf', psf) aeff = EffectiveAreaTable2D.read(filename, hdu='EFFECTIVE AREA') edisp_fov = EnergyDispersion2D.read(filename, hdu='ENERGY DISPERSION') table = fits.open('irf_file.fits')['BACKGROUND'] table.columns.change_name(str('BGD'), str('Bgd')) table.header['TUNIT7'] = '1 / (MeV s sr)' bkg = Background3D.read(filename, hdu='BACKGROUND') irfs = dict(psf=psf, aeff=aeff, edisp=edisp_fov, bkg=bkg, offset=offset) return irfs
def check_bkg(label): irf_file = '1dc/1dc/caldb/data/cta/1dc/bcf/' + label + '/irf_file.fits' log.info(f'Reading {irf_file}') plt.clf() bkg = Background3D.read(irf_file, hdu='BACKGROUND') table = bkg.data.data raise NotImplementedError # Columns: # BGD float32 (21, 36, 36) 1/s/MeV/sr # ENERG_LO float32 (21,) TeV # DETX_LO float32 (36,) deg # DETY_LO float32 (36,) deg for idx_detx in [18, 21, 22, 23, 24, 25, 26, 27]: detx = table['DETX_LO'].data.squeeze()[idx_detx] dety = table['DETY_LO'].data.squeeze()[18] energy = table['ENERG_LO'].data.squeeze() bkg = table['BGD'].data.squeeze()[:, idx_detx, 18] txt = f'detx={detx:.1f}, dety={dety:.1f}' plt.plot(energy, bkg, label=txt) plt.legend(loc='lower left') plt.xlabel('Energy (TeV)') plt.xlim(0.01, 1) plt.ylim(1e-6, 1) plt.ylabel('Background rate (s-1 MeV-1 sr-1)') plt.loglog() filename = 'checks/irfs/' + label + '_bkg.png' log.info(f'Writing {filename}') plt.savefig(filename)
def test_background_3D_read(): filename = ( "$GAMMAPY_DATA/cta-1dc/caldb/data/cta/1dc/bcf/South_z20_50h/irf_file.fits" ) bkg = Background3D.read(filename) data = bkg.data.data assert data.shape == (21, 36, 36) assert data.unit == "s-1 MeV-1 sr-1"
def setup(self): self.energy_lo = np.logspace(0, 1, 10)[:-1] * u.TeV self.energy_hi = np.logspace(0, 1, 10)[1:] * u.TeV self.energy_axis_true = MapAxis.from_energy_bounds("1 TeV", "10 TeV", nbin=9, name="energy_true") self.offset_lo = np.linspace(0, 1, 4)[:-1] * u.deg self.offset_hi = np.linspace(0, 1, 4)[1:] * u.deg self.offset_axis = MapAxis.from_bounds(0, 1, nbin=3, unit="deg", name="offset", node_type="edges") self.migra_lo = np.linspace(0, 3, 4)[:-1] self.migra_hi = np.linspace(0, 3, 4)[1:] self.migra_axis = MapAxis.from_bounds(0, 3, nbin=3, name="migra", node_type="edges") self.fov_lon_lo = np.linspace(-6, 6, 11)[:-1] * u.deg self.fov_lon_hi = np.linspace(-6, 6, 11)[1:] * u.deg self.fov_lon_axis = MapAxis.from_bounds(-6, 6, nbin=10, name="fov_lon") self.fov_lat_lo = np.linspace(-6, 6, 11)[:-1] * u.deg self.fov_lat_hi = np.linspace(-6, 6, 11)[1:] * u.deg self.fov_lat_axis = MapAxis.from_bounds(-6, 6, nbin=10, name="fov_lat") self.aeff_data = np.random.rand(9, 3) * u.cm * u.cm self.edisp_data = np.random.rand(9, 3, 3) self.bkg_data = np.random.rand(9, 10, 10) / u.MeV / u.s / u.sr self.aeff = EffectiveAreaTable2D( axes=[self.energy_axis_true, self.offset_axis], data=self.aeff_data.value, unit=self.aeff_data.unit, ) self.edisp = EnergyDispersion2D( axes=[ self.energy_axis_true, self.migra_axis, self.offset_axis, ], data=self.edisp_data, ) axes = [ self.energy_axis_true.copy(name="energy"), self.fov_lon_axis, self.fov_lat_axis, ] self.bkg = Background3D(axes=axes, data=self.bkg_data.value, unit=self.bkg_data.unit)
def get_irfs(): """Load CTA IRFs""" filename = "$GAMMAPY_DATA/cta-1dc/caldb/data/cta/1dc/bcf/South_z20_50h/irf_file.fits" psf = EnergyDependentMultiGaussPSF.read(filename, hdu="POINT SPREAD FUNCTION") aeff = EffectiveAreaTable2D.read(filename, hdu="EFFECTIVE AREA") edisp = EnergyDispersion2D.read(filename, hdu="ENERGY DISPERSION") bkg = Background3D.read(filename, hdu="BACKGROUND") return dict(psf=psf, aeff=aeff, edisp=edisp, bkg=bkg)
def test_background_3d_read(): filename = ( "$GAMMAPY_DATA/cta-1dc/caldb/data/cta/1dc/bcf/South_z20_50h/irf_file.fits" ) bkg = Background3D.read(filename) data = bkg.quantity assert bkg.axes.names == ["energy", "fov_lon", "fov_lat"] assert data.shape == (21, 36, 36) assert data.unit == "s-1 MeV-1 sr-1"
def load(self): """Load HDU as appropriate class. TODO: this should probably go via an extensible registry. """ hdu_class = self.hdu_class filename = self.path() hdu = self.hdu_name if hdu_class == "events": from gammapy.data import EventList return EventList.read(filename, hdu=hdu) elif hdu_class == "gti": from gammapy.data import GTI return GTI.read(filename, hdu=hdu) elif hdu_class == "aeff_2d": from gammapy.irf import EffectiveAreaTable2D return EffectiveAreaTable2D.read(filename, hdu=hdu) elif hdu_class == "edisp_2d": from gammapy.irf import EnergyDispersion2D return EnergyDispersion2D.read(filename, hdu=hdu) elif hdu_class == "psf_table": from gammapy.irf import PSF3D return PSF3D.read(filename, hdu=hdu) elif hdu_class == "psf_3gauss": from gammapy.irf import EnergyDependentMultiGaussPSF return EnergyDependentMultiGaussPSF.read(filename, hdu=hdu) elif hdu_class == "psf_king": from gammapy.irf import PSFKing return PSFKing.read(filename, hdu=hdu) elif hdu_class == "bkg_2d": from gammapy.irf import Background2D return Background2D.read(filename, hdu=hdu) elif hdu_class == "bkg_3d": from gammapy.irf import Background3D return Background3D.read(filename, hdu=hdu) else: raise ValueError(f"Invalid hdu_class: {hdu_class}")
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 test_writeread(self, tmp_path): path = tmp_path / "tmp.fits" fits.HDUList([ fits.PrimaryHDU(), self.aeff.to_table_hdu(), self.edisp.to_table_hdu(), self.bkg.to_table_hdu(), ]).writeto(path) read_aeff = EffectiveAreaTable2D.read(path, hdu="EFFECTIVE AREA") assert_allclose(read_aeff.data.data, self.aeff_data) read_edisp = EnergyDispersion2D.read(path, hdu="ENERGY DISPERSION") assert_allclose(read_edisp.data.data, self.edisp_data) read_bkg = Background3D.read(path, hdu="BACKGROUND") assert_allclose(read_bkg.data.data, self.bkg_data)
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 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 test_background_3d_read_write(tmp_path, bkg_3d): bkg_3d.to_fits().writeto(tmp_path / "bkg3d.fits") bkg_3d_2 = Background3D.read(tmp_path / "bkg3d.fits") axis = bkg_3d_2.data.axis("energy") assert axis.nbin == 2 assert axis.unit == "TeV" axis = bkg_3d_2.data.axis("fov_lon") assert axis.nbin == 3 assert axis.unit == "deg" axis = bkg_3d_2.data.axis("fov_lat") assert axis.nbin == 3 assert axis.unit == "deg" data = bkg_3d_2.data.data assert data.shape == (2, 3, 3) assert data.unit == "s-1 MeV-1 sr-1"
def bkg_3d(): """Example with simple values to test evaluate""" energy = [0.1, 10, 1000] * u.TeV fov_lon = [0, 1, 2, 3] * u.deg fov_lat = [0, 1, 2, 3] * u.deg data = np.ones((2, 3, 3)) * u.Unit("s-1 MeV-1 sr-1") # Axis order is (energy, fov_lon, fov_lat) # data.value[1, 0, 0] = 1 data.value[1, 1, 1] = 100 return Background3D( energy_lo=energy[:-1], energy_hi=energy[1:], fov_lon_lo=fov_lon[:-1], fov_lon_hi=fov_lon[1:], fov_lat_lo=fov_lat[:-1], fov_lat_hi=fov_lat[1:], data=data, )
def test_background_3d_read_write(tmp_path, bkg_3d): bkg_3d.to_table_hdu().writeto(tmp_path / "bkg3d.fits") bkg_3d_2 = Background3D.read(tmp_path / "bkg3d.fits") axis = bkg_3d_2.axes["energy"] assert axis.nbin == 2 assert axis.unit == "TeV" axis = bkg_3d_2.axes["fov_lon"] assert axis.nbin == 3 assert axis.unit == "deg" axis = bkg_3d_2.axes["fov_lat"] assert axis.nbin == 3 assert axis.unit == "deg" data = bkg_3d_2.quantity assert data.shape == (2, 3, 3) assert data.unit == "s-1 GeV-1 sr-1"
def test_writeread(self, tmpdir): filename = str(tmpdir / "testirf.fits") fits.HDUList([ fits.PrimaryHDU(), self.aeff.to_fits(), self.edisp.to_fits(), self.bkg.to_fits(), ]).writeto(filename) read_aeff = EffectiveAreaTable2D.read(filename=filename, hdu="EFFECTIVE AREA") assert_allclose(read_aeff.data.data, self.aeff_data) read_edisp = EnergyDispersion2D.read(filename=filename, hdu="ENERGY DISPERSION") assert_allclose(read_edisp.data.data, self.edisp_data) read_bkg = Background3D.read(filename=filename, hdu="BACKGROUND") assert_allclose(read_bkg.data.data, self.bkg_data)
def setup(self): self.energy_lo = np.logspace(0, 1, 11)[:-1] * u.TeV self.energy_hi = np.logspace(0, 1, 11)[1:] * u.TeV self.offset_lo = np.linspace(0, 1, 4)[:-1] * u.deg self.offset_hi = np.linspace(0, 1, 4)[1:] * u.deg self.migra_lo = np.linspace(0, 3, 4)[:-1] self.migra_hi = np.linspace(0, 3, 4)[1:] self.fov_lon_lo = np.linspace(-6, 6, 11)[:-1] * u.deg self.fov_lon_hi = np.linspace(-6, 6, 11)[1:] * u.deg self.fov_lat_lo = np.linspace(-6, 6, 11)[:-1] * u.deg self.fov_lat_hi = np.linspace(-6, 6, 11)[1:] * u.deg self.aeff_data = np.random.rand(10, 3) * u.cm * u.cm self.edisp_data = np.random.rand(10, 3, 3) self.bkg_data = np.random.rand(10, 10, 10) / u.MeV / u.s / u.sr self.aeff = EffectiveAreaTable2D( energy_lo=self.energy_lo, energy_hi=self.energy_hi, offset_lo=self.offset_lo, offset_hi=self.offset_hi, data=self.aeff_data, ) self.edisp = EnergyDispersion2D( e_true_lo=self.energy_lo, e_true_hi=self.energy_hi, migra_lo=self.migra_lo, migra_hi=self.migra_hi, offset_lo=self.offset_lo, offset_hi=self.offset_hi, data=self.edisp_data, ) self.bkg = Background3D( energy_lo=self.energy_lo, energy_hi=self.energy_hi, fov_lon_lo=self.fov_lon_lo, fov_lon_hi=self.fov_lon_hi, fov_lat_lo=self.fov_lat_lo, fov_lat_hi=self.fov_lat_hi, data=self.bkg_data, )
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"), interp_kwargs = dict(bounds_error=False, fill_value=None, values_scale="log") #allow extrapolation for symmetry tests )
def bkg_3d_interp(): """Example with simple values to test evaluate""" energy = np.logspace(-1, 3, 6) * 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((5, 3, 3)) data[-2, :, :] = 0.0 # clipping of value before last will cause extrapolation problems # as found with CTA background IRF bkg = Background3D( axes=[energy_axis, fov_lon_axis, fov_lat_axis], data=data, unit="s-1 GeV-1 sr-1", ) return bkg
def bkg_3d_custom(symmetry="constant"): if symmetry == "constant": data = np.ones((2, 3, 3)) * u.Unit("s-1 MeV-1 sr-1") elif symmetry == "symmetric": data = np.ones((2, 3, 3)) * u.Unit("s-1 MeV-1 sr-1") data[:, 1, 1] *= 2 elif symmetry == "asymmetric": data = np.indices((3, 3))[1] + 1 data = np.stack(2 * [data]) * u.Unit("s-1 MeV-1 sr-1") else: raise ValueError(f"Unkown value for symmetry: {symmetry}") energy = [0.1, 10, 1000] * u.TeV fov_lon = [-3, -1, 1, 3] * u.deg fov_lat = [-3, -1, 1, 3] * u.deg return Background3D( energy_lo=energy[:-1], energy_hi=energy[1:], fov_lon_lo=fov_lon[:-1], fov_lon_hi=fov_lon[1:], fov_lat_lo=fov_lat[:-1], fov_lat_hi=fov_lat[1:], data=data, )
aeff_data = np.ones(shape=(10, 3)) * u.cm * u.cm edisp_data = np.ones(shape=(10, 3, 3)) bkg_data = np.ones(shape=(10, 10, 10)) / u.MeV / u.s / u.sr # Create IRF Class objects with data aeff = EffectiveAreaTable2D(energy_lo=energy_lo, energy_hi=energy_hi, offset_lo=offset_lo, offset_hi=offset_hi, data=aeff_data) edisp = EnergyDispersion2D(e_true_lo=energy_lo, e_true_hi=energy_hi, migra_lo=migra_lo, migra_hi=migra_hi, offset_lo=offset_lo, offset_hi=offset_hi, data=edisp_data) bkg = Background3D(energy_lo=energy_lo, energy_hi=energy_hi, detx_lo=detx_lo, detx_hi=detx_hi, dety_lo=dety_lo, dety_hi=dety_hi, data=bkg_data) # Convert to astropy Table objects table_aeff = aeff.to_table() table_edisp = edisp.to_table() table_bkg = bkg.to_table() # Add any information that needs to be in the fits header table_aeff.meta.update(provenance) table_edisp.meta.update(provenance) table_bkg.meta.update(provenance) # Convert to fits HDU objects hdu_aeff = fits.BinTableHDU(table_aeff, name='EFFECTIVE AREA') hdu_edisp = fits.BinTableHDU(table_edisp, name='ENERGY DISPERSION')
def bkg_3d(): filename = ( "$GAMMAPY_DATA/cta-1dc/caldb/data/cta/1dc/bcf/South_z20_50h/irf_file.fits" ) return Background3D.read(filename, hdu="BACKGROUND")
"""Plot the background rate from the HESS DL3 data release 1.""" import matplotlib.pyplot as plt from gammapy.irf import Background3D filename = "$GAMMAPY_DATA/hess-dl3-dr1/data/hess_dl3_dr1_obs_id_020136.fits.gz" bkg = Background3D.read(filename, hdu="BKG") bkg.peek() plt.show()
# This is how for analysis you could slice out the PSF # at a given field of view offset psf.to_energy_dependent_table_psf("1 deg") # ### Background # # The hadronic background for CTA DC-1 is given as a template model with an absolute rate that depends on `energy`, `detx` and `dety`. The coordinates `detx` and `dety` are angles in the "field of view" coordinate frame. # # Note that really the background model for DC-1 and most CTA IRFs produced so far are radially symmetric, i.e. only depend on the FOV offset. The background model here was rotated to fill the FOV in a rotationally symmetric way, for no good reason. # In[ ]: from gammapy.irf import Background3D bkg = Background3D.read(irf_filename, hdu="BACKGROUND") print(bkg) # In[ ]: # TODO: implement a peek method for Background3D # bkg.peek() # In[ ]: bkg.data.evaluate(energy="3 TeV", fov_lon="1 deg", fov_lat="0 deg") # ## Index files and DataStore # # As we saw, you can access all of the CTA data using Astropy and Gammapy. #
"""Example of how to create an ObservationCTA from CTA's 1DC""" from gammapy.data import ObservationCTA, EventList, GTI from gammapy.irf import ( EnergyDependentMultiGaussPSF, EffectiveAreaTable2D, EnergyDispersion2D, Background3D, ) filename = "$GAMMAPY_DATA/cta-1dc/data/baseline/gps/gps_baseline_110380.fits" event_list = EventList.read(filename) gti = GTI.read(filename) filename = "$GAMMAPY_DATA/cta-1dc/caldb/data/cta/1dc/bcf/South_z20_50h/irf_file.fits" aeff = EffectiveAreaTable2D.read(filename) bkg = Background3D.read(filename) edisp = EnergyDispersion2D.read(filename, hdu="Energy Dispersion") psf = EnergyDependentMultiGaussPSF.read(filename, hdu="Point Spread Function") obs = ObservationCTA( obs_id=event_list.table.meta["OBS_ID"], events=event_list, gti=gti, psf=psf, aeff=aeff, edisp=edisp, bkg=bkg, pointing_radec=event_list.pointing_radec, observation_live_time_duration=event_list.observation_live_time_duration, observation_dead_time_fraction=event_list.observation_dead_time_fraction, )