Commit c6f557e59bead81570a987955df7c3fd82d017b7
1 parent
14632f27
Integrate code
Showing
9 changed files
with
364 additions
and
262 deletions
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
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, | ... | ... |