/
wip_bg_cube_model_comparison.py
368 lines (310 loc) · 15.4 KB
/
wip_bg_cube_model_comparison.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
"""
Script to produce plots comparing 2 sets of background cube models.
Details in stringdoc of the plot_bg_cube_model_comparison function.
"""
import argparse
import numpy as np
import matplotlib.pyplot as plt
from astropy.units import Quantity
from astropy.coordinates import Angle
from astropy.table import Table
from astropy.io import ascii
from gammapy.background import FOVCubeBackgroundModel
from gammapy.data import ObservationGroups, ObservationGroupAxis
NORMALIZE = 0 # normalize 1 w.r.t. 2 (i.e. true w.r.t. reco)
# 0: do not normalize
# 1: normalize w.r.t. cube integral
# 2: normalize w.r.t images integral (normalize each image on its own)
INPUT_DIR1 = '/home/mapaz/astropy/working_dir/bg_cube_models_gammapy_a_la_michi'
BINNING_FORMAT1 = 'default'
NAME1 = 'gammapy'
INPUT_DIR2 = '/home/mapaz/HESS/fits_data/pa_fits_prod02/pa/Model_Deconvoluted_Prod26/Mpp_Std/background'
BINNING_FORMAT2 = 'michi'
NAME2 = 'michi'
# group IDs for comparison
# The following group IDs
# - group_ids_selection = [14, 15, 20, 21, 26, 27]
# correspond to the cartesian product of the following
# "michi" alt az bin IDs:
# - alt_bin_ids_selection = [7, 10, 13]
# - az_bin_ids_selection = [0, 1]
group_ids_selection = [14, 15, 20, 21, 26, 27]
def convert_obs_groups_binning_def_michi_to_default():
"""Convert observation groups binning definition "michi" to "default".
"""
# observation groups binning definition "michi"
# alt az bin edges definitions
altitude_edges = Angle([0, 20, 23, 27, 30, 33, 37, 40, 44, 49, 53, 58, 64, 72, 90], 'degree')
azimuth_edges = Angle([-90, 90, 270], 'degree')
# convert observation groups binning definition "michi" to "default"
list_obs_group_axis = [ObservationGroupAxis('ALT', altitude_edges, fmt='edges'),
ObservationGroupAxis('AZ', azimuth_edges, fmt='edges')]
obs_groups_michi = ObservationGroups(list_obs_group_axis)
print("Observation groups 'michi':")
print(obs_groups_michi.obs_groups_table)
# save
outfile = 'bg_observation_groups_michi.ecsv'
print('Writing {}'.format(outfile))
obs_groups_michi.write(outfile)
# lookup table: equivalences in group/file naming "defualt" <-> "michi"
# 3 columns: GROUP_ID, ALT_ID, AZ_ID
# 28 rows: 1 per GROUP_ID
lookup_obs_groups_michi = Table()
n_cols = 1 + len(list_obs_group_axis)
n_rows = obs_groups_michi.n_groups
lookup_obs_groups_michi['GROUP_ID'] = np.zeros(n_rows, dtype=np.int)
lookup_obs_groups_michi['ALT_ID'] = np.zeros(n_rows, dtype=np.int)
lookup_obs_groups_michi['AZ_ID'] = np.zeros(n_rows, dtype=np.int)
# loop over each observation group axis
count_groups = 0
for alt_id in np.arange(len(altitude_edges) - 1):
for az_id in np.arange(len(azimuth_edges) - 1):
lookup_obs_groups_michi['GROUP_ID'][count_groups] = count_groups
lookup_obs_groups_michi['ALT_ID'][count_groups] = alt_id
lookup_obs_groups_michi['AZ_ID'][count_groups] = az_id
count_groups += 1
print("lookup table:")
print(lookup_obs_groups_michi)
# save
outfile = 'lookup_obs_groups_michi.ecsv'
print('Writing {}'.format(outfile))
# `~astropy.io.ascii` always overwrites the file
ascii.write(lookup_obs_groups_michi, outfile,
format='ecsv', fast_writer=False)
def look_obs_groups_michi(group_id):
"""
Find corresponding ALT_ID, AZ_ID for a given GROUP_ID in lookup table.
Parameters
----------
group_id : int
Group ID to look for.
Returns
-------
i_alt : int
Altitude bin index corresponding to the requested group ID.
i_az : int
Azimuth bin index corresponding to the requested group ID.
"""
# read lookup
filename = 'lookup_obs_groups_michi.ecsv'
lookup_obs_groups_michi = ascii.read(filename)
# find group row in lookup table
group_ids = lookup_obs_groups_michi['GROUP_ID'].data
group_index = np.where(group_ids == group_id)
row = group_index[0][0]
i_alt = lookup_obs_groups_michi['ALT_ID'][row]
i_az = lookup_obs_groups_michi['AZ_ID'][row]
return i_alt, i_az
def plot_bg_cube_model_comparison(input_dir1, binning_format1, name1,
input_dir2, binning_format2, name2):
"""
Plot background cube model comparison.
Produce a few figures for comparing 2 sets of bg cube models (1
and 2), supposing same binning in both sets of observation
groups (a.k.a. observation bin).
Each figure corresponds to 1 observation group.
Plot strategy in each figure:
* Images:
* rows: similar energy bin
* cols: same bg cube model set
* Spectra:
* rows: similar det bin
* cols: compare both bg cube model sets
The script can be customized by setting a few global variables:
* **group_ids_selection**: groups to compare; if empty: use all
groups
* **NORMALIZE**: normalization to use for the models. If
activate, model 1 is normalized to match model 2. This can be
useful, when comparing reco models w.r.t. true ones. Options:
* *0*: do not normalize
* *1*: normalize w.r.t. cube integral
* *2*: normalize w.r.t images integral; each image (i.e.
energy bin/slice) is normalized independently; this
option can alter the spectral shape of the bg rate, but
is is the way how smoothing method *michi* normalizes the
background cube model, hence it is necessary to compare
to those models that use that particular smoothing
Parameters
----------
input_dir1, input_dir2 : str
Directory where the corresponding set of bg cube models is stored.
binning_format1, binning_format2 : {'default', 'michi'}
String specifying the binning format; accepted values are:
* *default* for the Gammapy format from
`~gammapy.data.ObservationGroups`; an observation groups
ECVS file is expected in the bg cube models dir.
* *michi* for the binning used by Michale Mayer;
this script has methods to convert it to the
*default* format.
ref: [Mayer2015]_ (section 5.2.4)
name1, name2 : str
Name to use for plot labels/legends.
"""
# check binning
accepted_binnings = ['default', 'michi']
if ((binning_format1 not in accepted_binnings) or
(binning_format2 not in accepted_binnings)):
raise ValueError("Invalid binning format: {0} or {1}".format(binning_format1,
binning_format2))
# convert binning, if necessary
if binning_format1 == 'michi' or binning_format2 == 'michi':
convert_obs_groups_binning_def_michi_to_default()
# loop over observation groups: use binning of the 1st set to compare
if binning_format1 == 'michi':
observation_groups = obs_groups_michi
else:
observation_groups = ObservationGroups.read(input_dir1 + '/bg_observation_groups.ecsv')
groups = observation_groups.list_of_groups
print()
print("list of groups", groups)
for group in groups:
print()
print("group ", group)
# compare only observation groups in group IDs selection
# if empty, use all groups:
if len(group_ids_selection) is not 0:
groups_to_compare = group_ids_selection
else:
groups_to_compare = groups
if group in groups_to_compare:
group_info = observation_groups.info_group(group)
print(group_info)
# get cubes
if binning_format1 == 'michi':
# find corresponding ALT_ID, AZ_ID in lookup table
i_alt, i_az = look_obs_groups_michi(group)
filename1 = input_dir1 + '/hist_alt' + str(i_alt) + \
'_az' + str(i_az) + '.fits.gz'
else:
filename1 = input_dir1 + '/bg_cube_model_group' + str(group) + \
'_table.fits.gz'
if binning_format2 == 'michi':
# find corresponding ALT_ID, AZ_ID in lookup table
i_alt, i_az = look_obs_groups_michi(group)
filename2 = input_dir2 + '/hist_alt' + str(i_alt) + \
'_az' + str(i_az) + '.fits.gz'
else:
filename2 = input_dir2 + '/bg_cube_model_group' + str(group) + \
'_table.fits.gz'
print('filename1', filename1)
print('filename2', filename2)
bg_cube_model1 = FOVCubeBackgroundModel.read(filename1,
format='table').background_cube
bg_cube_model2 = FOVCubeBackgroundModel.read(filename2,
format='table').background_cube
# normalize 1 w.r.t. 2 (i.e. true w.r.t. reco)
if NORMALIZE == 1:
# normalize w.r.t. cube integral
integral1 = bg_cube_model1.integral
integral2 = bg_cube_model2.integral
bg_cube_model1.data *= integral2 / integral1
elif NORMALIZE == 2:
# normalize w.r.t images integral (normalize each image on its own)
integral_images1 = bg_cube_model1.integral_images
integral_images2 = bg_cube_model2.integral_images
for i_energy in np.arange(len(bg_cube_model1.energy_edges) - 1):
bg_cube_model1.data[i_energy] *= (integral_images2 / integral_images1)[i_energy]
# compare binning
print("energy edges 1", bg_cube_model1.energy_edges)
print("energy edges 2", bg_cube_model2.energy_edges)
print("detector edges 1 Y", bg_cube_model1.coordy_edges)
print("detector edges 2 Y", bg_cube_model2.coordy_edges)
print("detector edges 1 X", bg_cube_model1.coordx_edges)
print("detector edges 2 X", bg_cube_model2.coordx_edges)
# make sure that both cubes use the same units for the plots
bg_cube_model2.data = bg_cube_model2.data.to(bg_cube_model1.data.unit)
# plot
fig, axes = plt.subplots(nrows=2, ncols=3)
fig.set_size_inches(30., 15., forward=True)
plt.suptitle(group_info)
# plot images
# rows: similar energy bin
# cols: same file
# bg_cube_model1.plot_image(energy=Quantity(0.5, 'TeV'), ax=axes[0, 0])
bg_cube_model1.plot_image(energy=Quantity(5., 'TeV'), ax=axes[0, 0])
axes[0, 0].set_title("{0}: {1}".format(name1, axes[0, 0].get_title()))
bg_cube_model1.plot_image(energy=Quantity(50., 'TeV'), ax=axes[1, 0])
axes[1, 0].set_title("{0}: {1}".format(name1, axes[1, 0].get_title()))
# bg_cube_model2.plot_image(energy=Quantity(0.5, 'TeV'), ax=axes[0, 1])
bg_cube_model2.plot_image(energy=Quantity(5., 'TeV'), ax=axes[0, 1])
axes[0, 1].set_title("{0}: {1}".format(name2, axes[0, 1].get_title()))
bg_cube_model2.plot_image(energy=Quantity(50., 'TeV'), ax=axes[1, 1])
axes[1, 1].set_title("{0}: {1}".format(name2, axes[1, 1].get_title()))
# plot spectra
# rows: similar det bin
# cols: compare both files
bg_cube_model1.plot_spectrum(coord=Angle([0., 0.], 'degree'),
ax=axes[0, 2],
style_kwargs=dict(color='blue',
label=name1))
spec_title1 = axes[0, 2].get_title()
bg_cube_model2.plot_spectrum(coord=Angle([0., 0.], 'degree'),
ax=axes[0, 2],
style_kwargs=dict(color='red',
label=name2))
spec_title2 = axes[0, 2].get_title()
if spec_title1 != spec_title2:
s_error = "Expected same det binning, but got "
s_error += "\"{0}\" and \"{1}\"".format(spec_title1, spec_title2)
raise ValueError(s_error)
else:
axes[0, 2].set_title(spec_title1)
axes[0, 2].legend()
bg_cube_model1.plot_spectrum(coord=Angle([2., 2.], 'degree'),
ax=axes[1, 2],
style_kwargs=dict(color='blue',
label=name1))
spec_title1 = axes[1, 2].get_title()
bg_cube_model2.plot_spectrum(coord=Angle([2., 2.], 'degree'),
ax=axes[1, 2],
style_kwargs=dict(color='red',
label=name2))
spec_title2 = axes[1, 2].get_title()
if spec_title1 != spec_title2:
s_error = "Expected same det binning, but got "
s_error += "\"{0}\" and \"{1}\"".format(spec_title1, spec_title2)
raise ValueError(s_error)
else:
axes[1, 2].set_title(spec_title1)
axes[1, 2].legend()
plt.draw()
# save
outfile = "bg_cube_model_comparison_group{}.png".format(group)
print('Writing {}'.format(outfile))
fig.savefig(outfile)
plt.show() # don't leave at the end
if __name__ == '__main__':
"""Main function: parse arguments and launch the whole analysis chain.
"""
parser = argparse.ArgumentParser(description='Compare 2 sets of bg cube models.')
parser.add_argument('--input-dir1', type=str,
default=INPUT_DIR1,
help='Directory where the corresponding set '
'of bg cube models is stored. '
'(default is {}).'.format(INPUT_DIR1))
parser.add_argument('--binning-format1', type=str,
default=BINNING_FORMAT1,
choices=['default', 'michi'],
help='String specifying the binning format. '
'(default is {}).'.format(BINNING_FORMAT1))
parser.add_argument('--name1', type=str,
default=NAME1,
help='Name to use for plot labels/legends. '
'(default is {}).'.format(NAME1))
parser.add_argument('--input-dir2', type=str,
default=INPUT_DIR2,
help='Directory where the corresponding set '
'of bg cube models is stored. '
'(default is {}).'.format(INPUT_DIR2))
parser.add_argument('--binning-format2', type=str,
default=BINNING_FORMAT2,
choices=['default', 'michi'],
help='String specifying the binning format. '
'(default is {}).'.format(BINNING_FORMAT2))
parser.add_argument('--name2', type=str,
default=NAME2,
help='Name to use for plot labels/legends. '
'(default is {}).'.format(NAME2))
args = parser.parse_args()
plot_bg_cube_model_comparison(args.input_dir1, args.binning_format1, args.name1,
args.input_dir2, args.binning_format2, args.name2)