Commit 63661f50eaf9b47ab13ac11dd48cec9e5f091584

Authored by Jürgen Knödlseder
1 parent 49bfb301

Test all residual map algorithms in csresmap (#1828)

cscripts/csresmap.py
... ... @@ -28,12 +28,7 @@ import ctools
28 28 # ============== #
29 29 class csresmap(ctools.cscript):
30 30 """
31   - Generates a residual map.
32   -
33   - This class implements the creation of a residual map. It derives from
34   - the ctools.cscript class which provides support for parameter files,
35   - command line arguments, and logging. In that way the Python script
36   - behaves just as a regular ctool.
  31 + Generates a residual map
37 32 """
38 33  
39 34 # Constructor
... ... @@ -42,42 +37,18 @@ class csresmap(ctools.cscript):
42 37 Constructor.
43 38 """
44 39 # Set name
45   - self._name = "csresmap"
46   - self._version = "1.1.0"
  40 + self._name = 'csresmap'
  41 + self._version = '1.1.0'
47 42  
48 43 # Initialise class members
49   - self._algorithm = "SUB"
50 44 self._resmap = None
51   - self._modcube = "NONE"
52 45 self._use_maps = False
53 46 self._skip_binning = False
54   - self._publish = False
55   - self._outfile = ""
56   - self._use_maps = False
57   - self._skip_binning = False
58   - self._edisp = False
59   - self._xref = 83.63
60   - self._yref = 22.01
61   - self._emin = 0.1
62   - self._emax = 100.0
63   - self._enumbins = 0
64   - self._ebinalg = "LOG"
65   - self._coordsys = "CEL"
66   - self._proj = "CAR"
67   - self._nxpix = 200
68   - self._nypix = 200
69   - self._binsz = 0.02
70   - self._publish = False
71   - self._chatter = 2
72   - self._clobber = True
73   - self._debug = False
74   - self._log_clients = False
75   -
76   - # Initialise observation container from constructor arguments.
  47 +
  48 + # Initialise observation container from constructor arguments
77 49 self._obs, argv = self._set_input_obs(argv)
78 50  
79   - # Initialise application by calling the appropriate class
80   - # constructor.
  51 + # Initialise application by calling the appropriate class constructor
81 52 self._init_cscript(argv)
82 53  
83 54 # Return
... ... @@ -87,25 +58,29 @@ class csresmap(ctools.cscript):
87 58 # Private methods
88 59 def _get_parameters(self):
89 60 """
90   - Get parameters from parfile and setup the observation.
  61 + Get parameters from parfile and setup the observation
91 62 """
92 63 # Initialise some flags
93 64 self._use_maps = False
94 65 self._skip_binning = False
95 66  
96   - # First check if the inobs parameter is a counts cube
97   - if self._obs.size() == 0 and self["inobs"].filename() != "NONE":
98   - filename = gammalib.GFilename(self["inobs"].filename())
  67 + # If there are no observations in the observation container then check
  68 + # if the "inobs" parameter is a counts cube. If the "inobs" parameter
  69 + # represents a FITS file and if this FITS file is a counts cube then
  70 + # set self._skip_binning=True
  71 + if self._obs.size() == 0 and self['inobs'].filename() != 'NONE':
  72 + filename = gammalib.GFilename(self['inobs'].filename())
99 73 if filename.is_fits():
100 74 cta = gammalib.GCTAObservation()
101 75 cta.load(filename)
102   - if cta.eventtype() == "CountsCube":
  76 + if cta.eventtype() == 'CountsCube':
103 77 self._skip_binning = True
104 78  
105   - # If we have a counts cube, then ask whether we also have a model
  79 + # If we have a counts cube, then ask whether we also have a model cube.
  80 + # If a model cube name is given then set self._use_maps=True
106 81 if self._skip_binning:
107   - self._modcube = self["modcube"].filename()
108   - if self._modcube != "NONE":
  82 + modcube = self['modcube'].filename()
  83 + if modcube != 'NONE':
109 84 self._use_maps = True
110 85  
111 86 # If not two maps are given, proceed to set up observation
... ... @@ -113,56 +88,51 @@ class csresmap(ctools.cscript):
113 88  
114 89 # Set observation if not done before
115 90 if self._obs.size() == 0:
116   - self._require_inobs("csresmap.get_parameters()")
  91 + self._require_inobs('csresmap.get_parameters()')
117 92 self._obs = self._get_observations()
118 93  
119   - # Check if we have exactly one binned CTA observation
120   - if self._obs.size() == 1:
121   -
122   - if self._obs[0].classname() == "GCTAObservation":
123   - if self._obs[0].eventtype() == "CountsCube":
124   - # Skip ctbin step later on
125   - self._skip_binning = True
  94 + # If we have exactly one binned CTA observation then signal that
  95 + # the binning can be skipped
  96 + if self._obs.size() == 1 and \
  97 + self._obs[0].classname() == 'GCTAObservation' and \
  98 + self._obs[0].eventtype() == 'CountsCube':
  99 + self._skip_binning = True
126 100  
127 101 # Set models if we have none
128 102 if self._obs.models().size() == 0:
129   - self._obs.models(self["inmodel"].filename())
  103 + self._obs.models(self['inmodel'].filename())
130 104  
131   - # Skip query for spatial parameters if a binning is provided in the observation
  105 + # If no binning is provided in the observation then query now
  106 + # the counts cube binning parameters
132 107 if not self._skip_binning:
133   - # Read other parameters
134   - self._xref = self["xref"].real()
135   - self._yref = self["yref"].real()
136   - self._emin = self["emin"].real()
137   - self._emax = self["emax"].real()
138   - self._enumbins = self["enumbins"].integer()
139   - self._ebinalg = self["ebinalg"].string()
140   - self._coordsys = self["coordsys"].string()
141   - self._proj = self["proj"].string()
142   - self._nxpix = self["nxpix"].integer()
143   - self._nypix = self["nypix"].integer()
144   - self._binsz = self["binsz"].real()
  108 + self['xref'].real()
  109 + self['yref'].real()
  110 + self['emin'].real()
  111 + self['emax'].real()
  112 + self['enumbins'].integer()
  113 + self['ebinalg'].string()
  114 + self['coordsys'].string()
  115 + self['proj'].string()
  116 + self['nxpix'].integer()
  117 + self['nypix'].integer()
  118 + self['binsz'].real()
145 119  
146   - # Read energy dispersion flag
147   - self._edisp = self["edisp"].boolean()
148   -
149   - # Read algorithm
150   - self._algorithm = self["algorithm"].string()
151   -
152   - # Read standard parameters
153   - self._publish = self["publish"].boolean()
154   - self._chatter = self["chatter"].integer()
155   - self._clobber = self["clobber"].boolean()
156   - self._debug = self["debug"].boolean()
157   -
158   - # Read ahead output parameters
  120 + # Query parameters
  121 + self['edisp'].boolean()
  122 + self['algorithm'].string()
  123 + self['publish'].boolean()
  124 + self['chatter'].integer()
  125 + self['clobber'].boolean()
  126 + self['debug'].boolean()
  127 +
  128 + # Query ahead output parameters
159 129 if (self._read_ahead()):
160   - self["outmap"].filename()
  130 + self['outmap'].filename()
161 131  
162 132 # Write input parameters into logger
163 133 if self._logTerse():
164 134 self._log_parameters()
165   - self._log("\n")
  135 + self._log('\n')
166 136  
167 137 # Return
168 138 return
... ... @@ -171,7 +141,7 @@ class csresmap(ctools.cscript):
171 141 # Public methods
172 142 def run(self):
173 143 """
174   - Run the script.
  144 + Run the script
175 145 """
176 146 # Switch screen logging on in debug mode
177 147 if self._logDebug():
... ... @@ -182,46 +152,48 @@ class csresmap(ctools.cscript):
182 152  
183 153 # Write observation into logger
184 154 if self._logTerse():
185   - self._log("\n")
186   - self._log.header1("Observation")
  155 + self._log('\n')
  156 + self._log.header1(gammalib.number('Observation',len(self._obs)))
187 157 self._log(str(self._obs))
188   - self._log("\n")
  158 + self._log('\n')
189 159  
190   - # Use input file directly if given
  160 + # If a counts and model cube are specified then load them as sky map
191 161 if self._use_maps:
192   - countmap = gammalib.GSkyMap(self["inobs"].filename())
193   - modelmap = gammalib.GSkyMap(self._modcube)
  162 + countmap = gammalib.GSkyMap(self['inobs'].filename())
  163 + modelmap = gammalib.GSkyMap(self['modcube'].filename())
194 164  
  165 + # ... otherwise build a counts cube and model cube
195 166 else:
196 167  
197   - # ...
  168 + # Do not build counts cube if we have already one in the observation
  169 + # container
198 170 if self._skip_binning:
199   - cta_counts_cube = gammalib.GCTAEventCube(self._obs[0].events().clone())
  171 + cta_counts_cube = gammalib.GCTAEventCube(self._obs[0].events())
200 172  
201   - # ...
  173 + # ... otherwise generate one now from the event list
202 174 else:
203 175  
204 176 # Write header
205 177 if self._logTerse():
206   - self._log("\n")
207   - self._log.header1("Generate binned map (ctbin)")
  178 + self._log('\n')
  179 + self._log.header1('Generate binned map (ctbin)')
208 180  
209 181 # Create countsmap
210 182 bin = ctools.ctbin(self._obs)
211   - bin["nxpix"].integer(self._nxpix)
212   - bin["nypix"].integer(self._nypix)
213   - bin["proj"].string(self._proj)
214   - bin["coordsys"].string(self._coordsys)
215   - bin["xref"].real(self._xref)
216   - bin["yref"].real(self._yref)
217   - bin["enumbins"].integer(self._enumbins)
218   - bin["ebinalg"].string(self._ebinalg)
219   - bin["emin"].real(self._emin)
220   - bin["emax"].real(self._emax)
221   - bin["binsz"].real(self._binsz)
222   - bin["chatter"].integer(self._chatter)
223   - bin["clobber"].boolean(self._clobber)
224   - bin["debug"].boolean(self._debug)
  183 + bin['nxpix'] = self['nxpix'].integer()
  184 + bin['nypix'] = self['nypix'].integer()
  185 + bin['proj'] = self['proj'].string()
  186 + bin['coordsys'] = self['coordsys'].string()
  187 + bin['xref'] = self['xref'].real()
  188 + bin['yref'] = self['yref'].real()
  189 + bin['enumbins'] = self['enumbins'].integer()
  190 + bin['ebinalg'] = self['ebinalg'].string()
  191 + bin['emin'] = self['emin'].real()
  192 + bin['emax'] = self['emax'].real()
  193 + bin['binsz'] = self['binsz'].real()
  194 + bin['chatter'] = self['chatter'].integer()
  195 + bin['clobber'] = self['clobber'].boolean()
  196 + bin['debug'] = self['debug'].boolean()
225 197 bin.run()
226 198  
227 199 # Retrieve counts cube
... ... @@ -232,63 +204,51 @@ class csresmap(ctools.cscript):
232 204  
233 205 # Write header
234 206 if self._logTerse():
235   - self._log("\n")
236   - self._log.header1("Generate model map (ctmodel)")
  207 + self._log('\n')
  208 + self._log.header1('Generate model map (ctmodel)')
237 209  
238 210 # Create model map
239 211 model = ctools.ctmodel(self._obs)
240 212 model.cube(cta_counts_cube)
241   - model["chatter"].integer(self._chatter)
242   - model["clobber"].boolean(self._clobber)
243   - model["debug"].boolean(self._debug)
244   - model["edisp"].boolean(self._edisp)
  213 + model['chatter'] = self['chatter'].integer()
  214 + model['clobber'] = self['clobber'].boolean()
  215 + model['debug'] = self['debug'].boolean()
  216 + model['edisp'] = self['edisp'].boolean()
245 217 model.run()
246 218  
247 219 # Get model map into GSkyMap object
248 220 modelmap = model.cube().counts().copy()
249 221  
250   - # Store counts map as residual map. Note that we need a
251   - # special construct here to avoid memory leaks. This seems
252   - # to be a SWIG feature as SWIG creates a new object when
253   - # calling bin.cube()
254   - #residualmap = bin.cube().counts()
  222 + # Store counts map as residual map. Note that we need a special
  223 + # construct here to avoid memory leaks. This seems to be a SWIG feature
  224 + # as SWIG creates a new object when calling bin.cube()
255 225 self._resmap = countmap.copy()
256 226 self._resmap.stack_maps()
257 227 modelmap.stack_maps()
258 228  
259   - # Continue calculations depending on given algorithm
260   - if self._algorithm == "SUB":
  229 + # Get residual map algorithm type
  230 + algorithm = self['algorithm'].string()
261 231  
262   - # Subtract maps
  232 + # Subtract maps
  233 + if algorithm == 'SUB':
263 234 self._resmap -= modelmap
264 235  
265   - elif self._algorithm == "SUBDIV":
266   -
267   - # Subtract and divide by model map
  236 + # Subtract and divide by model map
  237 + elif algorithm == 'SUBDIV':
268 238 self._resmap -= modelmap
269 239 self._resmap /= modelmap
270   - #for pixel in modelmap:
271   - # if pixel != 0.0:
272   - # pixel = 1.0/pixel
273   - #self._resmap *= modelmap
274 240  
275   - elif self._algorithm == "SUBDIVSQRT":
276   -
277   - # subtract and divide by sqrt of model map
  241 + # Subtract and divide by sqrt of model map
  242 + elif algorithm == 'SUBDIVSQRT':
278 243 self._resmap -= modelmap
279 244 self._resmap /= modelmap.sqrt()
280   - #for pixel in modelmap:
281   - # if pixel != 0.0:
282   - # pixel = 1.0/math.sqrt(pixel)
283   - #self._resmap *= modelmap
284 245  
  246 + # Raise exception if algorithm is unknown
285 247 else:
286   -
287   - # Raise error if algorithm is unkown
288   - raise TypeError("Algorithm \""+self._algorithm+"\" not known")
  248 + raise TypeError('Algorithm "'+algorithm+'" not known')
289 249  
290 250 # Optionally publish map
291   - if self._publish:
  251 + if self['publish'].boolean():
292 252 self.publish()
293 253  
294 254 # Return
... ... @@ -296,7 +256,7 @@ class csresmap(ctools.cscript):
296 256  
297 257 def execute(self):
298 258 """
299   - Execute the script.
  259 + Execute the script
300 260 """
301 261 # Open logfile
302 262 self.logFileOpen()
... ... @@ -315,55 +275,59 @@ class csresmap(ctools.cscript):
315 275  
316 276 def save(self):
317 277 """
318   - Save residual map.
  278 + Save residual map
319 279 """
320 280 # Write header
321 281 if self._logTerse():
322   - self._log("\n")
323   - self._log.header1("Save residual map")
  282 + self._log('\n')
  283 + self._log.header1('Save residual map')
324 284  
325 285 # Get outmap parameter
326   - outmap = self["outmap"].filename()
  286 + outmap = self['outmap'].filename()
327 287  
328 288 # Continue only filename and residual map are valid
329 289 if self._resmap != None:
330 290  
331 291 # Log file name
332 292 if self._logTerse():
333   - self._log(gammalib.parformat("Residual map file"))
  293 + self._log(gammalib.parformat('Residual map file'))
334 294 self._log(outmap.url())
335   - self._log("\n")
  295 + self._log('\n')
336 296  
337 297 # Save residual map
338   - self._resmap.save(outmap, self._clobber)
  298 + self._resmap.save(outmap, self['clobber'].boolean())
339 299  
340 300 # Return
341 301 return
342 302  
343   - def publish(self, name=""):
  303 + def publish(self, name=''):
344 304 """
345 305 Publish residual map.
346 306  
347   - Kwargs:
348   - name: Name of residual map.
  307 + Parameters
  308 + ----------
  309 + name : str, optional
  310 + Name of residual map
349 311 """
350 312 # Write header
351 313 if self._logTerse():
352   - self._log("\n")
353   - self._log.header1("Publish residual map")
  314 + self._log('\n')
  315 + self._log.header1('Publish residual map')
354 316  
355 317 # Continue only if residual map is valid
356 318 if self._resmap != None:
357 319  
358 320 # Set default name is user name is empty
359 321 if not name:
360   - user_name = self.name
  322 + user_name = self._name
361 323 else:
362 324 user_name = name
363 325  
364 326 # Log map name
365 327 if self._logTerse():
366   - self._log("Publish residual map \""+user_name+"\".\n")
  328 + self._log(gammalib.parformat('Residual map name'))
  329 + self._log(user_name)
  330 + self._log('\n')
367 331  
368 332 # Publish map
369 333 self._resmap.publish(user_name)
... ... @@ -373,10 +337,10 @@ class csresmap(ctools.cscript):
373 337  
374 338 def models(self, models):
375 339 """
376   - Set model.
  340 + Set model
377 341 """
378 342 # Copy models
379   - self._obs.models(models.clone())
  343 + self._obs.models(models)
380 344  
381 345 # Return
382 346 return
... ...
test/test_csresmap.py
... ... @@ -76,7 +76,7 @@ class Test(test):
76 76 ' emin=0.1 emax=100.0 enumbins=20'+ \
77 77 ' nxpix=50 nypix=50 binsz=0.02'+ \
78 78 ' coordsys="CEL" proj="CAR" xref=83.63 yref=22.01'+ \
79   - ' algorithm="SUBDIV"'+ \
  79 + ' algorithm="SUBDIVSQRT"'+ \
80 80 ' logfile="csresmap_cmd1.log" chatter=1'
81 81  
82 82 # Check if execution of wrong command fails
... ... @@ -98,7 +98,7 @@ class Test(test):
98 98 ' emin=0.1 emax=100.0 enumbins=20'+ \
99 99 ' nxpix=50 nypix=50 binsz=0.02'+ \
100 100 ' coordsys="CEL" proj="CAR" xref=83.63 yref=22.01'+ \
101   - ' algorithm="SUBDIV"'+ \
  101 + ' algorithm="SUBDIVSQRT"'+ \
102 102 ' logfile="csresmap_cmd2.log" chatter=2'
103 103  
104 104 # Check if execution failed
... ... @@ -130,7 +130,7 @@ class Test(test):
130 130 resmap['proj'] = 'CAR'
131 131 resmap['xref'] = 83.63
132 132 resmap['yref'] = 22.01
133   - resmap['algorithm'] = 'SUBDIV'
  133 + resmap['algorithm'] = 'SUB'
134 134 resmap['logfile'] = 'csresmap_py1.log'
135 135 resmap['chatter'] = 2
136 136  
... ... @@ -157,6 +157,7 @@ class Test(test):
157 157 resmap['algorithm'] = 'SUBDIV'
158 158 resmap['logfile'] = 'csresmap_py2.log'
159 159 resmap['chatter'] = 3
  160 + resmap['publish'] = True
160 161  
161 162 # Run csresmap script
162 163 resmap.execute()
... ...