Mercurial > repos > astroteam > cta_astro_tool
comparison pre-defined_model.py @ 0:2f3e314c3dfa draft default tip
planemo upload for repository https://github.com/esg-epfl-apc/tools-astro/tree/main/tools commit 4543470805fc78f6cf2604b9d55beb6f06359995
| author | astroteam |
|---|---|
| date | Fri, 19 Apr 2024 10:06:21 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:2f3e314c3dfa |
|---|---|
| 1 #!/usr/bin/env python | |
| 2 # coding: utf-8 | |
| 3 | |
| 4 # flake8: noqa | |
| 5 | |
| 6 import json | |
| 7 import os | |
| 8 import shutil | |
| 9 | |
| 10 from oda_api.json import CustomJSONEncoder | |
| 11 | |
| 12 get_ipython().run_cell_magic( # noqa: F821 | |
| 13 "bash", | |
| 14 "", | |
| 15 'wget https://gitlab.renkulab.io/astronomy/mmoda/cta/-/raw/master/Franceschini17.txt\n\nrm -r IRFS | echo "Ok"\nmkdir IRFS\ncd IRFS\nwget https://zenodo.org/records/5499840/files/cta-prod5-zenodo-fitsonly-v0.1.zip\nunzip cta-prod5-zenodo-fitsonly-v0.1.zip\ncd fits\nfor fn in *.gz ; do tar -zxvf $fn; done \n \n', | |
| 16 ) | |
| 17 | |
| 18 import astropy.units as u | |
| 19 import matplotlib.pyplot as plt | |
| 20 import numpy as np | |
| 21 from astropy import wcs | |
| 22 from astropy.coordinates import SkyCoord | |
| 23 from astropy.io import fits | |
| 24 from gammapy.data import Observation | |
| 25 from gammapy.datasets import MapDataset, MapDatasetEventSampler | |
| 26 from gammapy.irf import load_irf_dict_from_file | |
| 27 from gammapy.makers import MapDatasetMaker | |
| 28 from gammapy.maps import MapAxis, WcsGeom | |
| 29 from gammapy.modeling.models import FoVBackgroundModel, Models | |
| 30 from numpy import cos, exp, pi, sqrt | |
| 31 from oda_api.api import ProgressReporter | |
| 32 from oda_api.data_products import BinaryProduct, PictureProduct | |
| 33 from regions import CircleSkyRegion | |
| 34 | |
| 35 # from gammapy.irf import load_cta_irfs | |
| 36 | |
| 37 RA = 166.113809 # http://odahub.io/ontology#PointOfInterestRA | |
| 38 DEC = 38.208833 # http://odahub.io/ontology#PointOfInterestDEC | |
| 39 | |
| 40 OffAxis_angle = 0.78 # http://odahub.io/ontology#AngleDegrees | |
| 41 # Exposure time in hours | |
| 42 Texp = 1.0 # http://odahub.io/ontology#TimeIntervalHours | |
| 43 # Source redshift | |
| 44 z = 0.03 # http://odahub.io/ontology#Float | |
| 45 # Source flux normalisaiton F0 in 1/(TeV cm2 s) at reference energy E0 | |
| 46 F0 = 1e-11 # http://odahub.io/ontology#Float | |
| 47 E0 = 1.0 # http://odahub.io/ontology#Energy_TeV | |
| 48 Gamma = 2.0 # http://odahub.io/ontology#Float | |
| 49 | |
| 50 Radius_spectal_extraction = 0.2 # http://odahub.io/ontology#Float | |
| 51 Radius_sky_image = 2.5 # http://odahub.io/ontology#AngleDegrees | |
| 52 | |
| 53 Site = "North" # http://odahub.io/ontology#String ; oda:allowed_value "North","South" | |
| 54 Telescope_LST = True # http://odahub.io/ontology#Boolean | |
| 55 Telescope_MST = True # http://odahub.io/ontology#Boolean | |
| 56 Telescope_SST = False # http://odahub.io/ontology#Boolean | |
| 57 | |
| 58 _galaxy_wd = os.getcwd() | |
| 59 | |
| 60 with open("inputs.json", "r") as fd: | |
| 61 inp_dic = json.load(fd) | |
| 62 if "_data_product" in inp_dic.keys(): | |
| 63 inp_pdic = inp_dic["_data_product"] | |
| 64 else: | |
| 65 inp_pdic = inp_dic | |
| 66 | |
| 67 for vn, vv in inp_pdic.items(): | |
| 68 if vn != "_selector": | |
| 69 globals()[vn] = type(globals()[vn])(vv) | |
| 70 | |
| 71 LSTs = Telescope_LST | |
| 72 MSTs = Telescope_MST | |
| 73 SSTs = Telescope_SST | |
| 74 | |
| 75 Texp = Texp * 3600.0 | |
| 76 DEC_pnt = DEC | |
| 77 cdec = cos(DEC * pi / 180.0) | |
| 78 RA_pnt = RA - OffAxis_angle / cdec | |
| 79 Radius = Radius_sky_image | |
| 80 R_s = Radius_spectal_extraction | |
| 81 | |
| 82 pointing = SkyCoord(RA_pnt, DEC_pnt, unit="deg", frame="icrs") | |
| 83 coord_s = SkyCoord(RA, DEC, unit="deg", frame="icrs") | |
| 84 RA_bkg = RA_pnt - (RA - RA_pnt) | |
| 85 DEC_bkg = DEC_pnt - (DEC - DEC_pnt) | |
| 86 coord_b = SkyCoord(RA_bkg, DEC_bkg, unit="deg", frame="icrs") | |
| 87 offaxis = coord_s.separation(pointing).deg | |
| 88 pr = ProgressReporter() | |
| 89 pr.report_progress(stage="Progress", progress=10.0) | |
| 90 | |
| 91 CTA_south_lat = -25.0 | |
| 92 CTA_north_lat = 18.0 | |
| 93 if Site == "North": | |
| 94 Zd = abs(DEC - CTA_north_lat) | |
| 95 if Zd < 30.0: | |
| 96 Zd = "20deg-" | |
| 97 elif Zd < 50: | |
| 98 Zd = "40deg-" | |
| 99 elif Zd < 70.0: | |
| 100 Zd = "60deg-" | |
| 101 else: | |
| 102 raise RuntimeError("Source not visible from " + Site) | |
| 103 if DEC > CTA_north_lat: | |
| 104 N_S = "NorthAz-" | |
| 105 else: | |
| 106 N_S = "SouthAz-" | |
| 107 if LSTs: | |
| 108 tel = "4LSTs" | |
| 109 if MSTs: | |
| 110 tel += "09MSTs" | |
| 111 if SSTs: | |
| 112 raise RuntimeError("No SSTs on the North site") | |
| 113 filename = "IRFS/fits/Prod5-North-" + Zd + N_S + tel | |
| 114 else: | |
| 115 Zd = abs(DEC - CTA_south_lat) | |
| 116 if Zd < 30.0: | |
| 117 Zd = "20deg-" | |
| 118 elif Zd < 50: | |
| 119 Zd = "40deg-" | |
| 120 elif Zd < 70.0: | |
| 121 Zd = "60deg-" | |
| 122 else: | |
| 123 raise RuntimeError("Source not visible from " + Site) | |
| 124 if DEC > CTA_south_lat: | |
| 125 N_S = "NorthAz-" | |
| 126 else: | |
| 127 N_S = "SouthAz-" | |
| 128 if MSTs: | |
| 129 tel = "14MSTs" | |
| 130 if SSTs: | |
| 131 tel += "37MSTs" | |
| 132 if LSTs: | |
| 133 raise RuntimeError("No LSTs on the South site") | |
| 134 filename = "IRFS/fits/Prod5-South-" + Zd + N_S + tel | |
| 135 | |
| 136 if Texp < 1800: | |
| 137 filename += ".1800s-v0.1.fits.gz" | |
| 138 elif Texp < 18000: | |
| 139 filename += ".18000s-v0.1.fits.gz" | |
| 140 else: | |
| 141 filename += ".180000s-v0.1.fits.gz" | |
| 142 import os | |
| 143 | |
| 144 print(filename) | |
| 145 if os.path.exists(filename) == False: | |
| 146 raise RuntimeError("No reponse function found") | |
| 147 message = "No reponse function found!" | |
| 148 | |
| 149 hdul = fits.open(filename) | |
| 150 aeff = hdul["EFFECTIVE AREA"].data | |
| 151 ENERG_LO = aeff["ENERG_LO"][0] | |
| 152 ENERG_HI = aeff["ENERG_HI"][0] | |
| 153 THETA_LO = aeff["THETA_LO"][0] | |
| 154 THETA_HI = aeff["THETA_HI"][0] | |
| 155 EFFAREA = aeff["EFFAREA"][0] | |
| 156 print(offaxis) | |
| 157 ind_offaxis = len(THETA_LO[THETA_LO < offaxis] - 1) | |
| 158 EFAREA = EFFAREA[ind_offaxis] | |
| 159 HDU_EFFAREA = hdul["EFFECTIVE AREA"] | |
| 160 HDU_RMF = hdul["ENERGY DISPERSION"] | |
| 161 | |
| 162 E = np.logspace(-2, 2, 20) | |
| 163 | |
| 164 d = np.genfromtxt("Franceschini17.txt") | |
| 165 ee = d[:, 0] | |
| 166 z_grid = np.array([0.01, 0.03, 0.1, 0.3, 0.5, 1.0, 1.5, 2.0, 3.0]) | |
| 167 ind = len(z_grid[z > z_grid]) - 1 | |
| 168 coeff = (z - z_grid[ind]) / (z_grid[ind + 1] - z_grid[ind]) | |
| 169 tau = d[:, ind + 1] + coeff * d[:, ind + 2] | |
| 170 tau_interp = np.interp(E, ee, tau) | |
| 171 | |
| 172 def powerlaw_EBL(): | |
| 173 return F0 * (E / E0) ** (-Gamma) * exp(-tau_interp) | |
| 174 | |
| 175 F = powerlaw_EBL() | |
| 176 plt.plot(E, E**2 * F) | |
| 177 plt.xscale("log") | |
| 178 plt.yscale("log") | |
| 179 plt.ylim(1e-14, 1e-10) | |
| 180 | |
| 181 # filename = "IRFS/fits/Prod5-North-20deg-AverageAz-4LSTs09MSTs.180000s-v0.1.fits.gz" | |
| 182 IRFS = load_irf_dict_from_file(filename) | |
| 183 dic = { | |
| 184 "components": [ | |
| 185 { | |
| 186 "name": "Source1", | |
| 187 "type": "SkyModel", | |
| 188 "spectral": { | |
| 189 "type": "TemplateSpectralModel", | |
| 190 "parameters": [{"name": "norm", "value": 1.0}], | |
| 191 "energy": {"data": E.tolist(), "unit": "TeV"}, | |
| 192 "values": {"data": F.tolist(), "unit": "1 / (cm2 TeV s)"}, | |
| 193 }, | |
| 194 "spatial": { | |
| 195 "type": "PointSpatialModel", | |
| 196 "frame": "icrs", | |
| 197 "parameters": [ | |
| 198 {"name": "lon_0", "value": RA, "unit": "deg"}, | |
| 199 {"name": "lat_0", "value": DEC, "unit": "deg"}, | |
| 200 ], | |
| 201 }, | |
| 202 }, | |
| 203 { | |
| 204 "type": "FoVBackgroundModel", | |
| 205 "datasets_names": ["my-dataset"], | |
| 206 "spectral": { | |
| 207 "type": "PowerLawNormSpectralModel", | |
| 208 "parameters": [ | |
| 209 {"name": "norm", "value": 1.0}, | |
| 210 {"name": "tilt", "value": 0.0}, | |
| 211 {"name": "reference", "value": 1.0, "unit": "TeV"}, | |
| 212 ], | |
| 213 }, | |
| 214 }, | |
| 215 ] | |
| 216 } | |
| 217 modelsky = Models.from_dict(dic) | |
| 218 | |
| 219 bkg_model = FoVBackgroundModel(dataset_name="my-dataset") | |
| 220 | |
| 221 observation = Observation.create( | |
| 222 obs_id="0", pointing=pointing, livetime=str(Texp) + " s", irfs=IRFS | |
| 223 ) | |
| 224 | |
| 225 print(f"Create the dataset for {pointing}") | |
| 226 energy_axis = MapAxis.from_energy_bounds( | |
| 227 "0.012 TeV", "100 TeV", nbin=10, per_decade=True | |
| 228 ) | |
| 229 energy_axis_true = MapAxis.from_energy_bounds( | |
| 230 "0.001 TeV", "300 TeV", nbin=20, per_decade=True, name="energy_true" | |
| 231 ) | |
| 232 migra_axis = MapAxis.from_bounds( | |
| 233 0.5, 2, nbin=150, node_type="edges", name="migra" | |
| 234 ) | |
| 235 | |
| 236 geom = WcsGeom.create( | |
| 237 skydir=pointing, | |
| 238 width=(2 * Radius, 2 * Radius), | |
| 239 binsz=0.02, | |
| 240 frame="icrs", | |
| 241 axes=[energy_axis], | |
| 242 ) | |
| 243 | |
| 244 empty = MapDataset.create( | |
| 245 geom, | |
| 246 energy_axis_true=energy_axis_true, | |
| 247 migra_axis=migra_axis, | |
| 248 name="my-dataset", | |
| 249 ) | |
| 250 maker = MapDatasetMaker(selection=["exposure", "background", "psf", "edisp"]) | |
| 251 dataset = maker.run(empty, observation) | |
| 252 | |
| 253 region_sky = CircleSkyRegion(center=pointing, radius=Radius * u.deg) | |
| 254 mask_map = dataset.geoms["geom"].region_mask(region_sky) | |
| 255 mod = modelsky.select_mask(mask_map) | |
| 256 | |
| 257 bkg_idx = np.where(np.array(modelsky.names) == "my-dataset-bkg") | |
| 258 mod.append(modelsky[int(bkg_idx[0][0])]) | |
| 259 | |
| 260 dataset.models = mod | |
| 261 | |
| 262 for m in dataset.models[:-1]: | |
| 263 sep = m.spatial_model.position.separation(pointing).deg | |
| 264 print( | |
| 265 f"This is the spatial separation of {m.name} from the pointing direction: {sep}" | |
| 266 ) | |
| 267 pr.report_progress(stage="Progress", progress=50.0) | |
| 268 print("Simulate...") | |
| 269 sampler = MapDatasetEventSampler() | |
| 270 events = sampler.run(dataset, observation) | |
| 271 | |
| 272 pr.report_progress(stage="Progress", progress=90.0) | |
| 273 print(f"Save events ...") | |
| 274 primary_hdu = fits.PrimaryHDU() | |
| 275 hdu_evt = fits.BinTableHDU(events.table) | |
| 276 hdu_gti = fits.BinTableHDU(dataset.gti.table, name="GTI") | |
| 277 hdu_all = fits.HDUList([primary_hdu, hdu_evt, hdu_gti, HDU_EFFAREA, HDU_RMF]) | |
| 278 hdu_all.writeto(f"./events.fits", overwrite=True) | |
| 279 | |
| 280 hdul = fits.open("events.fits") | |
| 281 ev = hdul["EVENTS"].data | |
| 282 ra = ev["RA"] | |
| 283 dec = ev["DEC"] | |
| 284 coords = SkyCoord(ra, dec, unit="degree") | |
| 285 en = ev["ENERGY"] | |
| 286 | |
| 287 from matplotlib.colors import LogNorm | |
| 288 | |
| 289 plt.figure() | |
| 290 pixsize = 0.1 | |
| 291 Nbins = 2 * int(Radius / pixsize) + 1 | |
| 292 ra0 = np.mean(ra) | |
| 293 dec0 = np.mean(dec) | |
| 294 from numpy import cos, pi | |
| 295 | |
| 296 cdec = cos(DEC_pnt * pi / 180.0) | |
| 297 ra_bins = np.linspace( | |
| 298 RA_pnt - Radius / cdec, RA_pnt + Radius / cdec, Nbins + 1 | |
| 299 ) | |
| 300 dec_bins = np.linspace(DEC_pnt - Radius, DEC_pnt + Radius, Nbins + 1) | |
| 301 | |
| 302 h = plt.hist2d(ra, dec, bins=[ra_bins, dec_bins], norm=LogNorm()) | |
| 303 image = h[0] | |
| 304 plt.colorbar() | |
| 305 plt.xlabel("RA") | |
| 306 plt.ylabel("Dec") | |
| 307 | |
| 308 plt.figure() | |
| 309 ev_src = en[coords.separation(coord_s).deg < R_s] | |
| 310 ev_bkg = en[coords.separation(coord_b).deg < R_s] | |
| 311 ENERG_BINS = np.concatenate((ENERG_LO, [ENERG_HI[-1]])) | |
| 312 ENERG = sqrt(ENERG_LO * ENERG_HI) | |
| 313 h1 = np.histogram(ev_src, bins=ENERG_BINS) | |
| 314 h2 = np.histogram(ev_bkg, bins=ENERG_BINS) | |
| 315 cts_s = h1[0] | |
| 316 cts_b = h2[0] | |
| 317 src = cts_s - cts_b | |
| 318 src_err = sqrt(cts_s + cts_b) | |
| 319 plt.errorbar(ENERG, src, src_err) | |
| 320 plt.axhline(0, linestyle="dashed", color="black") | |
| 321 plt.xscale("log") | |
| 322 plt.xlabel(r"$E$, TeV") | |
| 323 plt.ylabel("Counts") | |
| 324 plt.yscale("log") | |
| 325 plt.ylim(0.1, 2 * max(src)) | |
| 326 plt.savefig("Count_spectrum.png") | |
| 327 | |
| 328 plt.figure() | |
| 329 sep_s = coords.separation(coord_s).deg | |
| 330 sep_b = coords.separation(coord_b).deg | |
| 331 plt.hist(sep_s**2, bins=np.linspace(0, 0.5, 50)) | |
| 332 plt.hist(sep_b**2, bins=np.linspace(0, 0.5, 50)) | |
| 333 plt.axvline(R_s**2, color="black", linestyle="dashed") | |
| 334 plt.xlabel(r"$\theta^2$, degrees") | |
| 335 plt.ylabel("Counts") | |
| 336 plt.savefig("Theta2_plot.png") | |
| 337 | |
| 338 # Create a new WCS object. The number of axes must be set | |
| 339 # from the start | |
| 340 plt.figure() | |
| 341 w = wcs.WCS(naxis=2) | |
| 342 | |
| 343 w.wcs.ctype = ["RA---CAR", "DEC--CAR"] | |
| 344 # we need a Plate carrée (CAR) projection since histogram is binned by ra-dec | |
| 345 # the peculiarity here is that CAR projection produces rectilinear grid only if CRVAL2==0 | |
| 346 # also, we will follow convention of RA increasing from right to left (CDELT1<0, need to flip an input image) | |
| 347 # otherwise, aladin-lite doesn't show it | |
| 348 w.wcs.crval = [RA_pnt, 0] | |
| 349 w.wcs.crpix = [Nbins / 2.0 + 0.5, 1 - dec_bins[0] / pixsize] | |
| 350 w.wcs.cdelt = np.array([-pixsize / cdec, pixsize]) | |
| 351 | |
| 352 header = w.to_header() | |
| 353 | |
| 354 hdu = fits.PrimaryHDU(np.flip(image.T, axis=1), header=header) | |
| 355 hdu.writeto("Image.fits", overwrite=True) | |
| 356 hdu = fits.open("Image.fits") | |
| 357 im = hdu[0].data | |
| 358 wcs1 = wcs.WCS(hdu[0].header) | |
| 359 ax = plt.subplot(projection=wcs1) | |
| 360 lon = ax.coords["ra"] | |
| 361 lon.set_major_formatter("d.dd") | |
| 362 lat = ax.coords["dec"] | |
| 363 lat.set_major_formatter("d.dd") | |
| 364 plt.imshow(im, origin="lower") | |
| 365 plt.colorbar(label="Counts") | |
| 366 | |
| 367 plt.scatter( | |
| 368 [RA_pnt], | |
| 369 [DEC_pnt], | |
| 370 marker="x", | |
| 371 color="white", | |
| 372 alpha=0.5, | |
| 373 transform=ax.get_transform("world"), | |
| 374 ) | |
| 375 plt.scatter( | |
| 376 [RA], | |
| 377 [DEC], | |
| 378 marker="+", | |
| 379 color="red", | |
| 380 alpha=0.5, | |
| 381 transform=ax.get_transform("world"), | |
| 382 ) | |
| 383 plt.grid(color="white", ls="solid") | |
| 384 plt.xlabel("RA") | |
| 385 plt.ylabel("Dec") | |
| 386 plt.savefig("Image.png", format="png", bbox_inches="tight") | |
| 387 | |
| 388 fits_events = BinaryProduct.from_file("events.fits") | |
| 389 bin_image1 = PictureProduct.from_file("Image.png") | |
| 390 bin_image2 = PictureProduct.from_file("Theta2_plot.png") | |
| 391 bin_image3 = PictureProduct.from_file("Count_spectrum.png") | |
| 392 pr.report_progress(stage="Progress", progress=100.0) | |
| 393 | |
| 394 image_png = bin_image1 # http://odahub.io/ontology#ODAPictureProduct | |
| 395 theta2_png = bin_image2 # http://odahub.io/ontology#ODAPictureProduct | |
| 396 spectrum_png = bin_image3 # http://odahub.io/ontology#ODAPictureProduct | |
| 397 event_list_fits = fits_events # http://odahub.io/ontology#ODABinaryProduct | |
| 398 | |
| 399 # output gathering | |
| 400 _galaxy_meta_data = {} | |
| 401 _oda_outs = [] | |
| 402 _oda_outs.append( | |
| 403 ("out_pre_defined_model_image_png", "image_png_galaxy.output", image_png) | |
| 404 ) | |
| 405 _oda_outs.append( | |
| 406 ( | |
| 407 "out_pre_defined_model_theta2_png", | |
| 408 "theta2_png_galaxy.output", | |
| 409 theta2_png, | |
| 410 ) | |
| 411 ) | |
| 412 _oda_outs.append( | |
| 413 ( | |
| 414 "out_pre_defined_model_spectrum_png", | |
| 415 "spectrum_png_galaxy.output", | |
| 416 spectrum_png, | |
| 417 ) | |
| 418 ) | |
| 419 _oda_outs.append( | |
| 420 ( | |
| 421 "out_pre_defined_model_event_list_fits", | |
| 422 "event_list_fits_galaxy.output", | |
| 423 event_list_fits, | |
| 424 ) | |
| 425 ) | |
| 426 | |
| 427 for _outn, _outfn, _outv in _oda_outs: | |
| 428 _galaxy_outfile_name = os.path.join(_galaxy_wd, _outfn) | |
| 429 if isinstance(_outv, str) and os.path.isfile(_outv): | |
| 430 shutil.move(_outv, _galaxy_outfile_name) | |
| 431 _galaxy_meta_data[_outn] = {"ext": "_sniff_"} | |
| 432 elif getattr(_outv, "write_fits_file", None): | |
| 433 _outv.write_fits_file(_galaxy_outfile_name) | |
| 434 _galaxy_meta_data[_outn] = {"ext": "fits"} | |
| 435 elif getattr(_outv, "write_file", None): | |
| 436 _outv.write_file(_galaxy_outfile_name) | |
| 437 _galaxy_meta_data[_outn] = {"ext": "_sniff_"} | |
| 438 else: | |
| 439 with open(_galaxy_outfile_name, "w") as fd: | |
| 440 json.dump(_outv, fd, cls=CustomJSONEncoder) | |
| 441 _galaxy_meta_data[_outn] = {"ext": "json"} | |
| 442 | |
| 443 with open(os.path.join(_galaxy_wd, "galaxy.json"), "w") as fd: | |
| 444 json.dump(_galaxy_meta_data, fd) | |
| 445 print("*** Job finished successfully ***") |
