show_spill_over.py
1 |
#! /usr/bin/env python
|
---|---|
2 |
# ==========================================================================
|
3 |
# Show spill over for a given observation and model
|
4 |
#
|
5 |
# Copyright (C) 2018 Juergen Knoedlseder
|
6 |
#
|
7 |
# This program is free software: you can redistribute it and/or modify
|
8 |
# it under the terms of the GNU General Public License as published by
|
9 |
# the Free Software Foundation, either version 3 of the License, or
|
10 |
# (at your option) any later version.
|
11 |
#
|
12 |
# This program is distributed in the hope that it will be useful,
|
13 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
# GNU General Public License for more details.
|
16 |
#
|
17 |
# You should have received a copy of the GNU General Public License
|
18 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19 |
#
|
20 |
# ==========================================================================
|
21 |
import os |
22 |
import math |
23 |
import gammalib |
24 |
import ctools |
25 |
import cscripts |
26 |
import matplotlib.pyplot as plt |
27 |
|
28 |
|
29 |
# ====================== #
|
30 |
# Plot residual spectrum #
|
31 |
# ====================== #
|
32 |
def plot_resspec(frame, energies, resspec, errspec): |
33 |
"""
|
34 |
Plot residual spectrum
|
35 |
|
36 |
Parameters
|
37 |
----------
|
38 |
frame : pyplot
|
39 |
Frame for spectrum
|
40 |
"""
|
41 |
# Set axes scales and limit
|
42 |
frame.set_xscale('log')
|
43 |
frame.set_xlim([0.5, 40]) |
44 |
|
45 |
# Counts and model
|
46 |
frame.errorbar(energies, resspec, yerr=errspec, |
47 |
fmt='ko', capsize=0, linewidth=2, zorder=2, label='Data') |
48 |
frame.plot(frame.get_xlim(),[0,0], 'r-') |
49 |
frame.set_xlabel('Energy (TeV)')
|
50 |
frame.set_ylabel('Stacked - Joint Binned')
|
51 |
|
52 |
# Return
|
53 |
return
|
54 |
|
55 |
|
56 |
# ================= #
|
57 |
# Plot residual map #
|
58 |
# ================= #
|
59 |
def plot_resmap(plt1, plt2, map, error, steps=1): |
60 |
"""
|
61 |
Plot pull histogram
|
62 |
|
63 |
Parameters
|
64 |
----------
|
65 |
plt1 : pyplot
|
66 |
Frame for first map
|
67 |
plt2 : pyplot
|
68 |
Frame for second map
|
69 |
map : `~gammalib.GSkyMap()`
|
70 |
Sky map
|
71 |
error : `~gammalib.GSkyMap()`
|
72 |
Sky map
|
73 |
steps : int
|
74 |
Number of steps for rebinning
|
75 |
"""
|
76 |
# Create longitude array from skymap
|
77 |
x_lon = [] |
78 |
y_lon = [] |
79 |
e_lon = [] |
80 |
for ix in range(0, map.nx(), steps): |
81 |
value = 0.0
|
82 |
err = 0.0
|
83 |
for istep in range(steps): |
84 |
if istep+ix < map.nx(): |
85 |
for iy in range(map.ny()): |
86 |
index = istep+ix+iy*map.nx()
|
87 |
value += map[index]
|
88 |
err += error[index]*error[index] |
89 |
x_lon.append(ix) |
90 |
y_lon.append(value) |
91 |
e_lon.append(math.sqrt(err)) |
92 |
|
93 |
# Create latitude array from skymap
|
94 |
x_lat = [] |
95 |
y_lat = [] |
96 |
e_lat = [] |
97 |
for iy in range(0, map.ny(), steps): |
98 |
value = 0.0
|
99 |
err = 0.0
|
100 |
for istep in range(steps): |
101 |
if istep+iy < map.ny(): |
102 |
for ix in range(map.nx()): |
103 |
index = ix+(istep+iy)*map.nx()
|
104 |
value += map[index]
|
105 |
err += error[index]*error[index] |
106 |
x_lat.append(iy) |
107 |
y_lat.append(value) |
108 |
e_lat.append(math.sqrt(err)) |
109 |
|
110 |
# Plot longitude distribution
|
111 |
plt1.errorbar(x_lon, y_lon, yerr=e_lon, |
112 |
fmt='ko', capsize=0, linewidth=2, zorder=2) |
113 |
plt1.axhline(0, color='r', linestyle='--') |
114 |
plt1.set_xlabel('Right Ascension (pixels)')
|
115 |
plt1.set_ylabel('Stacked - Joint Binned')
|
116 |
plt1.grid(True)
|
117 |
|
118 |
# Plot latitude distribution
|
119 |
plt2.errorbar(x_lat, y_lat, yerr=e_lat, |
120 |
fmt='ko', capsize=0, linewidth=2, zorder=2) |
121 |
plt2.axhline(0, color='r', linestyle='--') |
122 |
plt2.set_xlabel('Declination (pixels)')
|
123 |
plt2.set_ylabel('Stacked - Joint Binned')
|
124 |
plt2.grid(True)
|
125 |
|
126 |
# Return
|
127 |
return
|
128 |
|
129 |
|
130 |
# ======== #
|
131 |
# Plot map #
|
132 |
# ======== #
|
133 |
def plot_map(frame, map, smooth=0.0): |
134 |
"""
|
135 |
Plot Aitoff map
|
136 |
|
137 |
Parameters
|
138 |
----------
|
139 |
frame : pyplot
|
140 |
Frame for map
|
141 |
map : `~gammalib.GSkyMap()`
|
142 |
Sky map
|
143 |
smooth : float, optional
|
144 |
Map smoothing parameter (degrees)
|
145 |
"""
|
146 |
# Optionally smooth map
|
147 |
if smooth > 0.0: |
148 |
_map = map.copy()
|
149 |
_map.smooth('DISK', smooth)
|
150 |
else:
|
151 |
_map = map
|
152 |
|
153 |
# Create array from skymap
|
154 |
array = [] |
155 |
v_max = 0.0
|
156 |
for iy in range(_map.ny()): |
157 |
row = [] |
158 |
for ix in range(_map.nx()): |
159 |
index = ix+iy*_map.nx() |
160 |
value = _map[index] |
161 |
row.append(value) |
162 |
array.append(row) |
163 |
|
164 |
# Show Aitoff projection
|
165 |
c = frame.imshow(array, aspect=1.0)
|
166 |
cbar = plt.colorbar(c, orientation='vertical', shrink=0.7) |
167 |
frame.grid(True)
|
168 |
frame.set_xlabel('Right Ascension (pixels)')
|
169 |
frame.set_ylabel('Declination (pixels)')
|
170 |
|
171 |
# Return
|
172 |
return
|
173 |
|
174 |
|
175 |
# =========================== #
|
176 |
# Determine spatial residuals #
|
177 |
# =========================== #
|
178 |
def residual_spatial(modcube1, modcube2): |
179 |
"""
|
180 |
Determine spatial residuals
|
181 |
|
182 |
Parameters
|
183 |
----------
|
184 |
modcube1 : `~gammalib.GCTAEventCube`
|
185 |
First model cube
|
186 |
modcube2 : `~gammalib.GCTAEventCube`
|
187 |
Second model cube
|
188 |
|
189 |
Returns
|
190 |
-------
|
191 |
map, error : `~gammalib.GSkyMap()`
|
192 |
Residual sky map and error
|
193 |
"""
|
194 |
# Get model cube maps
|
195 |
map1 = modcube1.counts() |
196 |
map2 = modcube2.counts() |
197 |
|
198 |
# Generate residual map
|
199 |
map = map1 - map2 |
200 |
map.stack_maps()
|
201 |
|
202 |
# Generate residual error bars
|
203 |
error = map2.copy() |
204 |
error.stack_maps() |
205 |
error = error.sqrt() |
206 |
|
207 |
# Return residual map and error map
|
208 |
return map, error |
209 |
|
210 |
|
211 |
# ============================ #
|
212 |
# Determine spectral residuals #
|
213 |
# ============================ #
|
214 |
def residual_spectral(modcube1, modcube2): |
215 |
"""
|
216 |
Determine spectral residuals
|
217 |
|
218 |
Parameters
|
219 |
----------
|
220 |
modcube1 : `~gammalib.GCTAEventCube`
|
221 |
First model cube
|
222 |
modcube2 : `~gammalib.GCTAEventCube`
|
223 |
Second model cube
|
224 |
|
225 |
Returns
|
226 |
-------
|
227 |
map, error : list
|
228 |
Residual vector
|
229 |
"""
|
230 |
# Get model cube maps
|
231 |
map1 = modcube1.counts() |
232 |
map2 = modcube2.counts() |
233 |
|
234 |
# Compute spectra
|
235 |
spec1 = [] |
236 |
spec2 = [] |
237 |
residual = [] |
238 |
error = [] |
239 |
energies = [] |
240 |
for ieng in range(map1.nmaps()): |
241 |
sum1 = 0.0
|
242 |
sum2 = 0.0
|
243 |
for ipix in range(map1.npix()): |
244 |
sum1 += map1[ipix, ieng] |
245 |
sum2 += map2[ipix, ieng] |
246 |
spec1.append(sum1) |
247 |
spec2.append(sum2) |
248 |
residual.append(sum1 - sum2) |
249 |
error.append(math.sqrt(sum2)) |
250 |
energies.append(modcube2.energy(ieng).TeV()) |
251 |
|
252 |
# Return residual and error
|
253 |
return energies, residual, error
|
254 |
|
255 |
|
256 |
# ================= #
|
257 |
# Create model cube #
|
258 |
# ================= #
|
259 |
def create_modcube(obs, ebins=20, edisp=False): |
260 |
"""
|
261 |
Create model cube
|
262 |
|
263 |
Parameters
|
264 |
----------
|
265 |
obs : `~gammalib.GObservations`
|
266 |
Observation container
|
267 |
ebins : int, optional
|
268 |
Number of energy bins for binned or stacked analysis
|
269 |
edisp : boolean, optional
|
270 |
Use energy dispersion
|
271 |
"""
|
272 |
# Create model cube
|
273 |
ctmodel = ctools.ctmodel(obs) |
274 |
ctmodel['edisp'] = edisp
|
275 |
ctmodel['incube'] = 'NONE' |
276 |
ctmodel['ebinalg'] = 'LOG' |
277 |
ctmodel['emin'] = 0.67 |
278 |
ctmodel['emax'] = 30.0 |
279 |
ctmodel['enumbins'] = ebins
|
280 |
ctmodel['coordsys'] = 'CEL' |
281 |
ctmodel['proj'] = 'CAR' |
282 |
ctmodel['xref'] = 83.63 |
283 |
ctmodel['yref'] = 22.01 |
284 |
ctmodel['nxpix'] = 50 |
285 |
ctmodel['nypix'] = 50 |
286 |
ctmodel['binsz'] = 0.02 |
287 |
#ctmodel['debug'] = True
|
288 |
ctmodel.run() |
289 |
#print(ctmodel.cube())
|
290 |
|
291 |
# Return model cube
|
292 |
return ctmodel.cube().copy()
|
293 |
|
294 |
|
295 |
# ========================== #
|
296 |
# Create stacked observation #
|
297 |
# ========================== #
|
298 |
def create_stacked(obs, ebins=20, edisp=False, enumbins=300): |
299 |
"""
|
300 |
Create stacked observation
|
301 |
|
302 |
Parameters
|
303 |
----------
|
304 |
obs : `~gammalib.GObservations`
|
305 |
Observation container
|
306 |
ebins : int, optional
|
307 |
Number of energy bins for binned or stacked analysis
|
308 |
edisp : boolean, optional
|
309 |
Use energy dispersion
|
310 |
"""
|
311 |
# Create counts cube
|
312 |
ctbin = ctools.ctbin(obs) |
313 |
ctbin['stack'] = True |
314 |
ctbin['ebinalg'] = 'LOG' |
315 |
ctbin['emin'] = 0.67 |
316 |
ctbin['emax'] = 30.0 |
317 |
ctbin['enumbins'] = ebins
|
318 |
ctbin['coordsys'] = 'CEL' |
319 |
ctbin['proj'] = 'CAR' |
320 |
ctbin['xref'] = 83.63 |
321 |
ctbin['yref'] = 22.01 |
322 |
ctbin['nxpix'] = 50 |
323 |
ctbin['nypix'] = 50 |
324 |
ctbin['binsz'] = 0.02 |
325 |
ctbin.run() |
326 |
|
327 |
# Create exposure cube
|
328 |
ctexpcube = ctools.ctexpcube(obs) |
329 |
ctexpcube['incube'] = 'NONE' |
330 |
ctexpcube['ebinalg'] = 'LOG' |
331 |
ctexpcube['emin'] = 0.1 |
332 |
ctexpcube['emax'] = 100.0 |
333 |
ctexpcube['enumbins'] = enumbins
|
334 |
ctexpcube['coordsys'] = 'CEL' |
335 |
ctexpcube['proj'] = 'CAR' |
336 |
ctexpcube['xref'] = 83.63 |
337 |
ctexpcube['yref'] = 22.01 |
338 |
ctexpcube['nxpix'] = 50 |
339 |
ctexpcube['nypix'] = 50 |
340 |
ctexpcube['binsz'] = 0.02 |
341 |
ctexpcube.run() |
342 |
|
343 |
# Create PSF cube
|
344 |
ctpsfcube = ctools.ctpsfcube(obs) |
345 |
ctpsfcube['incube'] = 'NONE' |
346 |
ctpsfcube['ebinalg'] = 'LOG' |
347 |
ctpsfcube['emin'] = 0.1 |
348 |
ctpsfcube['emax'] = 100.0 |
349 |
ctpsfcube['enumbins'] = enumbins
|
350 |
ctpsfcube['coordsys'] = 'CEL' |
351 |
ctpsfcube['proj'] = 'CAR' |
352 |
ctpsfcube['xref'] = 83.63 |
353 |
ctpsfcube['yref'] = 22.01 |
354 |
ctpsfcube['nxpix'] = 35 |
355 |
ctpsfcube['nypix'] = 25 |
356 |
ctpsfcube['binsz'] = 0.2 |
357 |
ctpsfcube['amax'] = 0.7 |
358 |
ctpsfcube['anumbins'] = 300 |
359 |
ctpsfcube.run() |
360 |
|
361 |
# Create energy dispersion cube
|
362 |
if edisp:
|
363 |
ctedispcube = ctools.ctedispcube(obs) |
364 |
ctedispcube['incube'] = 'NONE' |
365 |
ctedispcube['ebinalg'] = 'LOG' |
366 |
ctedispcube['emin'] = 0.1 # Full energy range |
367 |
ctedispcube['emax'] = 100.0 # Full energy range |
368 |
ctedispcube['enumbins'] = enumbins
|
369 |
ctedispcube['coordsys'] = 'CEL' |
370 |
ctedispcube['proj'] = 'CAR' |
371 |
ctedispcube['xref'] = 83.63 |
372 |
ctedispcube['yref'] = 22.01 |
373 |
ctedispcube['nxpix'] = 35 |
374 |
ctedispcube['nypix'] = 25 |
375 |
ctedispcube['binsz'] = 0.2 |
376 |
ctedispcube['migramax'] = 5.0 |
377 |
ctedispcube['migrabins'] = 300 |
378 |
ctedispcube.run() |
379 |
|
380 |
# Create dummy background cube
|
381 |
bkgcube = gammalib.GCTACubeBackground() |
382 |
|
383 |
# Get stacked observation
|
384 |
obs_stacked = ctbin.obs().copy() |
385 |
|
386 |
# Build stacked observation
|
387 |
if edisp:
|
388 |
obs_stacked[0].response(ctexpcube.expcube(),
|
389 |
ctpsfcube.psfcube(), |
390 |
ctedispcube.edispcube(), |
391 |
bkgcube) |
392 |
else:
|
393 |
obs_stacked[0].response(ctexpcube.expcube(),
|
394 |
ctpsfcube.psfcube(), |
395 |
bkgcube) |
396 |
|
397 |
# Return stacked observation
|
398 |
return obs_stacked
|
399 |
|
400 |
|
401 |
# =============== #
|
402 |
# Show spill over #
|
403 |
# =============== #
|
404 |
def show_spill_over(obsname, modelname, srcname, ebins=20, edisp=False): |
405 |
"""
|
406 |
"""
|
407 |
# Load observations
|
408 |
obs_unbinned = gammalib.GObservations(obsname) |
409 |
|
410 |
# Create stacked observation
|
411 |
obs_stacked = create_stacked(obs_unbinned, ebins=ebins, edisp=edisp) |
412 |
#print(obs_stacked)
|
413 |
#print(obs_stacked[0].response())
|
414 |
|
415 |
# Setup model
|
416 |
models = gammalib.GModels() |
417 |
models_full = gammalib.GModels(modelname) |
418 |
models.append(models_full[srcname]) |
419 |
|
420 |
# Append model to observations
|
421 |
obs_unbinned.models(models) |
422 |
obs_stacked.models(models) |
423 |
|
424 |
# Compute model cubes
|
425 |
modcube_unbinned = create_modcube(obs_unbinned, ebins=ebins, edisp=edisp) |
426 |
modcube_stacked = create_modcube(obs_stacked, ebins=ebins, edisp=edisp) |
427 |
|
428 |
# Create residual map
|
429 |
resmap, errmap = residual_spatial(modcube_stacked, modcube_unbinned) |
430 |
energies, resspec, errspec = residual_spectral(modcube_stacked, modcube_unbinned) |
431 |
|
432 |
# Create figure
|
433 |
fig = plt.figure(figsize=(14,5)) |
434 |
|
435 |
# Create subplot for residual spectrum
|
436 |
frame1 = fig.add_subplot(131)
|
437 |
plot_resspec(frame1, energies, resspec, errspec) |
438 |
|
439 |
# Create subplots for residual profiles
|
440 |
frame2 = fig.add_subplot(232)
|
441 |
frame3 = fig.add_subplot(235)
|
442 |
plot_resmap(frame2, frame3, resmap, errmap) |
443 |
|
444 |
# Create subplot for residual map
|
445 |
frame4 = fig.add_subplot(133)
|
446 |
plot_map(frame4, resmap, smooth=0.0)
|
447 |
|
448 |
# Show figure
|
449 |
plt.show() |
450 |
|
451 |
# Return
|
452 |
return
|
453 |
|
454 |
|
455 |
# ======================== #
|
456 |
# Main routine entry point #
|
457 |
# ======================== #
|
458 |
if __name__ == '__main__': |
459 |
|
460 |
# Check if $HESSDATA has been set
|
461 |
if 'HESSDATA' not in os.environ: |
462 |
print('HESSDATA environment variable not set. Please set HESSDATA to '
|
463 |
'the root directory of the H.E.S.S. data.')
|
464 |
exit(1) |
465 |
|
466 |
# Set parameters
|
467 |
obsname = 'obs_crab_selected.xml'
|
468 |
modelname = 'crab_results_ptsrc_plaw_gauss_grad_hess.xml'
|
469 |
srcname = 'Crab'
|
470 |
|
471 |
# Show spill over
|
472 |
show_spill_over(obsname, modelname, srcname, ebins=40, edisp=False) |