9
9
import copy
10
10
11
11
class SourceInjector ():
12
-
12
+
13
13
def __init__ (self , response_path , response_frame = "spacecraftframe" ):
14
14
15
15
"""
@@ -24,23 +24,23 @@ def __init__(self, response_path, response_frame = "spacecraftframe"):
24
24
"""
25
25
26
26
self .response_path = response_path
27
-
27
+
28
28
if response_frame == "spacecraftframe" or response_frame == "galactic" :
29
-
29
+
30
30
self .response_frame = response_frame
31
-
31
+
32
32
else :
33
33
raise ValueError ("The response frame can only be `spacecraftframe` or `galactic`!" )
34
34
35
35
36
36
37
37
@staticmethod
38
38
def get_psr_in_galactic (coordinate , response_path , spectrum ):
39
-
39
+
40
40
"""
41
41
Get the point source response (psr) in galactic. Please be aware that you must use a galactic response!
42
42
To do: to make the weight parameter not hardcoded
43
-
43
+
44
44
Parameters
45
45
----------
46
46
coordinate : astropy.coordinates.SkyCoord
@@ -49,39 +49,27 @@ def get_psr_in_galactic(coordinate, response_path, spectrum):
49
49
The path to the response.
50
50
spectrum : astromodels.functions
51
51
The spectrum of the source to be placed at the hypothesis coordinate.
52
-
52
+
53
53
Returns
54
54
-------
55
55
psr : histpy.Histogram
56
56
The point source response of the spectrum at the hypothesis coordinate.
57
57
"""
58
-
59
- # Open the response
60
- # Notes from Israel: Inside it contains a single histogram with all the regular axes for a Compton Data Space (CDS) analysis, in galactic coordinates. Since there is no class yet to handle it, this is how to read in the HDF5 manually.
61
-
62
- with h5 .File (response_path ) as f :
63
58
64
- axes_group = f ['hist/axes' ]
65
- axes = []
66
- for axis in axes_group .values ():
67
- # Get class. Backwards compatible with version
68
- # with only Axis
69
- axis_cls = Axis
70
- if '__class__' in axis .attrs :
71
- class_module , class_name = axis .attrs ['__class__' ]
72
- axis_cls = getattr (sys .modules [class_module ], class_name )
73
- axes += [axis_cls ._open (axis )]
74
- axes = Axes (axes )
75
-
76
- # get the pixel number of the hypothesis coordinate
77
- map_temp = HealpixMap (base = axes [0 ])
78
- coordinate_pix_number = map_temp .ang2pix (coordinate )
79
-
80
- # get the expectation for the hypothesis coordinate (a point source)
59
+ # Open the response Histogram, but don't read the whole thing into memory --
60
+ # just get the axes, the specific contents pixel(s), and its unit
81
61
with h5 .File (response_path ) as f :
62
+
63
+ axes = Axes .open (f ['hist/axes' ])
64
+
65
+ # get the pixel number of the hypothesis coordinate
66
+ hp_axis = axes [0 ]
67
+ coordinate_pix_number = hp_axis .ang2pix (coordinate )
68
+
69
+ # get the expectation for the hypothesis coordinate (a point source)
82
70
pix = coordinate_pix_number
83
- psr = PointSourceResponse (axes [1 :], f ['hist/contents' ][pix + 1 ], unit = f ['hist' ].attrs ['unit' ])
84
-
71
+ psr = PointSourceResponse (axes [1 :], f ['hist/contents' ][pix ], unit = f ['hist' ].attrs ['unit' ])
72
+
85
73
return psr
86
74
87
75
@@ -107,26 +95,28 @@ def inject_point_source(self, spectrum, coordinate, orientation = None, source_n
107
95
The path to save the injected data to a `.h5` file. This should include the file name. (the default is `None`, which means the injected data won't be saved.
108
96
project_axes : list, optional
109
97
The axes to project before saving the data file (the default is `None`, which means the data won't be projected).
110
-
98
+
111
99
Returns
112
100
-------
113
101
histpy.Histogram
114
102
The `Histogram object of the injected spectrum.`
115
103
"""
116
-
117
-
104
+
105
+
118
106
# get the point source response in local frame
119
107
if self .response_frame == "spacecraftframe" :
120
108
121
109
if orientation == None :
122
110
raise TypeError ("The when the data are binned in spacecraftframe frame, orientation must be provided to compute the expected counts." )
123
111
112
+
113
+
124
114
with FullDetectorResponse .open (self .response_path ) as response :
125
-
115
+
126
116
scatt_map = orientation .get_scatt_map (coordinate , response .nside * 2 , coordsys = 'galactic' , earth_occ = True )
127
-
117
+
128
118
psr = response .get_point_source_response (coord = coordinate , scatt_map = scatt_map )
129
-
119
+
130
120
# get the point source response in galactic frame
131
121
elif self .response_frame == "galactic" :
132
122
@@ -223,62 +213,61 @@ def inject_extended_source(
223
213
injected .write (data_save_path )
224
214
225
215
return injected
226
-
216
+
227
217
def inject_model (self , model , orientation = None , make_spectrum_plot = False , data_save_path = None , project_axes = None ):
228
-
218
+
229
219
if self .response_frame == "spacecraftframe" :
230
220
if orientation == None :
231
221
raise TypeError ("The when the data are binned in spacecraftframe frame, orientation must be provided to compute the expected counts." )
232
-
222
+
233
223
self .components = {}
234
-
224
+
235
225
# first inject point sources
236
226
point_sources = model .point_sources
237
-
227
+
238
228
# iterate through all point sources
239
229
for name , source in point_sources .items ():
240
-
230
+
241
231
injected = self .inject_point_source (spectrum = source .spectrum .main .shape , coordinate = source .position .sky_coord ,
242
232
orientation = orientation , source_name = name )
243
-
233
+
244
234
injected .axes ["Em" ].axis_scale = "log" # set to log scale manually. This inconsistency is from the detector response module
245
-
235
+
246
236
self .components [name ] = injected
247
-
237
+
248
238
# second inject extended sources
249
239
extended_sources = model .extended_sources
250
-
240
+
251
241
# iterate through all extended sources
252
242
for name , source in extended_sources .items ():
253
-
243
+
254
244
injected = self .inject_extended_source (source_model = source , source_name = name )
255
245
self .components [name ] = injected
256
-
257
-
246
+
247
+
258
248
if len (self .components ) == 1 :
259
-
249
+
260
250
# if there is only one component, the injected all is just the only component
261
251
injected_all = list (self .components .values ())[0 ]
262
-
252
+
263
253
if data_save_path is not None :
264
254
injected_all .write (data_save_path )
265
-
255
+
266
256
elif len (self .components ) > 1 :
267
-
257
+
268
258
injected_list = list (self .components .values ())
269
-
259
+
270
260
injected_all = copy .deepcopy (injected_list [0 ])
271
-
261
+
272
262
# add the rest of the injected sources
273
263
for i in injected_list [1 :]:
274
264
injected_all += i
275
-
265
+
276
266
if make_spectrum_plot :
277
267
ax , plot = injected_all .project ("Em" ).draw (color = "green" , linewidth = 2 )
278
268
ax .set_xscale ("log" )
279
269
ax .set_yscale ("log" )
280
270
ax .set_xlabel ("Em [keV]" , fontsize = 14 , fontweight = "bold" )
281
271
ax .set_ylabel ("Counts" , fontsize = 14 , fontweight = "bold" )
282
-
272
+
283
273
return injected_all
284
-