Skip to content

Commit be8d805

Browse files
committed
adding validation script
1 parent ac87ace commit be8d805

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed

validation/validate_source.py

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import logging
2+
import numpy as np
3+
from scipy.stats import norm
4+
from os import environ
5+
from IPython.display import display
6+
import pandas as pd
7+
import astropy.units as u
8+
from astropy.coordinates import Angle, SkyCoord
9+
from regions import CircleSkyRegion
10+
import matplotlib.pyplot as plt
11+
from gammapy.analysis import Analysis, AnalysisConfig
12+
from gammapy.datasets import MapDatasetOnOff, MapDataset,FluxPointsDataset
13+
from gammapy.estimators import ExcessMapEstimator,FluxPointsEstimator, FluxPoints
14+
from gammapy.makers import RingBackgroundMaker, SafeMaskMaker, MapDatasetMaker,SpectrumDatasetMaker
15+
from gammapy.data import DataStore
16+
from gammapy.maps import MapAxis, WcsGeom
17+
from tqdm.auto import tqdm
18+
from gammapy.modeling.models import PowerLawSpectralModel, SkyModel
19+
from astroquery.simbad import Simbad
20+
from gammapy.maps import MapCoord
21+
from gammapy.modeling import Fit
22+
from astropy.table import Table
23+
import sys
24+
import seaborn as sns
25+
26+
sns.set_theme(font="Serif",font_scale=2,style='ticks',context='paper',palette='pastel')
27+
28+
datastore_dir = sys.argv[1]
29+
source_pos = sys.argv[2] # [ra,dec] in deg
30+
plaw = sys.argv[3] # power law parameters as [index, amp] in TeV-1 s-1 cm-2
31+
source = sys.argv[4]
32+
33+
logfile = f'{source}.log'
34+
35+
datastore = DataStore.from_dir(datastore_dir)
36+
observations = datastore.get_observations()
37+
38+
source_pos = SkyCoord(source_pos[0],source_pos[1],unit='deg')
39+
40+
energy_axis = MapAxis.from_energy_edges([0.2,0.316,0.501,0.794,1.259,1.995,3.162,5.012,7.943,12.589,19.953,31.623]*u.TeV, unit="TeV")
41+
energy_axis_true = MapAxis.from_energy_bounds(
42+
0.05, 100, 100, unit="TeV", name="energy_true"
43+
)
44+
geom = WcsGeom.create(
45+
skydir=(source_pos.ra.value, source_pos.dec.value),
46+
binsz=0.02,
47+
width=(4, 4),
48+
frame="icrs",
49+
proj="CAR",
50+
axes=[energy_axis],
51+
)
52+
simbad = Simbad()
53+
simbad.reset_votable_fields()
54+
simbad.add_votable_fields('ra', 'dec', "flux(B)", "flux(V)", "jp11")
55+
simbad.remove_votable_fields('coordinates')
56+
57+
srcs_tab = simbad.query_region(source_pos, radius=1.5*u.deg)
58+
srcs_tab = srcs_tab[srcs_tab["FLUX_B"]<6]
59+
srcs_tab = srcs_tab[srcs_tab["FLUX_V"]!=np.ma.masked]
60+
61+
geom_image = geom.to_image().to_cube([energy_axis.squash()])
62+
63+
regions = CircleSkyRegion(center=source_pos, radius=0.5 * u.deg)
64+
all_ex = [regions]
65+
stars = []
66+
for star in srcs_tab:
67+
pos = SkyCoord(star["RA"], star["DEC"], frame="fk5", unit=(u.hourangle, u.deg))
68+
star = CircleSkyRegion(center=pos, radius=0.3 * u.deg)
69+
stars.append(star)
70+
all_ex.append(star)
71+
72+
exclusion_mask = ~geom_image.region_mask(all_ex)
73+
stacked = MapDataset.create(
74+
geom=geom, energy_axis_true=energy_axis_true, name="stacked"
75+
)
76+
offset_max = 1.75 * u.deg
77+
maker = MapDatasetMaker()# add make exposure map?
78+
maker.available_selection = []
79+
maker_safe_mask = SafeMaskMaker(
80+
methods=["aeff-max","offset-max"], aeff_percent=10, offset_max = 1.75 * u.deg
81+
)
82+
83+
for obs in observations:
84+
dataset = maker.run(stacked,obs)
85+
dataset = maker_safe_mask.run(dataset, obs)
86+
stacked.stack(dataset)
87+
88+
ring_maker = RingBackgroundMaker(
89+
r_in="0.63 deg", width="0.14 deg", exclusion_mask=exclusion_mask
90+
)
91+
dataset_on_off = ring_maker.run(stacked)
92+
93+
pl = PowerLawSpectralModel(index=plaw[0],amplitude=plaw[1]*u.Unit("TeV-1 s-1 cm-2"))
94+
sky_model = SkyModel(spectral_model=pl,name='Crab')
95+
96+
on_region = CircleSkyRegion(center=source_pos, radius=np.sqrt(0.008) * u.deg)
97+
spectral_dataset = dataset_on_off.to_spectrum_dataset(on_region,containment_correction=True)
98+
spectral_dataset.models = [sky_model]
99+
fit = Fit()
100+
result = fit.run(datasets=spectral_dataset)
101+
102+
index = result.models['Crab'].index
103+
index_err = result.models['Crab'].index.error
104+
amplitude = result.models['Crab'].amplitude
105+
amplitude_error = result.models['Crab'].amplitude
106+
107+
with open (logfile) as log:
108+
log.write(f'index: {index} +/- {index_err}')
109+
log.write(f'amplitude: {amplitude} +/- {amplitude_error}')
110+
111+
fpe = FluxPointsEstimator(
112+
energy_edges=[0.2,0.316,0.501,0.794,1.259,1.995,3.162,5.012,7.943,12.589]*u.TeV, source="Crab", selection_optional="all",reoptimize=False
113+
)
114+
flux_points = fpe.run(datasets=spectral_dataset)
115+
116+
flux_points.plot(sed_type='dnde',energy_power=2,color='plum')
117+
result.models[0].spectral_model.plot([0.1,10]*u.TeV,sed_type='dnde',color='b',energy_power=2)
118+
result.models[0].spectral_model.plot_error([0.1,10]*u.TeV,sed_type='dnde',alpha=0.1,energy_power=2)
119+
plt.savefig(f'{source}_sed.pdf',format='pdf')
120+
121+
flux_points.write(f'{source}_fluxpoints.ecsv')
122+
123+
# make new dataset for significance calculations
124+
stacked = MapDataset.create(
125+
geom=geom, energy_axis_true=energy_axis_true, name="stacked"
126+
)
127+
128+
maker_safe_mask = SafeMaskMaker(
129+
methods=["offset-max"], offset_max = 1.75 * u.deg
130+
)
131+
132+
for obs in observations:
133+
dataset = maker.run(stacked,obs)
134+
dataset = maker_safe_mask.run(dataset, obs)
135+
stacked.stack(dataset)
136+
137+
ring_maker = RingBackgroundMaker(
138+
r_in="0.63 deg", width="0.14 deg", exclusion_mask=exclusion_mask
139+
)
140+
dataset_on_off = ring_maker.run(stacked)
141+
142+
estimator = ExcessMapEstimator(np.sqrt(0.008) * u.deg, selection_optional=["alpha"],correlate_off=False,
143+
#gamma_min_sensitivity=10,bkg_syst_fraction_sensitivity=0.05,
144+
#apply_threshold_sensitivity=False,
145+
#spectral_model = pl,
146+
#sum_over_energy_groups=True,
147+
)
148+
lima_maps = estimator.run(dataset_on_off)
149+
150+
significance_map = lima_maps["sqrt_ts"]
151+
excess_map = lima_maps["npred_excess"]
152+
153+
my_cmap = sns.color_palette("magma", as_cmap=True)
154+
155+
# We can plot the excess and significance maps
156+
fig, (ax1, ax2) = plt.subplots(
157+
figsize=(13, 5), subplot_kw={"projection": lima_maps.geom.wcs}, ncols=2
158+
)
159+
ax1.set_title("Significance map")
160+
significance_map.plot(ax=ax1, add_cbar=True,cmap=my_cmap,vmin=-5,vmax=5)
161+
ax2.set_title("Excess map")
162+
excess_map.plot(ax=ax2, add_cbar=True,cmap=my_cmap)
163+
plt.savefig(f'{source}_sigmap.pdf', format='pdf')
164+
165+
significance_map_off = significance_map * exclusion_mask
166+
167+
# significance distribution
168+
significance_all = significance_map.data[np.isfinite(significance_map.data)].flatten()
169+
significance_off = significance_map_off.data[exclusion_mask & np.isfinite(significance_map_off.data)].flatten()
170+
171+
fig, ax = plt.subplots()
172+
ax.hist(
173+
significance_all,
174+
density=True,
175+
alpha=0.5,
176+
color="red",
177+
label="all bins",
178+
bins=np.linspace(-5,10,50),
179+
)
180+
181+
ax.hist(
182+
significance_off,
183+
density=True,
184+
alpha=0.5,
185+
color="blue",
186+
label="off bins",
187+
bins=np.linspace(-5,10,50),
188+
)
189+
190+
191+
mu, std = norm.fit(significance_off)
192+
x = np.linspace(-5, 10, 100)
193+
p = norm.pdf(x, mu, std)
194+
ax.plot(x, p, lw=2, color="black")
195+
ax.plot(x,norm.pdf(x,0,1),color='k',ls='--')
196+
197+
ax.legend()
198+
ax.set_xlabel("Significance")
199+
ax.set_yscale("log")
200+
ax.set_ylim(1e-5, 1)
201+
ax.set_xlim(-5, 10)
202+
203+
print(f"Fit results: mu = {mu:.2f}, std = {std:.2f}")
204+
ax.text(-4.5, 0.5, f"Fit results: mu = {mu:.2f}, std = {std:.2f}")
205+
plt.savefig(f'{source}_sigdist.pdf',format='pdf')
206+
207+
counts = lima_maps['counts'].get_by_coord((source_pos,1*u.TeV))[0]
208+
sig = lima_maps['sqrt_ts'].get_by_coord((source_pos,1*u.TeV))[0]
209+
bkg = lima_maps['npred_background'].get_by_coord((source_pos,1*u.TeV))[0]
210+
alpha = lima_maps['alpha'].get_by_coord((source_pos,1*u.TeV))[0]
211+
212+
with open(logfile) as log:
213+
log.write(f'ON: {counts}')
214+
log.write(f'OFF: {bkg}')
215+
log.write(f'Significance: {sig}')
216+
log.write(f'Alpha: {alpha}')

0 commit comments

Comments
 (0)