show_model_mc.py
1 |
#! /usr/bin/env python
|
---|---|
2 |
# ==========================================================================
|
3 |
# Show simulation - model for a given 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 |
from cscripts import obsutils |
27 |
import matplotlib.pyplot as plt |
28 |
|
29 |
|
30 |
# ====================== #
|
31 |
# Plot residual spectrum #
|
32 |
# ====================== #
|
33 |
def plot_resspec(frame, energies, resspec, errspec, emin=0.1, emax=100.0): |
34 |
"""
|
35 |
Plot residual spectrum
|
36 |
|
37 |
Parameters
|
38 |
----------
|
39 |
frame : pyplot
|
40 |
Frame for spectrum
|
41 |
"""
|
42 |
# Set axes scales and limit
|
43 |
frame.set_xscale('log')
|
44 |
frame.set_xlim([emin, emax]) |
45 |
|
46 |
# Counts and model
|
47 |
frame.errorbar(energies, resspec, yerr=errspec, |
48 |
fmt='ko', capsize=0, linewidth=2, zorder=2, label='Data') |
49 |
frame.plot(frame.get_xlim(),[0,0], 'r-') |
50 |
frame.set_xlabel('Energy (TeV)')
|
51 |
frame.set_ylabel('Simulation - Model')
|
52 |
|
53 |
# Return
|
54 |
return
|
55 |
|
56 |
|
57 |
# ================= #
|
58 |
# Plot residual map #
|
59 |
# ================= #
|
60 |
def plot_resmap(plt1, plt2, map, error, steps=1): |
61 |
"""
|
62 |
Plot pull histogram
|
63 |
|
64 |
Parameters
|
65 |
----------
|
66 |
plt1 : pyplot
|
67 |
Frame for first map
|
68 |
plt2 : pyplot
|
69 |
Frame for second map
|
70 |
map : `~gammalib.GSkyMap()`
|
71 |
Sky map
|
72 |
error : `~gammalib.GSkyMap()`
|
73 |
Sky map
|
74 |
steps : int
|
75 |
Number of steps for rebinning
|
76 |
"""
|
77 |
# Create longitude array from skymap
|
78 |
x_lon = [] |
79 |
y_lon = [] |
80 |
e_lon = [] |
81 |
for ix in range(0, map.nx(), steps): |
82 |
value = 0.0
|
83 |
err = 0.0
|
84 |
for istep in range(steps): |
85 |
if istep+ix < map.nx(): |
86 |
for iy in range(map.ny()): |
87 |
index = istep+ix+iy*map.nx()
|
88 |
value += map[index]
|
89 |
err += error[index]*error[index] |
90 |
x_lon.append(ix) |
91 |
y_lon.append(value) |
92 |
e_lon.append(math.sqrt(err)) |
93 |
|
94 |
# Create latitude array from skymap
|
95 |
x_lat = [] |
96 |
y_lat = [] |
97 |
e_lat = [] |
98 |
for iy in range(0, map.ny(), steps): |
99 |
value = 0.0
|
100 |
err = 0.0
|
101 |
for istep in range(steps): |
102 |
if istep+iy < map.ny(): |
103 |
for ix in range(map.nx()): |
104 |
index = ix+(istep+iy)*map.nx()
|
105 |
value += map[index]
|
106 |
err += error[index]*error[index] |
107 |
x_lat.append(iy) |
108 |
y_lat.append(value) |
109 |
e_lat.append(math.sqrt(err)) |
110 |
|
111 |
# Plot longitude distribution
|
112 |
plt1.errorbar(x_lon, y_lon, yerr=e_lon, |
113 |
fmt='ko', capsize=0, linewidth=2, zorder=2) |
114 |
plt1.axhline(0, color='r', linestyle='--') |
115 |
plt1.set_xlabel('Right Ascension (pixels)')
|
116 |
plt1.set_ylabel('Simulation - Model')
|
117 |
plt1.grid(True)
|
118 |
|
119 |
# Plot latitude distribution
|
120 |
plt2.errorbar(x_lat, y_lat, yerr=e_lat, |
121 |
fmt='ko', capsize=0, linewidth=2, zorder=2) |
122 |
plt2.axhline(0, color='r', linestyle='--') |
123 |
plt2.set_xlabel('Declination (pixels)')
|
124 |
plt2.set_ylabel('Simulation - Model')
|
125 |
plt2.grid(True)
|
126 |
|
127 |
# Return
|
128 |
return
|
129 |
|
130 |
|
131 |
# ======== #
|
132 |
# Plot map #
|
133 |
# ======== #
|
134 |
def plot_map(frame, map, smooth=0.0): |
135 |
"""
|
136 |
Plot Aitoff map
|
137 |
|
138 |
Parameters
|
139 |
----------
|
140 |
frame : pyplot
|
141 |
Frame for map
|
142 |
map : `~gammalib.GSkyMap()`
|
143 |
Sky map
|
144 |
smooth : float, optional
|
145 |
Map smoothing parameter (degrees)
|
146 |
"""
|
147 |
# Optionally smooth map
|
148 |
if smooth > 0.0: |
149 |
_map = map.copy()
|
150 |
_map.smooth('DISK', smooth)
|
151 |
else:
|
152 |
_map = map
|
153 |
|
154 |
# Create array from skymap
|
155 |
array = [] |
156 |
v_max = 0.0
|
157 |
for iy in range(_map.ny()): |
158 |
row = [] |
159 |
for ix in range(_map.nx()): |
160 |
index = ix+iy*_map.nx() |
161 |
value = _map[index] |
162 |
row.append(value) |
163 |
array.append(row) |
164 |
|
165 |
# Show Aitoff projection
|
166 |
c = frame.imshow(array, aspect=1.0)
|
167 |
cbar = plt.colorbar(c, orientation='vertical', shrink=0.7) |
168 |
frame.grid(True)
|
169 |
frame.set_xlabel('Right Ascension (pixels)')
|
170 |
frame.set_ylabel('Declination (pixels)')
|
171 |
|
172 |
# Return
|
173 |
return
|
174 |
|
175 |
|
176 |
# =========================== #
|
177 |
# Determine spatial residuals #
|
178 |
# =========================== #
|
179 |
def residual_spatial(countscube, modelcube): |
180 |
"""
|
181 |
Determine spatial residuals
|
182 |
|
183 |
Parameters
|
184 |
----------
|
185 |
countscube : `~gammalib.GCTAEventCube`
|
186 |
Simulated counts cube
|
187 |
modelcube : `~gammalib.GCTAEventCube`
|
188 |
Model cube
|
189 |
|
190 |
Returns
|
191 |
-------
|
192 |
map, error : `~gammalib.GSkyMap()`
|
193 |
Residual sky map and error
|
194 |
"""
|
195 |
# Get model cube maps
|
196 |
map1 = countscube.counts() |
197 |
map2 = modelcube.counts() |
198 |
|
199 |
# Generate residual map
|
200 |
map = map1 - map2 |
201 |
map.stack_maps()
|
202 |
|
203 |
# Generate residual error bars
|
204 |
error = map2.copy() |
205 |
error.stack_maps() |
206 |
error = error.sqrt() |
207 |
|
208 |
# Return residual map and error map
|
209 |
return map, error |
210 |
|
211 |
|
212 |
# ============================ #
|
213 |
# Determine spectral residuals #
|
214 |
# ============================ #
|
215 |
def residual_spectral(countscube, modelcube): |
216 |
"""
|
217 |
Determine spectral residuals
|
218 |
|
219 |
Parameters
|
220 |
----------
|
221 |
countscube : `~gammalib.GCTAEventCube`
|
222 |
Simulated counts cube
|
223 |
modelcube : `~gammalib.GCTAEventCube`
|
224 |
Model cube
|
225 |
|
226 |
Returns
|
227 |
-------
|
228 |
map, error : list
|
229 |
Residual vector
|
230 |
"""
|
231 |
# Get model cube maps
|
232 |
map1 = countscube.counts() |
233 |
map2 = modelcube.counts() |
234 |
|
235 |
# Compute spectra
|
236 |
spec1 = [] |
237 |
spec2 = [] |
238 |
residual = [] |
239 |
error = [] |
240 |
energies = [] |
241 |
for ieng in range(map1.nmaps()): |
242 |
sum1 = 0.0
|
243 |
sum2 = 0.0
|
244 |
for ipix in range(map1.npix()): |
245 |
sum1 += map1[ipix, ieng] |
246 |
sum2 += map2[ipix, ieng] |
247 |
spec1.append(sum1) |
248 |
spec2.append(sum2) |
249 |
residual.append(sum1 - sum2) |
250 |
error.append(math.sqrt(sum2)) |
251 |
energies.append(modelcube.energy(ieng).TeV()) |
252 |
|
253 |
# Return residual and error
|
254 |
return energies, residual, error
|
255 |
|
256 |
|
257 |
# ==================== #
|
258 |
# Simulate counts cube #
|
259 |
# ==================== #
|
260 |
def simulate_counts_cube(obs, ra=83.63, dec=22.01, npix=50, binsz=0.02, |
261 |
emin=0.1, emax=100.0, ebins=20, seed=1, edisp=False): |
262 |
"""
|
263 |
Simulate counts cube
|
264 |
|
265 |
Parameters
|
266 |
----------
|
267 |
obs : `~gammalib.GObservations`
|
268 |
Observation container
|
269 |
ebins : int, optional
|
270 |
Number of energy bins for binned or stacked analysis
|
271 |
edisp : boolean, optional
|
272 |
Use energy dispersion
|
273 |
"""
|
274 |
# Simulate events
|
275 |
ctobssim = ctools.ctobssim(obs) |
276 |
ctobssim['seed'] = seed
|
277 |
ctobssim['edisp'] = edisp
|
278 |
ctobssim.run() |
279 |
|
280 |
# Create counts cube
|
281 |
ctbin = ctools.ctbin(ctobssim.obs()) |
282 |
ctbin['stack'] = True |
283 |
ctbin['ebinalg'] = 'LOG' |
284 |
ctbin['emin'] = emin
|
285 |
ctbin['emax'] = emax
|
286 |
ctbin['enumbins'] = ebins
|
287 |
ctbin['coordsys'] = 'CEL' |
288 |
ctbin['proj'] = 'CAR' |
289 |
ctbin['xref'] = ra
|
290 |
ctbin['yref'] = dec
|
291 |
ctbin['nxpix'] = npix
|
292 |
ctbin['nypix'] = npix
|
293 |
ctbin['binsz'] = binsz
|
294 |
ctbin.run() |
295 |
|
296 |
# Return counts cube
|
297 |
return ctbin.cube().copy()
|
298 |
|
299 |
|
300 |
# ================= #
|
301 |
# Create model cube #
|
302 |
# ================= #
|
303 |
def create_model_cube(obs, countscube, edisp=False): |
304 |
"""
|
305 |
Create model cube
|
306 |
|
307 |
Parameters
|
308 |
----------
|
309 |
obs : `~gammalib.GObservations`
|
310 |
Observation container
|
311 |
countscube : `~gammalib.GCTAEventCube`
|
312 |
Counts cube for binning
|
313 |
edisp : boolean, optional
|
314 |
Use energy dispersion
|
315 |
"""
|
316 |
# Create model cube
|
317 |
ctmodel = ctools.ctmodel(obs) |
318 |
ctmodel.cube(countscube) |
319 |
ctmodel['edisp'] = edisp
|
320 |
ctmodel.run() |
321 |
|
322 |
# Return model cube
|
323 |
return ctmodel.cube().copy()
|
324 |
|
325 |
|
326 |
# ================= #
|
327 |
# Show model and MC #
|
328 |
# ================= #
|
329 |
def show_model_mc(modelname, duration=180000.0, caldb='prod2', irf='South_50h', |
330 |
ebins=20, edisp=False): |
331 |
"""
|
332 |
"""
|
333 |
# Set pointing direction
|
334 |
pntdir = gammalib.GSkyDir() |
335 |
pntdir.radec_deg(83.63, 22.01) |
336 |
|
337 |
# Set single CTA observation
|
338 |
run = obsutils.set_obs(pntdir, duration=duration, caldb=caldb, irf=irf) |
339 |
|
340 |
# Set observation container
|
341 |
obs = gammalib.GObservations() |
342 |
obs.append(run) |
343 |
|
344 |
# Load and append model
|
345 |
models = gammalib.GModels(modelname) |
346 |
obs.models(models) |
347 |
|
348 |
# Create simulated counts cube
|
349 |
countscube = simulate_counts_cube(obs, npix=400, binsz=0.002, ebins=ebins, |
350 |
edisp=edisp) |
351 |
print(countscube) |
352 |
|
353 |
# Create model cube
|
354 |
modelcube = create_model_cube(obs, countscube, edisp=edisp) |
355 |
print(modelcube) |
356 |
|
357 |
# Create residual map and spectrum
|
358 |
resmap, errmap = residual_spatial(countscube, modelcube) |
359 |
energies, resspec, errspec = residual_spectral(countscube, modelcube) |
360 |
|
361 |
# Create figure
|
362 |
fig = plt.figure(figsize=(14,5)) |
363 |
|
364 |
# Create subplot for residual spectrum
|
365 |
frame1 = fig.add_subplot(131)
|
366 |
plot_resspec(frame1, energies, resspec, errspec, emin=0.05, emax=150.0) |
367 |
|
368 |
# Create subplots for residual profiles
|
369 |
frame2 = fig.add_subplot(232)
|
370 |
frame3 = fig.add_subplot(235)
|
371 |
plot_resmap(frame2, frame3, resmap, errmap, steps=5)
|
372 |
|
373 |
# Create subplot for residual map
|
374 |
frame4 = fig.add_subplot(133)
|
375 |
plot_map(frame4, resmap, smooth=0.02)
|
376 |
|
377 |
# Show figure
|
378 |
plt.show() |
379 |
|
380 |
# Return
|
381 |
return
|
382 |
|
383 |
|
384 |
# ======================== #
|
385 |
# Main routine entry point #
|
386 |
# ======================== #
|
387 |
if __name__ == '__main__': |
388 |
|
389 |
# Set model name
|
390 |
modelname = 'crab.xml'
|
391 |
|
392 |
# Show model and MC
|
393 |
#show_model_mc(modelname, ebins=50, edisp=False)
|
394 |
show_model_mc(modelname, ebins=50, edisp=True) |