Commit c6f557e59bead81570a987955df7c3fd82d017b7

Authored by Jürgen Knödlseder
1 parent 14632f27

Integrate code

cscripts/Makefile.am
... ... @@ -37,7 +37,7 @@ pythonscripts = $(top_builddir)/cscripts/__init__.py \
37 37 $(srcdir)/csphasecrv.py \
38 38 $(srcdir)/cspull.py \
39 39 $(srcdir)/csresmap.py \
40   - $(srcdir)/csresspec.py \
  40 + $(srcdir)/csresspec.py \
41 41 $(srcdir)/csroot2caldb.py \
42 42 $(srcdir)/cssens.py \
43 43 $(srcdir)/csspec.py \
... ... @@ -72,7 +72,7 @@ parfiles = $(srcdir)/cscaldb.par \
72 72 $(srcdir)/csphasecrv.par \
73 73 $(srcdir)/cspull.par \
74 74 $(srcdir)/csresmap.par \
75   - $(srcdir)/csresspec.par \
  75 + $(srcdir)/csresspec.par \
76 76 $(srcdir)/csroot2caldb.par \
77 77 $(srcdir)/cssens.par \
78 78 $(srcdir)/csspec.par \
... ... @@ -104,7 +104,7 @@ linkscripts = cscaldb \
104 104 csphasecrv \
105 105 cspull \
106 106 csresmap \
107   - csresspec \
  107 + csresspec \
108 108 csroot2caldb \
109 109 cssens \
110 110 csspec \
... ...
cscripts/__init__.py.in
1 1 # ==========================================================================
2 2 # cscripts Python module
3 3 #
4   -# Copyright (C) 2015-2017 Juergen Knoedlseder
  4 +# Copyright (C) 2015-2018 Juergen Knoedlseder
5 5 #
6 6 # This program is free software: you can redistribute it and/or modify
7 7 # it under the terms of the GNU General Public License as published by
... ... @@ -40,6 +40,7 @@ __all__ = [
40 40 "csphasecrv",
41 41 "cspull",
42 42 "csresmap",
  43 + "csresspec",
43 44 "csroot2caldb",
44 45 "cssens",
45 46 "csspec",
... ... @@ -77,6 +78,7 @@ from cscripts.csphagen import csphagen
77 78 from cscripts.csphasecrv import csphasecrv
78 79 from cscripts.cspull import cspull
79 80 from cscripts.csresmap import csresmap
  81 +from cscripts.csresspec import csresspec
80 82 from cscripts.csroot2caldb import csroot2caldb
81 83 from cscripts.cssens import cssens
82 84 from cscripts.csspec import csspec
... ...
cscripts/csresspec.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Generates a residual spectrum.
4 4 #
5   -# Copyright (C) 2017- Luigi Tibaldo
  5 +# Copyright (C) 2017-2018 Luigi Tibaldo
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -29,7 +29,7 @@ import sys
29 29 # =============== #
30 30 class csresspec(ctools.csobservation):
31 31 """
32   - Generates a residual map
  32 + Generates a residual spectrum
33 33 """
34 34  
35 35 # Constructor
... ... @@ -43,9 +43,9 @@ class csresspec(ctools.csobservation):
43 43  
44 44 # Initialise class members
45 45 self._use_maps = False
46   - self._stack = False
47   - self._mask = False
48   - self._fits = gammalib.GFits()
  46 + self._stack = False
  47 + self._mask = False
  48 + self._fits = gammalib.GFits()
49 49  
50 50 # Return
51 51 return
... ... @@ -65,8 +65,8 @@ class csresspec(ctools.csobservation):
65 65 # Collect number of unbinned, binned and On/Off observations in
66 66 # observation container
67 67 n_unbinned = 0
68   - n_binned = 0
69   - n_onoff = 0
  68 + n_binned = 0
  69 + n_onoff = 0
70 70 for obs in self.obs():
71 71 if obs.classname() == 'GCTAObservation':
72 72 if obs.eventtype() == 'CountsCube':
... ... @@ -75,21 +75,10 @@ class csresspec(ctools.csobservation):
75 75 n_unbinned += 1
76 76 elif obs.classname() == 'GCTAOnOffObservation':
77 77 n_onoff += 1
78   - n_cta = n_unbinned + n_binned + n_onoff
  78 + n_cta = n_unbinned + n_binned + n_onoff
79 79 n_other = self.obs().size() - n_cta
80 80  
81   - # Log census of input observations
82   - self._log_value(gammalib.NORMAL, 'Unbinned CTA observations',
83   - n_unbinned)
84   - self._log_value(gammalib.NORMAL, 'Binned CTA observations', n_binned)
85   - self._log_value(gammalib.NORMAL, 'On/off CTA observations', n_onoff)
86   - self._log_value(gammalib.NORMAL, 'Other observations', n_other)
87   - if n_other > 0:
88   - msg = 'WARNING: Only CTA observation can be handled, all non-CTA ' \
89   - + 'observations will be ignored.\n'
90   - self._log_string(gammalib.TERSE, msg)
91   -
92   - # Query wether to compute model for individual components
  81 + # Query whether to compute model for individual components
93 82 components = self['components'].boolean()
94 83  
95 84 # If there is only one binned observation and no model for individual
... ... @@ -109,18 +98,13 @@ class csresspec(ctools.csobservation):
109 98 self['emin'].real()
110 99 self['emax'].real()
111 100 self['enumbins'].integer()
112   - msg = 'User defined energy binning will be used for %s unbinned observations.' % n_unbinned
113   - self._log_string(gammalib.TERSE, msg)
114 101 if n_cta > n_unbinned:
115 102 n_notunbin = n_cta - n_unbinned
116   - msg = 'The intrinsic binning will be used for the remaining %s CTA observations.' % (
117   - n_notunbin)
118   - self._log_string(gammalib.TERSE, msg)
119 103  
120 104 # If there is more than one observation, and observations are all
121 105 # unbinned or all onoff query user to know if they wish stacked results
122   - if self.obs().size() > 1 and (
123   - n_unbinned == self.obs().size() or n_onoff == self.obs().size()):
  106 + if self.obs().size() > 1 and \
  107 + (n_unbinned == self.obs().size() or n_onoff == self.obs().size()):
124 108 self._stack = self['stack'].boolean()
125 109 # If we are to stack event lists query parameters for cube creation
126 110 if self._stack and n_unbinned == self.obs().size():
... ... @@ -151,9 +135,7 @@ class csresspec(ctools.csobservation):
151 135 # Unless all observations are On/Off, or we are using precomputed model
152 136 # maps query whether to use energy dispersion
153 137 if n_onoff == n_cta or self._use_maps:
154   - msg = 'Energy dispersion is applied based on the input data/model ' \
155   - + 'and not according to the edisp parameter'
156   - self._log_string(gammalib.TERSE, msg)
  138 + pass
157 139 else:
158 140 self['edisp'].boolean()
159 141  
... ... @@ -167,6 +149,35 @@ class csresspec(ctools.csobservation):
167 149 # Write input parameters into logger
168 150 self._log_parameters(gammalib.TERSE)
169 151  
  152 + # Write header for observation census
  153 + self._log_header1(gammalib.TERSE, 'Observation census')
  154 +
  155 + # Log census of input observations
  156 + self._log_value(gammalib.NORMAL, 'Unbinned CTA observations', n_unbinned)
  157 + self._log_value(gammalib.NORMAL, 'Binned CTA observations', n_binned)
  158 + self._log_value(gammalib.NORMAL, 'On/off CTA observations', n_onoff)
  159 + self._log_value(gammalib.NORMAL, 'Other observations', n_other)
  160 + if n_other > 0:
  161 + msg = 'WARNING: Only CTA observation can be handled, all non-CTA ' \
  162 + + 'observations will be ignored.'
  163 + self._log_string(gammalib.TERSE, msg)
  164 +
  165 + # Log for unbinned observations
  166 + if n_unbinned != 0:
  167 + msg = ' User defined energy binning will be used for %d unbinned '\
  168 + 'observations.' % (n_unbinned)
  169 + self._log_string(gammalib.TERSE, msg)
  170 + if n_cta > n_unbinned:
  171 + msg = ' The intrinsic binning will be used for the remaining '\
  172 + '%d CTA observations.' % (n_notunbin)
  173 + self._log_string(gammalib.TERSE, msg)
  174 +
  175 + # Signal how energy dispersion is applied
  176 + if n_onoff == n_cta or self._use_maps:
  177 + msg = ' Energy dispersion is applied based on the input data/model ' \
  178 + + 'and not according to the edisp parameter'
  179 + self._log_string(gammalib.TERSE, msg)
  180 +
170 181 # Return
171 182 return
172 183  
... ... @@ -175,23 +186,26 @@ class csresspec(ctools.csobservation):
175 186 Stack multiple observations and replace observation container
176 187 with single stacked observation
177 188  
178   - :return: `~gammalib.GObservations' container with a single stacked observation
  189 + Returns
  190 + -------
  191 + obs : `~gammalib.GObservations'
  192 + Observation container with a single stacked observation
179 193 """
180 194 # Use first observation to determine the type and apply the
181 195 # appropriate stacking method
182 196  
183 197 # If On/Off use the GCTAOnOffObservation constructor
184 198 if self.obs()[0].classname() == 'GCTAOnOffObservation':
185   - msg = 'Stacking %s On/Off observations.' % (self.obs().size())
  199 + msg = 'Stacking %d On/Off observations.' % (self.obs().size())
186 200 self._log_string(gammalib.NORMAL, msg)
187   - stacked = gammalib.GCTAOnOffObservation(self.obs())
  201 + stacked = gammalib.GCTAOnOffObservation(self.obs())
188 202 stacked_obs = gammalib.GObservations()
189 203 stacked_obs.append(stacked)
190 204 stacked_obs.models(self.obs().models())
191 205  
192 206 # If event list then bin observations
193 207 elif self.obs()[0].classname() == 'GCTAObservation':
194   - msg = 'Stacking %s event lists.' % (self.obs().size())
  208 + msg = 'Stacking %d event lists.' % (self.obs().size())
195 209 self._log_string(gammalib.NORMAL, msg)
196 210 stacked_obs = obsutils.get_stacked_obs(self, self.obs())
197 211  
... ... @@ -201,24 +215,31 @@ class csresspec(ctools.csobservation):
201 215 def _bin_evlist(self, obs):
202 216 """
203 217 Turn single event list into counts cube
204   - :param obs: `~gammalib.GObservations' observation container with single event list
205   - :return: `~gammalib.GObservations' binned observation container
206   - dict with event list ROI and energy range information
207 218  
  219 + Parameters
  220 + ----------
  221 + obs : `~gammalib.GObservations()`
  222 + Observation container with single event list
  223 +
  224 + Returns
  225 + -------
  226 + obs : `~gammalib.GObservations()`
  227 + Binned observation container dict with event list ROI and energy
  228 + range information
208 229 """
209 230 # Retrieve information about ROI in event list
210 231 roi = obs[0].roi()
211   - ra = roi.centre().dir().ra_deg()
  232 + ra = roi.centre().dir().ra_deg()
212 233 dec = roi.centre().dir().dec_deg()
213 234 rad = roi.radius()
214 235  
215 236 # We will cover the whole ROI with 0.02 deg binning
216   - npix = int(2 * rad / 0.02) + 1
  237 + npix = int(2.0 * rad / 0.02) + 1
  238 +
  239 + # Log binning of events
  240 + self._log_string(gammalib.EXPLICIT, 'Binning events')
217 241  
218 242 # Bin events
219   - msg = 'Binning events'
220   - self._log_string(gammalib.EXPLICIT, msg)
221   - #
222 243 cntcube = ctools.ctbin(obs)
223 244 cntcube['xref'] = ra
224 245 cntcube['yref'] = dec
... ... @@ -239,9 +260,10 @@ class csresspec(ctools.csobservation):
239 260 # Retrieve the binned observation container
240 261 binned_obs = cntcube.obs().copy()
241 262  
  263 + # Log response computation
  264 + self._log_string(gammalib.EXPLICIT, 'Computing binned response')
  265 +
242 266 # Compute binned response
243   - msg = 'Computing binned response'
244   - self._log_string(gammalib.EXPLICIT, msg)
245 267 response = obsutils.get_stacked_response(obs, ra, dec,
246 268 binsz=0.02,
247 269 nxpix=npix,
... ... @@ -287,44 +309,68 @@ class csresspec(ctools.csobservation):
287 309 regfile='NONE'):
288 310 """
289 311 Mask an event cube and returns the masked cube
290   - :param cube: `~gammalib.GCTAEventCube' Events cube
291   - :param ra: double (str 'INDEF' for no selection on direction)
292   - :param dec: double (str 'INDEF' for no selection on direction)
293   - :param rad: double (str 'INDEF' for no selection on direction)
294   - :param emin: double (str 'INDEF' for no selection on energy)
295   - :param emax: double (str 'INDEF' for no selection on energy)
296   - :param regfile: str with name of ds9 file with exclusion regions
297   - :return: `~gammalib.GCTAEventCube'
298   - """
299 312  
  313 + Parameters
  314 + ----------
  315 + cube : `~gammalib.GCTAEventCube()`
  316 + Event cube
  317 + ra : float (str 'INDEF' for no selection on direction)
  318 + Right Ascension (deg)
  319 + dec : float (str 'INDEF' for no selection on direction)
  320 + Declination (deg)
  321 + rad : float (str 'INDEF' for no selection on direction)
  322 + Radius (deg)
  323 + emin : float (str 'INDEF' for no selection on energy)
  324 + Minimum energy (TeV)
  325 + emax : float (str 'INDEF' for no selection on energy)
  326 + Maximum energy (TeV)
  327 +
  328 + Returns
  329 + -------
  330 + cube : `~gammalib.GCTAEventCube()`
  331 + Event cube
  332 + """
300 333 # Turn cube into observation container to feed to ctcubemask
301 334 obs = gammalib.GCTAObservation()
302 335 obs.events(cube)
303 336 obs_cont = gammalib.GObservations()
304 337 obs_cont.append(obs)
305 338  
306   - # Use ctcubemask to mask
  339 + # Use ctcubemask to mask event cube pixels
307 340 cubemask = ctools.ctcubemask(obs_cont)
308   - cubemask['ra'] = ra
309   - cubemask['dec'] = dec
310   - cubemask['rad'] = rad
311   - cubemask['emin'] = emin
312   - cubemask['emax'] = emax
  341 + cubemask['ra'] = ra
  342 + cubemask['dec'] = dec
  343 + cubemask['rad'] = rad
  344 + cubemask['emin'] = emin
  345 + cubemask['emax'] = emax
313 346 cubemask['regfile'] = regfile
314 347 cubemask.run()
315 348  
316   - # Return
317   - # Note: we return a copy to avoid memory leaks in SWIG
318   - return cubemask.cube().copy()
  349 + # Extract copy of cube from observation container (copy is needed to
  350 + # avoid memory leaks in SWIG)
  351 + cube = cubemask.obs()[0].events().copy()
  352 +
  353 + # Return cube
  354 + return cube
319 355  
320 356 def _cube_to_spectrum(self, cube, evlist_info):
321 357 """
322   - Derive from event cube a count spectrum. If data come from event list
323   - use only the ROI and energy range of the original data. Apply user
324   - defined mask if requested.
325   - :param cube: `~gammalib.GCTAEventCube' input cube
326   - :param evlist_info: dic with information on original event list
327   - :return: `~gammalib.GNdarray' count spectrum
  358 + Derive from event cube a count spectrum
  359 +
  360 + If data come from event list use only the ROI and energy range of
  361 + the original data. Apply user defined mask if requested.
  362 +
  363 + Parameters
  364 + ----------
  365 + cube : `~gammalib.GCTAEventCube()`
  366 + Event cube
  367 + evlist_info : dict
  368 + Dictionary with information on original event list
  369 +
  370 + Returns
  371 + -------
  372 + array : `~gammalib.GNdarray()'
  373 + Counts spectrum
328 374 """
329 375 # If we started from event list mask the ROI only
330 376 # for residual computation
... ... @@ -350,7 +396,7 @@ class csresspec(ctools.csobservation):
350 396 counts = counts.clip(0.)
351 397  
352 398 # Convert skymap into GNdarray count spectrum
353   - counts = counts.total_counts()
  399 + counts = counts.counts()
354 400  
355 401 # Return
356 402 return counts
... ... @@ -358,30 +404,42 @@ class csresspec(ctools.csobservation):
358 404 def _residuals_table(self, obs_id, ebounds, counts, model, residuals):
359 405 """
360 406 Create a Fits Table and store counts, model, and residuals
361   - :param obs_id: str observation id
362   - :param ebounds: `~gammalib.GEbounds' energy bounds
363   - :param counts: `~gammalib.GNdarray'
364   - :param model: `~gammalib.GNdarray'
365   - :param residuals: `~gammalib.GNdarray'
366   - :return: `~gammalib.GFitsBinTable'
  407 +
  408 + Parameters
  409 + ----------
  410 + obs_id : str
  411 + Observation id
  412 + ebounds : `~gammalib.GEbounds()'
  413 + Energy boundaries
  414 + counts : `~gammalib.GNdarray()'
  415 + Counts spectrum
  416 + model : `~gammalib.GNdarray()'
  417 + Model spectrum
  418 + residuals : `~gammalib.GNdarray()'
  419 + Residual spectrum
  420 +
  421 + Returns
  422 + -------
  423 + table : `~gammalib.GFitsBinTable()'
  424 + Residual spectrum as FITS binary table
367 425 """
368 426 # Create FITS table columns
369   - nrows = ebounds.size()
370   - energy_low = gammalib.GFitsTableDoubleCol('Emin', nrows)
  427 + nrows = ebounds.size()
  428 + energy_low = gammalib.GFitsTableDoubleCol('Emin', nrows)
371 429 energy_high = gammalib.GFitsTableDoubleCol('Emax', nrows)
372   - counts_col = gammalib.GFitsTableDoubleCol('Counts', nrows)
373   - model_col = gammalib.GFitsTableDoubleCol('Model', nrows)
374   - resid_col = gammalib.GFitsTableDoubleCol('Residuals', nrows)
  430 + counts_col = gammalib.GFitsTableDoubleCol('Counts', nrows)
  431 + model_col = gammalib.GFitsTableDoubleCol('Model', nrows)
  432 + resid_col = gammalib.GFitsTableDoubleCol('Residuals', nrows)
375 433 energy_low.unit('TeV')
376 434 energy_high.unit('TeV')
377 435  
378 436 # Fill FITS table columns
379 437 for i in range(nrows):
380   - energy_low[i] = ebounds.emin(i).TeV()
  438 + energy_low[i] = ebounds.emin(i).TeV()
381 439 energy_high[i] = ebounds.emax(i).TeV()
382   - counts_col[i] = counts[i]
383   - model_col[i] = model[i]
384   - resid_col[i] = residuals[i]
  440 + counts_col[i] = counts[i]
  441 + model_col[i] = model[i]
  442 + resid_col[i] = residuals[i]
385 443  
386 444 # Initialise FITS Table with extension set to obs id
387 445 table = gammalib.GFitsBinTable(nrows)
... ... @@ -401,15 +459,26 @@ class csresspec(ctools.csobservation):
401 459 table.append(model_col)
402 460 table.append(resid_col)
403 461  
  462 + # Return binary table
404 463 return table
405 464  
406 465 def _append_column(self, table, name, data):
407 466 """
408 467 Append optional column to residual table
409   - :param table: `~gammalib.GFitsBinTable'
410   - :param name: str column name
411   - :param data: `gammalib.GNdarray' data to be filled into new column
412   - :return: `~gammalib.GFitsBinTable'
  468 +
  469 + Parameters
  470 + ----------
  471 + table : `~gammalib.GFitsBinTable()'
  472 + FITS binary table
  473 + name : str
  474 + Column name
  475 + data : `~gammalib.GEbounds()'
  476 + Data to be filled into new column
  477 +
  478 + Returns
  479 + -------
  480 + table : `~gammalib.GFitsBinTable()'
  481 + FITS binary table
413 482 """
414 483 # Check size compatibility
415 484 if table.nrows() == data.size():
... ... @@ -417,7 +486,7 @@ class csresspec(ctools.csobservation):
417 486 # Otherwise throw error
418 487 else:
419 488 msg = 'csresspec._append_column: FITS table and data have ' \
420   - + 'incompatible size.'
  489 + 'incompatible size.'
421 490 raise RuntimeError(msg)
422 491  
423 492 # Create column
... ... @@ -433,6 +502,7 @@ class csresspec(ctools.csobservation):
433 502 # Return modified table
434 503 return table
435 504  
  505 +
436 506 # Public methods
437 507 def run(self):
438 508 """
... ... @@ -453,17 +523,21 @@ class csresspec(ctools.csobservation):
453 523 self._log_header1(gammalib.TERSE, 'Stacking Observations')
454 524 self.obs(self._stack_observations())
455 525  
  526 + # Log processing header
456 527 self._log_header1(gammalib.TERSE, 'Processing Observations')
  528 +
457 529 # Loop over observations and calculate residuals
458 530 for s, observation in enumerate(self.obs()):
459 531  
460 532 # Retrieve and store obs id
461 533 obs_id = observation.id()
  534 +
462 535 # If observation id is empty and there is more than one observation
463 536 # replace with incremental number
464 537 if obs_id == '' and self.obs().size() > 1:
465 538 obs_id = str(s)
466 539  
  540 + # Log processing of observation
467 541 if self.obs().size() > 1:
468 542 self._log_header2(gammalib.NORMAL,
469 543 'Processing observation %s' % obs_id)
... ... @@ -476,7 +550,7 @@ class csresspec(ctools.csobservation):
476 550 # if 3D observation
477 551 if obs[0].classname() == 'GCTAObservation':
478 552  
479   - ## Prepare Observations
  553 + # Prepare Observations
480 554  
481 555 # If already binned set the evlist_info dictionary to have
482 556 # attribute was_list False
... ... @@ -490,7 +564,7 @@ class csresspec(ctools.csobservation):
490 564 self._log_string(gammalib.NORMAL, msg)
491 565 obs, evlist_info = self._bin_evlist(obs)
492 566  
493   - ## Calculate Model and residuals
  567 + # Calculate Model and residuals
494 568 # If model cube is provided load it
495 569 if self._use_maps:
496 570 modcube = gammalib.GCTAEventCube(self['inmodel'].filename())
... ... @@ -509,7 +583,7 @@ class csresspec(ctools.csobservation):
509 583 msg = 'Computing counts, model, and residual spectra'
510 584 self._log_string(gammalib.NORMAL, msg)
511 585 counts = self._cube_to_spectrum(cntcube, evlist_info)
512   - model = self._cube_to_spectrum(modcube, evlist_info)
  586 + model = self._cube_to_spectrum(modcube, evlist_info)
513 587  
514 588 # Calculate residuals
515 589 residuals = obsutils.residuals(self, counts, model)
... ... @@ -523,7 +597,7 @@ class csresspec(ctools.csobservation):
523 597 table = self._residuals_table(obs_id, ebounds, counts, model,
524 598 residuals)
525 599  
526   - ## Calculate models of individual components if requested
  600 + # Calculate models of individual components if requested
527 601 if self['components'].boolean():
528 602 for component in obs.models():
529 603 self._log_value(gammalib.NORMAL,
... ... @@ -551,7 +625,7 @@ class csresspec(ctools.csobservation):
551 625  
552 626 onoff = obs[0]
553 627  
554   - ## Calculate Counts, Model and residuals
  628 + # Calculate Counts, Model and residuals
555 629 msg = 'Computing counts, model, and residual spectra'
556 630 self._log_string(gammalib.NORMAL, msg)
557 631  
... ... @@ -559,12 +633,11 @@ class csresspec(ctools.csobservation):
559 633 counts = onoff.on_spec().counts_spectrum()
560 634  
561 635 # Model for On region
562   - background = onoff.model_background(
563   - obs.models()).counts_spectrum()
564   - alpha = onoff.on_spec().backscal_spectrum()
565   - model = background.copy()
566   - model *= alpha
567   - model += onoff.model_gamma(obs.models()).counts_spectrum()
  636 + background = onoff.model_background(obs.models()).counts_spectrum()
  637 + alpha = onoff.on_spec().backscal_spectrum()
  638 + model = background.copy()
  639 + model *= alpha
  640 + model += onoff.model_gamma(obs.models()).counts_spectrum()
568 641  
569 642 # On Residuals
570 643 residuals = obsutils.residuals(self, counts, model)
... ... @@ -594,7 +667,7 @@ class csresspec(ctools.csobservation):
594 667 table = self._append_column(table, 'Residuals_Off',
595 668 residuals_off)
596 669  
597   - ## Calculate models of individual components if requested
  670 + # Calculate models of individual components if requested
598 671 if self['components'].boolean():
599 672 for component in obs.models():
600 673 # If background pass
... ... @@ -625,9 +698,12 @@ class csresspec(ctools.csobservation):
625 698 table = self._append_column(table, 'Background',
626 699 background)
627 700  
628   - ## Append results table to output file
  701 + # Append results table to output file
629 702 self._fits.append(table)
630 703  
  704 + # Return
  705 + return
  706 +
631 707 def save(self):
632 708 """
633 709 Save residuals
... ... @@ -637,6 +713,7 @@ class csresspec(ctools.csobservation):
637 713  
638 714 # Continue only if FITS file is valid
639 715 if self._fits != None:
  716 +
640 717 # Get outfile parameter
641 718 outfile = self['outfile'].filename()
642 719  
... ... @@ -654,6 +731,7 @@ class csresspec(ctools.csobservation):
654 731 # Main routine entry point #
655 732 # ======================== #
656 733 if __name__ == '__main__':
  734 +
657 735 # Create instance of application
658 736 app = csresspec(sys.argv)
659 737  
... ...
cscripts/obsutils.py
1 1 # ==========================================================================
2 2 # Utility functions for observation handling
3 3 #
4   -# Copyright (C) 2011-2017 Juergen Knoedlseder
  4 +# Copyright (C) 2011-2018 Juergen Knoedlseder
5 5 #
6 6 # This program is free software: you can redistribute it and/or modify
7 7 # it under the terms of the GNU General Public License as published by
... ... @@ -829,22 +829,33 @@ def get_onoff_obs(cls, obs):
829 829 # Return On/Off oberservation container
830 830 return onoff_obs
831 831  
  832 +
832 833 # ========================================= #
833 834 # Calculate residuals from counts and model #
834 835 # ========================================= #
835   -def residuals(cls,counts,model):
  836 +def residuals(cls, counts, model):
836 837 """
837   - Calculate residuals given counts and models,
838   - according to algorithm specified by user.
  838 + Calculate residuals given counts and models, according to algorithm
  839 + specified by user.
  840 +
839 841 Can handle GSkyMap or GNdarray objects
840   - :param cls: `~ctools.cscript`
  842 +
  843 +
  844 + Parameters
  845 + ----------
  846 + cls : `~ctools.cscript`
841 847 cscript class
842   - :param counts: `~gammalib.GSkyMap/~gammalib.GNdarray' data counts
843   - :param model: `~gammalib.GSkyMap/~gammalib.GNdarray' model counts
844   - :return residuals: `~gammalib.GSkyMap/~gammalib.GNdarray' residuals
  848 + counts : `~gammalib.GSkyMap/~gammalib.GNdarray'
  849 + Data counts
  850 + model : `~gammalib.GSkyMap/~gammalib.GNdarray'
  851 + Model counts
  852 +
  853 + Returns
  854 + -------
  855 + residuals : `~gammalib.GSkyMap/~gammalib.GNdarray'
  856 + Residuals
845 857 """
846   - # Find type of objects we are manipulating
847   - # and set size to iterate later
  858 + # Find type of objects we are manipulating and set size to iterate later
848 859  
849 860 # If GNdarray
850 861 if counts.classname() == 'GNdarray':
... ... @@ -893,9 +904,8 @@ def residuals(cls,counts,model):
893 904 # significance^2 and save it ...
894 905 data_val = residuals[i]
895 906 if data_val > 0.0:
896   - log_val = math.log(data_val / model_val)
897   - residuals[i] = (
898   - data_val * log_val) + model_val - data_val
  907 + log_val = math.log(data_val / model_val)
  908 + residuals[i] = (data_val * log_val) + model_val - data_val
899 909  
900 910 # ... otherwise compute the reduced value of the above
901 911 # expression. This is necessary to avoid computing log(0).
... ... @@ -908,7 +918,7 @@ def residuals(cls,counts,model):
908 918  
909 919 # Compute significance map
910 920 residuals *= 2.0
911   - residuals = residuals.sqrt()
  921 + residuals = residuals.sqrt()
912 922 residuals *= sign
913 923  
914 924 # Raise exception if algorithm is unknown
... ... @@ -916,4 +926,4 @@ def residuals(cls,counts,model):
916 926 raise TypeError('Algorithm "' + algorithm + '" not known')
917 927  
918 928 # Return
919   - return residuals
920 929 \ No newline at end of file
  930 + return residuals
... ...
examples/Makefile.am
1 1 #############################################################################
2 2 # Makefile for ctools examples #
3 3 # ------------------------------------------------------------------------- #
4   -# Copyright (C) 2016-2017 Juergen Knoedlseder #
  4 +# Copyright (C) 2016-2018 Juergen Knoedlseder #
5 5 # ------------------------------------------------------------------------- #
6 6 # #
7 7 # This program is free software: you can redistribute it and/or modify #
... ... @@ -28,7 +28,7 @@ scripts = README.md \
28 28 show_pha.py \
29 29 show_pull_evolution.py \
30 30 show_pull_histogram.py \
31   - show_residuals.py \
  31 + show_residuals.py \
32 32 show_response.py \
33 33 show_sensitivity.py \
34 34 show_spectrum.py \
... ...
examples/show_residuals.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Display residuals generated by csresspec
4 4 #
5   -# Copyright (C) 2017- Luigi Tibaldo
  5 +# Copyright (C) 2017-2018 Luigi Tibaldo
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -22,11 +22,9 @@ import sys
22 22 import math
23 23 import gammalib
24 24 import cscripts
25   -
26 25 try:
27 26 import matplotlib.pyplot as plt
28 27 from matplotlib import gridspec
29   -
30 28 plt.figure()
31 29 plt.close()
32 30 except (ImportError, RuntimeError):
... ... @@ -42,23 +40,41 @@ def fill_cmr(row, counts, model, resid, e_counts, e_resid, c_counts, c_model,
42 40 """
43 41 Helper function that fills in the counts, model, residuals lists and
44 42 calculate relative errors for given table row
45   - :param row: int row number
46   - :param counts: list
47   - :param model: list
48   - :param resid: list
49   - :param e_counts: list
50   - :param e_resid: list
51   - :param c_counts: `~gammalib.GFits.table.column'
52   - :param c_model: `~gammalib.GFits.table.column'
53   - :param c_resid: `~gammalib.GFits.table.column'
54   - :param algorithm: string
55   - :return: counts, model, resid, e_counts, e_resid
  43 +
  44 + Parameters
  45 + ----------
  46 + row : int
  47 + Table row index
  48 + counts : list of float
  49 + Counts
  50 + model : list of float
  51 + Model
  52 + resid : list of float
  53 + Residual
  54 + e_counts : list of float
  55 + Counts error
  56 + e_resid : list of float
  57 + Residual error
  58 + c_counts : `~gammalib.GFitsTableCol()'
  59 + Counts FITS table column
  60 + c_model : `~gammalib.GFitsTableCol()'
  61 + Model FITS table column
  62 + c_resid : `~gammalib.GFitsTableCol()'
  63 + Residual FITS table column
  64 + algorithm : str
  65 + Algorithm
  66 +
  67 + Returns
  68 + -------
  69 + counts, model, resid, e_counts, e_resid : tuple of float
  70 + Values
56 71 """
  72 + # Extract values
57 73 counts.append(c_counts.real(row))
58 74 model.append(c_model.real(row))
59 75 resid.append(c_resid.real(row))
60 76  
61   - # calculate count error
  77 + # Calculate count error
62 78 err = math.sqrt(c_counts.real(row))
63 79 if err == 1:
64 80 # this prevents visualization problem in matplotlib
... ... @@ -67,7 +83,7 @@ def fill_cmr(row, counts, model, resid, e_counts, e_resid, c_counts, c_model,
67 83 pass
68 84 e_counts.append(err)
69 85  
70   - # calculate residual error
  86 + # Calculate residual error
71 87 if algorithm == 'SUB':
72 88 e_resid.append(err)
73 89 elif algorithm == 'SUBDIV':
... ... @@ -77,6 +93,7 @@ def fill_cmr(row, counts, model, resid, e_counts, e_resid, c_counts, c_model,
77 93 elif algorithm == 'SIGNIFICANCE':
78 94 e_resid.append(1.)
79 95  
  96 + # Return tuple
80 97 return counts, model, resid, e_counts, e_resid
81 98  
82 99  
... ... @@ -97,59 +114,63 @@ def plot_residuals(filename, plotfile, hdu):
97 114 Number of observation to plot
98 115 """
99 116 # Read spectrum file
100   - fits = gammalib.GFits(filename)
101   - table = fits.table(1 + hdu)
102   - c_emin = table['Emin']
103   - c_emax = table['Emax']
  117 + fits = gammalib.GFits(filename)
  118 + table = fits.table(1 + hdu)
  119 + c_emin = table['Emin']
  120 + c_emax = table['Emax']
104 121 c_counts = table['Counts']
105   - c_model = table['Model']
106   - c_resid = table['Residuals']
  122 + c_model = table['Model']
  123 + c_resid = table['Residuals']
107 124 try:
108 125 c_counts_off = table['Counts_Off']
109   - c_model_off = table['Model_Off']
110   - c_resid_off = table['Residuals_Off']
111   - is_onoff = True
  126 + c_model_off = table['Model_Off']
  127 + c_resid_off = table['Residuals_Off']
  128 + is_onoff = True
112 129 except:
113 130 is_onoff = False
114 131  
115 132 # Initialise arrays to be filled
116   - ebounds = []
117   - ed_engs = []
118   - eu_engs = []
119   - em_engs = []
120   - counts = []
121   - e_counts = []
122   - model = []
123   - resid = []
124   - e_resid = []
125   - counts_off = []
  133 + ebounds = []
  134 + ed_engs = []
  135 + eu_engs = []
  136 + em_engs = []
  137 + counts = []
  138 + e_counts = []
  139 + model = []
  140 + resid = []
  141 + e_resid = []
  142 + counts_off = []
126 143 e_counts_off = []
127   - model_off = []
128   - resid_off = []
129   - e_resid_off = []
  144 + model_off = []
  145 + resid_off = []
  146 + e_resid_off = []
130 147  
131 148 # Residual algorithm
132 149 algorithm = table.header()['ALGORITHM'].string()
133 150  
134 151 # Loop over rows of the file
135 152 nrows = table.nrows()
136   - # add first energy boundary
  153 +
  154 + # Add first energy boundary
137 155 ebounds.append(c_emin.real(0))
138 156 for row in range(nrows):
139   - # boundaries
  157 +
  158 + # Boundaries
140 159 ebounds.append(c_emax.real(row))
141   - # geometrical mean energy and errors
  160 +
  161 + # Geometrical mean energy and errors
142 162 emean = math.sqrt(c_emin.real(row) * c_emax.real(row))
143 163 em_engs.append(emean)
144 164 ed_engs.append(emean - c_emin.real(row))
145 165 eu_engs.append(c_emax.real(row) - emean)
  166 +
146 167 # counts, model, residuals
147 168 counts, model, resid, e_counts, e_resid = fill_cmr(row, counts, model,
148 169 resid, e_counts,
149 170 e_resid, c_counts,
150 171 c_model, c_resid,
151 172 algorithm)
152   - # onoff
  173 + # On/off
153 174 if is_onoff:
154 175 counts_off, model_off, resid_off, e_counts_off, e_resid_off = fill_cmr(
155 176 row, counts_off,
... ... @@ -168,7 +189,7 @@ def plot_residuals(filename, plotfile, hdu):
168 189 # Plot
169 190 axarr = []
170 191 if is_onoff:
171   - f = plt.figure(figsize=(8, 4))
  192 + f = plt.figure(figsize=(8, 4))
172 193 gs = gridspec.GridSpec(2, 2, height_ratios=[2, 1])
173 194 gs.update(hspace=0, left=0.09, right=0.97)
174 195 axarr.append(f.add_subplot(gs[0]))
... ... @@ -176,7 +197,7 @@ def plot_residuals(filename, plotfile, hdu):
176 197 axarr.append(f.add_subplot(gs[1], sharex=axarr[0]))
177 198 axarr.append(f.add_subplot(gs[3], sharex=axarr[1]))
178 199 else:
179   - f = plt.figure()
  200 + f = plt.figure()
180 201 gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])
181 202 gs.update(hspace=0)
182 203 axarr.append(f.add_subplot(gs[0]))
... ... @@ -185,12 +206,14 @@ def plot_residuals(filename, plotfile, hdu):
185 206 axarr[0].set_xscale('log')
186 207 axarr[1].set_xscale('log')
187 208 axarr[0].set_yscale('log')
  209 +
188 210 # Counts and model
189 211 axarr[0].errorbar(em_engs, counts, yerr=e_counts, xerr=[ed_engs, eu_engs],
190 212 fmt='ko', capsize=0, linewidth=2, zorder=2, label='Data')
191 213 axarr[0].step(ebounds, model, color='0.5', linewidth=2, zorder=1,
192 214 label='Model')
193 215 axarr[0].set_ylabel('Counts')
  216 +
194 217 # Residuals
195 218 axarr[1].errorbar(em_engs, resid, yerr=e_resid, xerr=[ed_engs, eu_engs],
196 219 fmt='ko', capsize=0, linewidth=2, zorder=2)
... ... @@ -202,6 +225,7 @@ def plot_residuals(filename, plotfile, hdu):
202 225 axarr[1].set_ylabel('Residuals (fraction)')
203 226 elif algorithm == 'SUBDIVSQRT' or algorithm == 'SIGNIFICANCE':
204 227 axarr[1].set_ylabel(r'Residuals ($\sigma$)')
  228 +
205 229 # On/Off
206 230 if is_onoff:
207 231 axarr[0].set_title('ON')
... ... @@ -210,6 +234,7 @@ def plot_residuals(filename, plotfile, hdu):
210 234 axarr[2].set_xscale('log')
211 235 axarr[3].set_xscale('log')
212 236 axarr[2].set_yscale('log')
  237 +
213 238 # Counts and model
214 239 axarr[2].errorbar(em_engs, counts_off, yerr=e_counts_off,
215 240 xerr=[ed_engs, eu_engs],
... ... @@ -217,6 +242,7 @@ def plot_residuals(filename, plotfile, hdu):
217 242 label='Data')
218 243 axarr[2].step(ebounds, model_off, color='0.5', linewidth=2, zorder=1,
219 244 label='Model')
  245 +
220 246 # Residuals
221 247 axarr[3].errorbar(em_engs, resid_off, yerr=e_resid_off,
222 248 xerr=[ed_engs, eu_engs],
... ... @@ -284,5 +310,6 @@ def show_residuals():
284 310 # Main routine entry point #
285 311 # ======================== #
286 312 if __name__ == '__main__':
287   - # Show spectrum
  313 +
  314 + # Show residuals
288 315 show_residuals()
... ...
pyext/ctcubemask.i
... ... @@ -48,7 +48,6 @@ public:
48 48 void run(void);
49 49 void save(void);
50 50 void publish(const std::string& name = "");
51   - GCTAEventCube* cube(void);
52 51 };
53 52  
54 53  
... ...
src/ctcubemask/ctcubemask.hpp
... ... @@ -62,7 +62,6 @@ public:
62 62 void run(void);
63 63 void save(void);
64 64 void publish(const std::string& name = "");
65   - GCTAEventCube* cube(void) const;
66 65  
67 66 protected:
68 67 // Protected methods
... ... @@ -89,19 +88,4 @@ protected:
89 88 bool m_select_energy; //!< Perform energy selection
90 89 };
91 90  
92   -/***********************************************************************//**
93   - * @brief Return events cube
94   - *
95   - * @return Reference to events cube correspoding to ith observation
96   - *
97   - * Returns a reference to the events cube.
98   - ***************************************************************************/
99   -inline
100   -GCTAEventCube* ctcubemask::cube(void) const
101   -{
102   -
103   - return dynamic_cast<GCTAEventCube*>(const_cast<GEvents*>(m_obs[0]->events()));
104   -
105   -}
106   -
107 91 #endif /* CTCUBEMASK_HPP */
... ...
test/test_csresspec.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # This scripts performs unit tests for the csresspec script.
4 4 #
5   -# Copyright (C) 2017- Luigi Tibaldo
  5 +# Copyright (C) 2017-2018 Luigi Tibaldo
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -43,7 +43,7 @@ class Test(test):
43 43 test.__init__(self)
44 44  
45 45 # Set members
46   - self._inonoff = self._datadir + '/onoff_obs.xml'
  46 + self._inonoff = self._datadir + '/onoff_obs.xml'
47 47 self._onoff_model = self._datadir + '/onoff_model.xml'
48 48  
49 49 # Return
... ... @@ -74,12 +74,12 @@ class Test(test):
74 74  
75 75 # Setup csresspec command
76 76 cmd = csresspec + ' inobs="' + self._events + '"' + \
77   - ' outfile="csresspec_cmd1.fits"' + \
78   - ' inmodel="' + self._model + '"' + \
79   - ' caldb="' + self._caldb + '" irf="' + self._irf + '"' + \
80   - ' emin=0.1 emax=100.0 enumbins=20' + \
81   - ' mask=no algorithm="SUB"' + \
82   - ' logfile="csresspec_cmd1.log" chatter=4'
  77 + ' outfile="csresspec_cmd1.fits"' + \
  78 + ' inmodel="' + self._model + '"' + \
  79 + ' caldb="' + self._caldb + '" irf="' + self._irf + '"' + \
  80 + ' emin=0.1 emax=100.0 enumbins=20' + \
  81 + ' mask=no algorithm="SUB"' + \
  82 + ' logfile="csresspec_cmd1.log" chatter=4'
83 83  
84 84 # Check if execution of wrong command fails
85 85 self.test_assert(self._execute('command_that_does_not_exist') != 0,
... ... @@ -94,12 +94,12 @@ class Test(test):
94 94  
95 95 # Setup csresspec command
96 96 cmd = csresspec + ' inobs="event_file_that_does_not_exist.fits"' + \
97   - ' outfile="csresspec_cmd2.fits"' + \
98   - ' inmodel="' + self._model + '"' + \
99   - ' caldb="' + self._caldb + '" irf="' + self._irf + '"' + \
100   - ' emin=0.1 emax=100.0 enumbins=20' + \
101   - ' mask=no algorithm="SUBDIVSQRT"' + \
102   - ' logfile="csresspec_cmd2.log" debug=yes chatter=2'
  97 + ' outfile="csresspec_cmd2.fits"' + \
  98 + ' inmodel="' + self._model + '"' + \
  99 + ' caldb="' + self._caldb + '" irf="' + self._irf + '"' + \
  100 + ' emin=0.1 emax=100.0 enumbins=20' + \
  101 + ' mask=no algorithm="SUBDIVSQRT"' + \
  102 + ' logfile="csresspec_cmd2.log" debug=yes chatter=2'
103 103  
104 104 # Check if execution failed
105 105 self.test_assert(self._execute(cmd) != 0,
... ... @@ -118,18 +118,18 @@ class Test(test):
118 118 """
119 119 # Set-up csresspec for event list
120 120 resspec = cscripts.csresspec()
121   - resspec['inobs'] = self._events
122   - resspec['outfile'] = 'csresspec_py1.fits'
123   - resspec['inmodel'] = self._model
124   - resspec['caldb'] = self._caldb
125   - resspec['irf'] = self._irf
126   - resspec['emin'] = 0.1
127   - resspec['emax'] = 100.0
128   - resspec['enumbins'] = 20
129   - resspec['mask'] = False
  121 + resspec['inobs'] = self._events
  122 + resspec['outfile'] = 'csresspec_py1.fits'
  123 + resspec['inmodel'] = self._model
  124 + resspec['caldb'] = self._caldb
  125 + resspec['irf'] = self._irf
  126 + resspec['emin'] = 0.1
  127 + resspec['emax'] = 100.0
  128 + resspec['enumbins'] = 20
  129 + resspec['mask'] = False
130 130 resspec['algorithm'] = 'SUB'
131   - resspec['logfile'] = 'csresspec_py1.log'
132   - resspec['chatter'] = 2
  131 + resspec['logfile'] = 'csresspec_py1.log'
  132 + resspec['chatter'] = 2
133 133  
134 134 # Run csresspec script
135 135 resspec.logFileOpen() # Make sure we get a log file
... ... @@ -142,19 +142,19 @@ class Test(test):
142 142 # Set-up csresspec for event list
143 143 # with computation of individual model components
144 144 resspec = cscripts.csresspec()
145   - resspec['inobs'] = self._events
146   - resspec['outfile'] = 'csresspec_py2.fits'
147   - resspec['inmodel'] = self._model
148   - resspec['caldb'] = self._caldb
149   - resspec['irf'] = self._irf
150   - resspec['emin'] = 0.1
151   - resspec['emax'] = 100.0
152   - resspec['enumbins'] = 20
153   - resspec['mask'] = False
154   - resspec['algorithm'] = 'SUBDIV'
  145 + resspec['inobs'] = self._events
  146 + resspec['outfile'] = 'csresspec_py2.fits'
  147 + resspec['inmodel'] = self._model
  148 + resspec['caldb'] = self._caldb
  149 + resspec['irf'] = self._irf
  150 + resspec['emin'] = 0.1
  151 + resspec['emax'] = 100.0
  152 + resspec['enumbins'] = 20
  153 + resspec['mask'] = False
  154 + resspec['algorithm'] = 'SUBDIV'
155 155 resspec['components'] = True
156   - resspec['logfile'] = 'csresspec_py2.log'
157   - resspec['chatter'] = 2
  156 + resspec['logfile'] = 'csresspec_py2.log'
  157 + resspec['chatter'] = 2
158 158  
159 159 # Run csresspec script
160 160 resspec.execute()
... ... @@ -164,20 +164,20 @@ class Test(test):
164 164  
165 165 # Set-up csresspec for counts cube
166 166 resspec = cscripts.csresspec()
167   - resspec['inobs'] = self._cntcube
168   - resspec['modcube'] = 'NONE'
169   - resspec['expcube'] = 'NONE'
170   - resspec['psfcube'] = 'NONE'
  167 + resspec['inobs'] = self._cntcube
  168 + resspec['modcube'] = 'NONE'
  169 + resspec['expcube'] = 'NONE'
  170 + resspec['psfcube'] = 'NONE'
171 171 resspec['edispcube'] = 'NONE'
172   - resspec['bkgcube'] = 'NONE'
173   - resspec['caldb'] = self._caldb
174   - resspec['irf'] = self._irf
175   - resspec['inmodel'] = self._model
176   - resspec['outfile'] = 'csresspec_py3.fits'
177   - resspec['mask'] = False
  172 + resspec['bkgcube'] = 'NONE'
  173 + resspec['caldb'] = self._caldb
  174 + resspec['irf'] = self._irf
  175 + resspec['inmodel'] = self._model
  176 + resspec['outfile'] = 'csresspec_py3.fits'
  177 + resspec['mask'] = False
178 178 resspec['algorithm'] = 'SUBDIVSQRT'
179   - resspec['logfile'] = 'csresspec_py3.log'
180   - resspec['chatter'] = 3
  179 + resspec['logfile'] = 'csresspec_py3.log'
  180 + resspec['chatter'] = 3
181 181  
182 182 # Run csresspec script
183 183 resspec.execute()
... ... @@ -188,24 +188,24 @@ class Test(test):
188 188 # Set-up csresspec for counts cube
189 189 # with source mask
190 190 resspec = cscripts.csresspec()
191   - resspec['inobs'] = self._cntcube
192   - resspec['modcube'] = 'NONE'
193   - resspec['expcube'] = 'NONE'
194   - resspec['psfcube'] = 'NONE'
  191 + resspec['inobs'] = self._cntcube
  192 + resspec['modcube'] = 'NONE'
  193 + resspec['expcube'] = 'NONE'
  194 + resspec['psfcube'] = 'NONE'
195 195 resspec['edispcube'] = 'NONE'
196   - resspec['bkgcube'] = 'NONE'
197   - resspec['caldb'] = self._caldb
198   - resspec['irf'] = self._irf
199   - resspec['inmodel'] = self._model
200   - resspec['outfile'] = 'csresspec_py4.fits'
201   - resspec['mask'] = True
202   - resspec['ra'] = 83.63
203   - resspec['dec'] = 22.01
204   - resspec['rad'] = 0.2
205   - resspec['regfile'] = 'NONE'
  196 + resspec['bkgcube'] = 'NONE'
  197 + resspec['caldb'] = self._caldb
  198 + resspec['irf'] = self._irf
  199 + resspec['inmodel'] = self._model
  200 + resspec['outfile'] = 'csresspec_py4.fits'
  201 + resspec['mask'] = True
  202 + resspec['ra'] = 83.63
  203 + resspec['dec'] = 22.01
  204 + resspec['rad'] = 0.2
  205 + resspec['regfile'] = 'NONE'
206 206 resspec['algorithm'] = 'SUBDIVSQRT'
207   - resspec['logfile'] = 'csresspec_py4.log'
208   - resspec['chatter'] = 3
  207 + resspec['logfile'] = 'csresspec_py4.log'
  208 + resspec['chatter'] = 3
209 209  
210 210 # Run csresspec script
211 211 resspec.execute()
... ... @@ -215,13 +215,13 @@ class Test(test):
215 215  
216 216 # Set-up csresspec for On/Off observations
217 217 resspec = cscripts.csresspec()
218   - resspec['inobs'] = self._inonoff
219   - resspec['inmodel'] = self._onoff_model
220   - resspec['outfile'] = 'csresspec_py5.fits'
  218 + resspec['inobs'] = self._inonoff
  219 + resspec['inmodel'] = self._onoff_model
  220 + resspec['outfile'] = 'csresspec_py5.fits'
221 221 resspec['algorithm'] = 'SIGNIFICANCE'
222   - resspec['stack'] = False
223   - resspec['logfile'] = 'csresspec_py5.log'
224   - resspec['chatter'] = 3
  222 + resspec['stack'] = False
  223 + resspec['logfile'] = 'csresspec_py5.log'
  224 + resspec['chatter'] = 3
225 225  
226 226 # Run csresspec script
227 227 resspec.execute()
... ... @@ -232,14 +232,14 @@ class Test(test):
232 232 # Set-up csresspec for On/Off observations
233 233 # in stacked mode and with WSTAT background
234 234 resspec = cscripts.csresspec()
235   - resspec['inobs'] = self._inonoff
236   - resspec['inmodel'] = self._onoff_model
237   - resspec['outfile'] = 'csresspec_py6.fits'
  235 + resspec['inobs'] = self._inonoff
  236 + resspec['inmodel'] = self._onoff_model
  237 + resspec['outfile'] = 'csresspec_py6.fits'
238 238 resspec['algorithm'] = 'SIGNIFICANCE'
239 239 resspec['statistic'] = 'WSTAT'
240   - resspec['stack'] = True
241   - resspec['logfile'] = 'csresspec_py6.log'
242   - resspec['chatter'] = 3
  240 + resspec['stack'] = True
  241 + resspec['logfile'] = 'csresspec_py6.log'
  242 + resspec['chatter'] = 3
243 243  
244 244 # Run csresspec script
245 245 resspec.execute()
... ... @@ -263,9 +263,11 @@ class Test(test):
263 263  
264 264 # Check first hdu with residuals
265 265 table = fits[1]
  266 +
266 267 # Check number of rows = number of energy bins
267 268 self.test_value(table.nrows(), enumbins,
268 269 'Check for %d rows in residuals' % enumbins)
  270 +
269 271 # Check number of columns, 2 for energy bounds + 3 for 3D or 6 for
270 272 # On/Off + 1 for each model component if components = yes
271 273 self.test_value(table.ncols(), ncols,
... ...