Mercurial > repos > astroteam > hess_astro_tool
view Spectrum.py @ 1:593c4b45eda5 draft default tip
planemo upload for repository https://github.com/esg-epfl-apc/tools-astro/tree/main/tools commit 2f23ec010eab8d33d2760f061286756e17015af7
author | astroteam |
---|---|
date | Thu, 18 Apr 2024 09:26:15 +0000 |
parents | 02e4bb4fa10c |
children |
line wrap: on
line source
#!/usr/bin/env python # coding: utf-8 # flake8: noqa import json import os import shutil import matplotlib.pyplot as plt import numpy as np from astropy.coordinates import SkyCoord from astropy.io import fits from astropy.time import Time from numpy import log10, sqrt from oda_api.data_products import ODAAstropyTable, PictureProduct from oda_api.json import CustomJSONEncoder if os.path.exists("hess_dl3_dr1.tar.gz") == False: get_ipython().system( # noqa: F821 "wget https://zenodo.org/record/1421099/files/hess_dl3_dr1.tar.gz" ) get_ipython().system("tar -zxvf hess_dl3_dr1.tar.gz") # noqa: F821 from oda_api.api import ProgressReporter pr = ProgressReporter() pr.report_progress(stage="Progress", progress=0.0) # src_name='Crab' #http://odahub.io/ontology#AstrophysicalObject # RA = 83.628700 # http://odahub.io/ontology#PointOfInterestRA # DEC = 22.014700 # http://odahub.io/ontology#PointOfInterestDEC src_name = "PKS 2155-304" # http://odahub.io/ontology#AstrophysicalObject RA = 329.716938 # http://odahub.io/ontology#PointOfInterestRA DEC = -30.225588 # http://odahub.io/ontology#PointOfInterestDEC T1 = "2000-10-09T13:16:00.0" # http://odahub.io/ontology#StartTime T2 = "2022-10-10T13:16:00.0" # http://odahub.io/ontology#EndTime Radius = 2.5 # http://odahub.io/ontology#AngleDegrees R_s = 0.2 # http://odahub.io/ontology#AngleDegrees Emin = 0.1 # http://odahub.io/ontology#Energy_TeV Emax = 100.0 # http://odahub.io/ontology#Energy_TeV NEbins = 30 # http://odahub.io/ontology#Integer Efit_min = 0.2 # http://odahub.io/ontology#Energy_TeV Efit_max = 10.0 # http://odahub.io/ontology#Energy_TeV _galaxy_wd = os.getcwd() with open("inputs.json", "r") as fd: inp_dic = json.load(fd) if "_data_product" in inp_dic.keys(): inp_pdic = inp_dic["_data_product"] else: inp_pdic = inp_dic for vn, vv in inp_pdic.items(): if vn != "_selector": globals()[vn] = type(globals()[vn])(vv) E0 = 1.0 def model_dNdE(E, N, Gam): return N * (E / E0) ** (Gam) def model_rate(E1, E2, N, Gam): dEE = E2 - E1 EE = sqrt(E1 * E2) return model_dNdE(EE, N, Gam) * dEE Ebins = np.logspace(log10(Emin), log10(Emax), NEbins + 1) Emins = Ebins[:-1] Emaxs = Ebins[1:] Emeans = sqrt(Emins * Emaxs) lgEmeans = log10(Emeans) dE = Ebins[1:] - Ebins[:-1] T1 = Time(T1, format="isot", scale="utc").mjd T2 = Time(T2, format="isot", scale="utc").mjd message = "" RA_pnts = [] DEC_pnts = [] DL3_files = [] OBSIDs = [] Tstart = [] Tstop = [] flist = os.listdir("data") for f in flist: if f[-7:] == "fits.gz": DL3_files.append(f) OBSIDs.append(int(f[20:26])) hdul = fits.open("data/" + f) RA_pnts.append(float(hdul[1].header["RA_PNT"])) DEC_pnts.append(float(hdul[1].header["DEC_PNT"])) Tstart.append( Time( hdul[1].header["DATE-OBS"] + "T" + hdul[1].header["TIME-OBS"], format="isot", scale="utc", ).mjd ) Tstop.append( Time( hdul[1].header["DATE-END"] + "T" + hdul[1].header["TIME-END"], format="isot", scale="utc", ).mjd ) hdul.close() Coords_s = SkyCoord(RA, DEC, unit="degree") COORDS_pnts = SkyCoord(RA_pnts, DEC_pnts, unit="degree") seps = COORDS_pnts.separation(Coords_s).deg mask = np.where((seps < Radius) & (Tstart > T1) & (Tstop < T2))[0] OBSlist = [] for i in mask: OBSlist.append(DL3_files[i]) if len(OBSlist) == 0: message = "No data found" raise RuntimeError("No data found") offaxis = seps[mask] Tstart = np.array(Tstart)[mask] print("Found", len(Tstart), "pointings") ind = 0 pointing = OBSlist[ind] hdul = fits.open("data/" + pointing) RMF = hdul["EDISP"].data ENERG_LO = RMF["ENERG_LO"][0] ENERG_HI = RMF["ENERG_HI"][0] MIGRA_LO = RMF["MIGRA_LO"][0] # MIGRA_bins=np.linspace(0.2,5,161) MIGRA_HI = RMF["MIGRA_HI"][0] MIGRA = (MIGRA_LO + MIGRA_HI) / 2.0 ENERG = sqrt(ENERG_LO * ENERG_HI) dENERG = ENERG_HI - ENERG_LO cts_s = [] cts_b = [] Eff_area = [] Eff_area_interp = [] Texp = [] RMFs = [] for ind in range(len(OBSlist)): pointing = OBSlist[ind] hdul = fits.open("data/" + pointing) RA_pnt = hdul[1].header["RA_PNT"] DEC_pnt = hdul[1].header["DEC_PNT"] Texp.append(hdul[1].header["LIVETIME"]) dRA = RA - RA_pnt dDEC = DEC - DEC_pnt RA_b = RA_pnt - dRA DEC_b = DEC_pnt - dDEC Coords_b = SkyCoord(RA_b, DEC_b, unit="degree") Coords_pnt = SkyCoord(RA_pnt, DEC_pnt, unit="degree") dist = Coords_pnt.separation(Coords_s).deg RMF = hdul["EDISP"].data mask = RMF["THETA_LO"] < dist ind_th = len(RMF["THETA_LO"][mask]) - 1 RMF_th = RMF["MATRIX"][0][ind_th] RMF_interp = np.zeros((len(Emeans), len(ENERG))) for k in range(len(ENERG)): dp_dErec = RMF_th[:, k] / ENERG[k] Erec = MIGRA * ENERG[k] dp_dErec_interp = ( np.interp(Emeans, Erec, dp_dErec) * (Emeans > min(Erec)) * (Emeans < max(Erec)) ) RMF_interp[:, k] = dp_dErec_interp RMFs.append(RMF_interp) AEFF = hdul["AEFF"].data Eff_area.append(AEFF["EFFAREA"][0][ind_th]) Eff_area_interp.append(np.interp(Emeans, ENERG, Eff_area[-1])) ev = hdul["EVENTS"].data ev_ra = ev["RA"] ev_dec = ev["DEC"] ev_en = ev["ENERGY"] ev_time = ev["TIME"] ev_coords = SkyCoord(ev_ra, ev_dec, unit="degree") sep_s = ev_coords.separation(Coords_s).deg sep_b = ev_coords.separation(Coords_b).deg mask = sep_s < R_s cts_s.append(np.histogram(ev_en[mask], bins=Ebins)[0]) mask = sep_b < R_s cts_b.append(np.histogram(ev_en[mask], bins=Ebins)[0]) hdul.close() cts_s = np.array(cts_s) cts_b = np.array(cts_b) Eff_area = np.array(Eff_area) Eff_area_interp = np.array(Eff_area_interp) Texp = np.array(Texp) cts_s_tot = sum(cts_s) cts_b_tot = sum(cts_b) src_tot = cts_s_tot - cts_b_tot src_tot_err = sqrt(cts_s_tot + cts_b_tot) Expos_tot_interp = sum(Eff_area_interp * np.outer(Texp, np.ones(NEbins))) * 1e4 flux_tot = src_tot / (Expos_tot_interp + 1) / (Emaxs - Emins) * Emaxs * Emins flux_tot_err = ( src_tot_err / (Expos_tot_interp + 1) / (Emaxs - Emins) * Emaxs * Emins ) print( "Total source counts:", sum(cts_s_tot), "; background counts", sum(cts_b_tot), ) def model_cts_Erec(N, Gam): model_ENERG = model_rate(ENERG_LO, ENERG_HI, N, Gam) res = np.zeros(NEbins) for ind in range(len(OBSlist)): model_counts_ENERG = model_ENERG * Eff_area[ind] * 1e4 * Texp[ind] for k in range(len(ENERG)): res += model_counts_ENERG[k] * RMFs[ind][:, k] * dE return res def chi2(p): N, slope = p counts = model_cts_Erec(N, slope) m = Emeans > Efit_min m &= Emeans < Efit_max m &= src_tot_err > 0.0 chi2 = (((counts[m] - src_tot[m]) / src_tot_err[m]) ** 2).sum() dof = sum(m) # print(N,slope,chi2) return chi2, dof plt.errorbar( Emeans, src_tot, src_tot_err, xerr=[Emeans - Emins, Emaxs - Emeans], linestyle="none", ) plt.axvline(Efit_min, color="black") plt.axvline(Efit_max, color="black") plt.xscale("log") plt.yscale("log") N = 4e-11 Gam = -2.7 plt.plot(Emeans, model_cts_Erec(N, Gam)) chi2([N, Gam])[0] # 1) find 90% confidence contour scanning over a wide parameter space Norm_max = 1e-10 Norm_min = 1e-12 Norm_bins = 100 Gam_min = -1.0 Gam_max = -5.0 Gam_bins = 100 Ns = np.linspace(Norm_min, Norm_max, Norm_bins) Gams = np.linspace(Gam_min, Gam_max, Gam_bins) chi2_map = np.zeros((Norm_bins, Gam_bins)) Norm_best = Norm_min Gam_best = Gam_min chi2_best = 1e10 for i, N in enumerate(Ns): pr.report_progress(stage="Progress", progress=5 + 45 * i / Norm_bins) for j, Gam in enumerate(Gams): chi2_map[i, j] = chi2([N, Gam])[0] if chi2_map[i, j] < chi2_best: Norm_best = N Gam_best = Gam chi2_best = chi2_map[i, j] print(Norm_best, Gam_best) # plt.imshow(chi2_map,vmax=np.amin(chi2_map)+4,origin='lower',extent=[Gams[0],Gams[-1],Ns[0],Ns[-1]],aspect=(Gams[-1]-Gams[0])/(Ns[-1]-Ns[0])) # 68% contour from https://ui.adsabs.harvard.edu/abs/1976ApJ...208..177L/abstract for two-parameter fit # 90% contour from https://ui.adsabs.harvard.edu/abs/1976ApJ...208..177L/abstract for two-parameter fit cnt = plt.contour( Gams, Ns, chi2_map, levels=[np.amin(chi2_map) + 4.61], colors="red" ) plt.scatter([Gam_best], [Norm_best], marker="x", color="red") # plt.colorbar() print(np.amin(chi2_map)) cont = cnt.get_paths()[0].vertices gammas = cont[:, 0] norms = cont[:, 1] # 2) refine with 68% contour calculation within the initial 90% countour Norm_max = max(1.1 * norms) Norm_min = min(0.9 * norms) Norm_bins = 50 Gam_min = min(gammas - 0.2) Gam_max = max(gammas + 0.2) Gam_bins = 50 Ns = np.linspace(Norm_min, Norm_max, Norm_bins) Gams = np.linspace(Gam_min, Gam_max, Gam_bins) chi2_map = np.zeros((Norm_bins, Gam_bins)) Norm_best = Norm_min Gam_best = Gam_min chi2_best = 1e10 for i, N in enumerate(Ns): pr.report_progress(stage="Progress", progress=50 + 50 * i / Norm_bins) for j, Gam in enumerate(Gams): chi2_map[i, j] = chi2([N, Gam])[0] if chi2_map[i, j] < chi2_best: Norm_best = N Gam_best = Gam chi2_best = chi2_map[i, j] print(Norm_best, Gam_best) # plt.imshow(chi2_map,vmax=np.amin(chi2_map)+4,origin='lower',extent=[Gams[0],Gams[-1],Ns[0],Ns[-1]],aspect=(Gams[-1]-Gams[0])/(Ns[-1]-Ns[0])) # 68% contour from https://ui.adsabs.harvard.edu/abs/1976ApJ...208..177L/abstract for two-parameter fit cnt = plt.contour( Gams, Ns, chi2_map, levels=[np.amin(chi2_map) + 2.3], colors="black" ) cont = cnt.get_paths()[0].vertices gammas = cont[:, 0] norms = cont[:, 1] plt.scatter([Gam_best], [Norm_best], marker="x", color="black") # plt.colorbar() print(chi2([Norm_best, Gam_best])) plt.xlabel("Slope") plt.ylabel(r"Norm, 1/(TeV cm$^2$ s)") plt.savefig("Contour.png", format="png", bbox_inches="tight") plt.figure(figsize=(8, 6)) x = np.logspace(log10(Efit_min), log10(Efit_max), 10) ymax = np.zeros(10) ymin = np.ones(10) for i in range(len(gammas)): y = model_dNdE(x, norms[i], gammas[i]) * x**2 ymax = np.maximum(y, ymax) ymin = np.minimum(y, ymin) # plt.plot(x,y) plt.fill_between(x, ymin, ymax, alpha=0.2) plt.plot(x, model_dNdE(x, Norm_best, Gam_best) * x**2) plt.errorbar( Emeans, flux_tot, flux_tot_err, xerr=[Emeans - Emins, Emaxs - Emeans], linestyle="none", color="black", linewidth=2, ) # plt.axvspan(0, Efit_min, alpha=0.2, color='black') # plt.axvspan(Efit_max, 1000, alpha=0.2, color='black') plt.xscale("log") plt.yscale("log") plt.xlim(0.1, 100) plt.xlabel("$E$, TeV") plt.ylabel("$E^2 dN/dE$, TeV/(cm$^2$ s)") plt.savefig("Spectrum.png", format="png", bbox_inches="tight") print("Here") new_hdul = fits.HDUList() form = str(len(ENERG)) + "E" for i in range(len(OBSlist)): col1 = fits.Column(name="COUNTS", format="E", array=cts_s[i]) col2 = fits.Column(name="BACKGROUND", format="E", array=cts_b[i]) hdu = fits.BinTableHDU.from_columns([col1, col2], name="COUNTS") new_hdul.append(hdu) col = fits.Column(format=form, array=RMFs[i], name="RMF") hdu = fits.BinTableHDU.from_columns([col], name="RMF") new_hdul.append(hdu) col = fits.Column(format="E", array=Eff_area[i], name="ARF") hdu = fits.BinTableHDU.from_columns([col], name="ARF") new_hdul.append(hdu) col1 = fits.Column(name="E_MIN", format="E", unit="TeV", array=Emins) col2 = fits.Column(name="E_MAX", format="E", unit="TeV", array=Emaxs) cols = fits.ColDefs([col1, col2]) hdu = fits.BinTableHDU.from_columns(cols, name="Erec_BOUNDS") new_hdul.append(hdu) col1 = fits.Column(name="ENERG_LO", format="E", unit="TeV", array=ENERG_LO) col2 = fits.Column(name="ENERG_HI", format="E", unit="TeV", array=ENERG_HI) cols = fits.ColDefs([col1, col2]) hdu = fits.BinTableHDU.from_columns(cols, name="Etrue_BOUNDS") new_hdul.append(hdu) # new_hdul.writeto('Spectra.fits',overwrite=True) print("and here") bin_image = PictureProduct.from_file("Spectrum.png") bin_image1 = PictureProduct.from_file("Contour.png") from astropy.table import Table data = [Emeans, Emins, Emaxs, flux_tot, flux_tot_err] names = ( "Emean[TeV]", "Emin[TeV]", "Emax[TeV]", "Flux[TeV/cm2s]", "Flux_error[TeV/cm2s]", ) spec = ODAAstropyTable(Table(data, names=names)) data = [ np.concatenate(([Gam_best], gammas)), np.concatenate(([Norm_best], norms)), ] names = ["Gamma", "Norm_1TeV[1/(TeV cm2 s)]"] error_ellipse = ODAAstropyTable(Table(data, names=names)) png = bin_image # http://odahub.io/ontology#ODAPictureProduct table_confidence_contour = ( error_ellipse # http://odahub.io/ontology#ODAAstropyTable ) table_spectrum = spec # http://odahub.io/ontology#ODAAstropyTable # output gathering _galaxy_meta_data = {} _oda_outs = [] _oda_outs.append(("out_Spectrum_png", "png_galaxy.output", png)) _oda_outs.append( ( "out_Spectrum_table_confidence_contour", "table_confidence_contour_galaxy.output", table_confidence_contour, ) ) _oda_outs.append( ( "out_Spectrum_table_spectrum", "table_spectrum_galaxy.output", table_spectrum, ) ) for _outn, _outfn, _outv in _oda_outs: _galaxy_outfile_name = os.path.join(_galaxy_wd, _outfn) if isinstance(_outv, str) and os.path.isfile(_outv): shutil.move(_outv, _galaxy_outfile_name) _galaxy_meta_data[_outn] = {"ext": "_sniff_"} elif getattr(_outv, "write_fits_file", None): _outv.write_fits_file(_galaxy_outfile_name) _galaxy_meta_data[_outn] = {"ext": "fits"} elif getattr(_outv, "write_file", None): _outv.write_file(_galaxy_outfile_name) _galaxy_meta_data[_outn] = {"ext": "_sniff_"} else: with open(_galaxy_outfile_name, "w") as fd: json.dump(_outv, fd, cls=CustomJSONEncoder) _galaxy_meta_data[_outn] = {"ext": "json"} with open(os.path.join(_galaxy_wd, "galaxy.json"), "w") as fd: json.dump(_galaxy_meta_data, fd) print("*** Job finished successfully ***")