Commit 8fd062ec715801378ce9924addc1563006ab833f
1 parent
8ef51306
treatment of 3D observations
Showing
2 changed files
with
176 additions
and
19 deletions
cscripts/csresspec.par
| @@ -51,6 +51,7 @@ ra, r, a, 83.63,0,360, "RA for ROI centre (degrees)" | @@ -51,6 +51,7 @@ ra, r, a, 83.63,0,360, "RA for ROI centre (degrees)" | ||
| 51 | dec, r, a, 22.01,-90,90, "Dec for ROI centre (degrees)" | 51 | dec, r, a, 22.01,-90,90, "Dec for ROI centre (degrees)" |
| 52 | rad, r, a, 3.0,0,180, "Radius of ROI (degrees)" | 52 | rad, r, a, 3.0,0,180, "Radius of ROI (degrees)" |
| 53 | regfile, f, a, "NONE",,, "Input exclusion region file in ds9 format" | 53 | regfile, f, a, "NONE",,, "Input exclusion region file in ds9 format" |
| 54 | +algorithm, s, a, SIGNIFICANCE,SUB|SUBDIV|SUBDIVSQRT|SIGNIFICANCE,, "Residuals computation algorithm" | ||
| 54 | 55 | ||
| 55 | # | 56 | # |
| 56 | # Standard parameters | 57 | # Standard parameters |
cscripts/csresspec.py
| @@ -81,9 +81,10 @@ class csresspec(ctools.csobservation): | @@ -81,9 +81,10 @@ class csresspec(ctools.csobservation): | ||
| 81 | self._log_value(gammalib.TERSE, 'Binned CTA observations', n_binned) | 81 | self._log_value(gammalib.TERSE, 'Binned CTA observations', n_binned) |
| 82 | self._log_value(gammalib.TERSE, 'On/off CTA observations', n_onoff) | 82 | self._log_value(gammalib.TERSE, 'On/off CTA observations', n_onoff) |
| 83 | self._log_value(gammalib.TERSE, 'Other observations', n_other) | 83 | self._log_value(gammalib.TERSE, 'Other observations', n_other) |
| 84 | - self._log_string(gammalib.TERSE, '\nWARNING: Only CTA observation ' | ||
| 85 | - 'can be handled, all ' | ||
| 86 | - 'non-CTA observation will be ignored.\n') | 84 | + if n_other > 0: |
| 85 | + msg = 'WARNING: Only CTA observation can be handled, all non-CTA ' \ | ||
| 86 | + + 'observations will be ignored.\n' | ||
| 87 | + self._log_string(gammalib.TERSE, msg) | ||
| 87 | 88 | ||
| 88 | # Query wether to compute model for individual components | 89 | # Query wether to compute model for individual components |
| 89 | components = self['components'].boolean() | 90 | components = self['components'].boolean() |
| @@ -109,8 +110,8 @@ class csresspec(ctools.csobservation): | @@ -109,8 +110,8 @@ class csresspec(ctools.csobservation): | ||
| 109 | self._log_value(gammalib.TERSE, msg) | 110 | self._log_value(gammalib.TERSE, msg) |
| 110 | if n_cta > n_unbinned: | 111 | if n_cta > n_unbinned: |
| 111 | n_notunbin = n_cta - n_unbinned | 112 | n_notunbin = n_cta - n_unbinned |
| 112 | - msg = 'The intrinsic binning will be used for the remaining %s observations.' % ( | ||
| 113 | - n_notunbin) | 113 | + msg = 'The intrinsic binning will be used for the remaining %s CTA observations.' % ( |
| 114 | + n_notunbin) | ||
| 114 | self._log_value(gammalib.TERSE, msg) | 115 | self._log_value(gammalib.TERSE, msg) |
| 115 | 116 | ||
| 116 | # If there is more than one observation, and observations are all | 117 | # If there is more than one observation, and observations are all |
| @@ -150,8 +151,17 @@ class csresspec(ctools.csobservation): | @@ -150,8 +151,17 @@ class csresspec(ctools.csobservation): | ||
| 150 | self['dec'].real() | 151 | self['dec'].real() |
| 151 | self['rad'].real() | 152 | self['rad'].real() |
| 152 | 153 | ||
| 153 | - # Apply energy dispersion | ||
| 154 | - self['edisp'].boolean() | 154 | + # Unless all observations are On/Off, or we are using precomputed model |
| 155 | + # maps query whether to use energy dispersion | ||
| 156 | + if n_onoff == n_cta or self._use_maps: | ||
| 157 | + msg = 'Energy dispersion is applied based on the input data/model ' \ | ||
| 158 | + + 'and not according to the edisp parameter' | ||
| 159 | + self._log_value(gammalib.TERSE, msg) | ||
| 160 | + else: | ||
| 161 | + self['edisp'].boolean() | ||
| 162 | + | ||
| 163 | + # Query algorithm for residual computation | ||
| 164 | + self['algorithm'].string() | ||
| 155 | 165 | ||
| 156 | # Read ahead output parameters | 166 | # Read ahead output parameters |
| 157 | if self._read_ahead(): | 167 | if self._read_ahead(): |
| @@ -164,28 +174,118 @@ class csresspec(ctools.csobservation): | @@ -164,28 +174,118 @@ class csresspec(ctools.csobservation): | ||
| 164 | return | 174 | return |
| 165 | 175 | ||
| 166 | def _stack_observations(self): | 176 | def _stack_observations(self): |
| 177 | + """ | ||
| 178 | + Stack multiple observations and replace observation container | ||
| 179 | + with single stacked observation | ||
| 180 | + | ||
| 181 | + :return: stacked_obs container with a single stacked observation | ||
| 182 | + """ | ||
| 167 | # Use first observation to determine the type and apply the | 183 | # Use first observation to determine the type and apply the |
| 168 | - # approptiate stacking method | 184 | + # appropriate stacking method |
| 169 | 185 | ||
| 170 | # If On/Off use the GCTAOnOffObservation constructor | 186 | # If On/Off use the GCTAOnOffObservation constructor |
| 171 | if self.obs()[0].classname() == 'GCTAOnOffObservation': | 187 | if self.obs()[0].classname() == 'GCTAOnOffObservation': |
| 172 | - msg = 'Stacking %s On/Off observations.'%(self.obs().size()) | 188 | + msg = 'Stacking %s On/Off observations.' % (self.obs().size()) |
| 173 | self._log_value(gammalib.TERSE, msg) | 189 | self._log_value(gammalib.TERSE, msg) |
| 174 | - stacked_obs = gammalib.GCTAOnOffObservation(self.obs) | ||
| 175 | - # reset observation container | ||
| 176 | - self.obs = gammalib.GObservations() | ||
| 177 | - # and append the new stacked observation | ||
| 178 | - self.obs.append(stacked_obs) | 190 | + stacked_obs = gammalib.GCTAOnOffObservation(self.obs()) |
| 179 | 191 | ||
| 180 | # If event list then bin observations | 192 | # If event list then bin observations |
| 181 | elif self.obs()[0].classname() == 'GCTAObservation': | 193 | elif self.obs()[0].classname() == 'GCTAObservation': |
| 182 | msg = 'Stacking %s event lists.' % (self.obs().size()) | 194 | msg = 'Stacking %s event lists.' % (self.obs().size()) |
| 183 | self._log_value(gammalib.TERSE, msg) | 195 | self._log_value(gammalib.TERSE, msg) |
| 184 | - stacked_obs = obsutils.get_stacked_obs(self, self.obs) | ||
| 185 | - self.obs = stacked_obs | 196 | + stacked_obs = obsutils.get_stacked_obs(self, self.obs()) |
| 186 | 197 | ||
| 187 | # Return | 198 | # Return |
| 188 | - return | 199 | + return stacked_obs |
| 200 | + | ||
| 201 | + def _bin_evlist(self, obs): | ||
| 202 | + """ | ||
| 203 | + Turn single event list into counts cube | ||
| 204 | + :param obs: observation container with single event list | ||
| 205 | + :return: binned_obs: binned observation container | ||
| 206 | + ra: RA of ROI centre | ||
| 207 | + dec: Dec of ROI centre | ||
| 208 | + rad: ROI radius | ||
| 209 | + """ | ||
| 210 | + # Retrieve information about ROI in event list | ||
| 211 | + roi = obs[0].roi() | ||
| 212 | + ra = roi.centre().dir().ra_deg() | ||
| 213 | + dec = roi.centre().dir().dec_deg() | ||
| 214 | + rad = roi.radius() | ||
| 215 | + | ||
| 216 | + # We will cover the whole ROI with 0.02 deg binning | ||
| 217 | + npix = int(2 * rad / 0.02) + 1 | ||
| 218 | + | ||
| 219 | + # Bin events | ||
| 220 | + cntcube = ctools.ctbin(obs) | ||
| 221 | + cntcube['xref'] = ra | ||
| 222 | + cntcube['yref'] = dec | ||
| 223 | + cntcube['binsz'] = 0.02 | ||
| 224 | + cntcube['nxpix'] = npix | ||
| 225 | + cntcube['nypix'] = npix | ||
| 226 | + cntcube['proj'] = 'CAR' | ||
| 227 | + cntcube['coordsys'] = 'CEL' | ||
| 228 | + cntcube['ebinalg'] = self['ebinalg'].string() | ||
| 229 | + if self['ebinalg'].string() == 'FILE' | ||
| 230 | + cntcube['ebinfile'] = self['ebinfile'].filename() | ||
| 231 | + else: | ||
| 232 | + cntcube['enumbins'] = self['enumbins'].integer() | ||
| 233 | + cntcube['emin'] = self['emin'].real() | ||
| 234 | + cntcube['emax'] = self['emax'].real() | ||
| 235 | + cntcube.run() | ||
| 236 | + | ||
| 237 | + # Retrieve a new oberservation container | ||
| 238 | + binned_obs = cntcube.obs().copy() | ||
| 239 | + | ||
| 240 | + # Retrieve models | ||
| 241 | + models = obs.models() | ||
| 242 | + | ||
| 243 | + # Set models for new oberservation container | ||
| 244 | + binned_obs.models(models) | ||
| 245 | + | ||
| 246 | + # Return new oberservation container | ||
| 247 | + return binned_obs, ra, dec, rad | ||
| 248 | + | ||
| 249 | + def _add_binned_response(self, obs): | ||
| 250 | + """ | ||
| 251 | + Add response to binned observation | ||
| 252 | + :param obs: observation container | ||
| 253 | + :return: obs: observation container with response | ||
| 254 | + """ | ||
| 255 | + | ||
| 256 | + # use counts cube to extract geometry | ||
| 257 | + cube = obs[0].events() | ||
| 258 | + proj = cube.counts().projection() | ||
| 259 | + | ||
| 260 | + response = obsutils.get_stacked_response(obs, | ||
| 261 | + proj.crval(0), | ||
| 262 | + proj.crval(1), | ||
| 263 | + binsz=max(abs(proj.cdelt(0)), | ||
| 264 | + abs(proj.cdelt(1))), | ||
| 265 | + # always ensures coverage of whole cube | ||
| 266 | + nxpix=cube.nx(), | ||
| 267 | + nypix=cube.ny(), | ||
| 268 | + coordsys=proj.coordsys(), | ||
| 269 | + proj=proj.code(), | ||
| 270 | + emin=cube.emin().TeV(), | ||
| 271 | + emax=cube.emax().TeV(), | ||
| 272 | + edisp=self['edisp'].boolean()) | ||
| 273 | + | ||
| 274 | + if self['edisp'].boolean(): | ||
| 275 | + obs[0].response(response['expcube'], response['psfcube'], | ||
| 276 | + response['edispcube'], response['bkgcube']) | ||
| 277 | + else: | ||
| 278 | + obs[0].response(response['expcube'], response['psfcube'], | ||
| 279 | + response['bkgcube']) | ||
| 280 | + | ||
| 281 | + # Get new models | ||
| 282 | + models = response['models'] | ||
| 283 | + | ||
| 284 | + # Set models for observation container | ||
| 285 | + obs.models(models) | ||
| 286 | + | ||
| 287 | + # Return observation container | ||
| 288 | + return obs | ||
| 189 | 289 | ||
| 190 | # Public methods | 290 | # Public methods |
| 191 | def run(self): | 291 | def run(self): |
| @@ -204,12 +304,68 @@ class csresspec(ctools.csobservation): | @@ -204,12 +304,68 @@ class csresspec(ctools.csobservation): | ||
| 204 | 304 | ||
| 205 | # Stack observations if requested | 305 | # Stack observations if requested |
| 206 | if self._stack: | 306 | if self._stack: |
| 207 | - self._stack_observations() | 307 | + self.obs(self._stack_observations()) |
| 208 | 308 | ||
| 209 | # Loop over observations and calculate residuals | 309 | # Loop over observations and calculate residuals |
| 210 | for obs in self.obs(): | 310 | for obs in self.obs(): |
| 211 | - | ||
| 212 | 311 | ||
| 312 | + # Turn into observation container and assign models | ||
| 313 | + obs = gammalib.GObservations(obs) | ||
| 314 | + obs.models(self.obs().models()) | ||
| 315 | + | ||
| 316 | + # if 3D observations | ||
| 317 | + if obs[0].classname() == 'GCTAObservation': | ||
| 318 | + | ||
| 319 | + ## Prepare Observations | ||
| 320 | + | ||
| 321 | + ## If already binned pass | ||
| 322 | + was_list = False | ||
| 323 | + if obs[0].eventtype() == 'CountsCube': | ||
| 324 | + pass | ||
| 325 | + ## Otherwise bin now | ||
| 326 | + else: | ||
| 327 | + # we remember if we binned an event list | ||
| 328 | + # so that we can mask only the ROI for residual calculation | ||
| 329 | + was_list = True | ||
| 330 | + obs, ev_ra, ev_dec, ev_rad = self._bin_evlist(obs) | ||
| 331 | + | ||
| 332 | + ## If binned response is present or model cube provided pass | ||
| 333 | + if obs[0].has_response() or self._use_maps: | ||
| 334 | + pass | ||
| 335 | + ## Otherwise calculate binned response now | ||
| 336 | + else: | ||
| 337 | + obs = self._add_binned_response(obs) | ||
| 338 | + | ||
| 339 | + ## Calculate Model and residuals | ||
| 340 | + | ||
| 341 | + ## If model cube is provided load it | ||
| 342 | + if self._use_maps: | ||
| 343 | + modcube = gammalib.GCTAEventCube(self['inmodel'].filename()) | ||
| 344 | + ## Otherwise calculate it now | ||
| 345 | + else: | ||
| 346 | + modelcube = ctools.ctmodel(obs) | ||
| 347 | + modelcube.run() | ||
| 348 | + modcube = modelcube.cube().copy() | ||
| 349 | + | ||
| 350 | + ## Extract cntcube for residual computation | ||
| 351 | + cntcube = obs[0].events().copy() | ||
| 352 | + | ||
| 353 | + ## If we started from event list mask the ROI only | ||
| 354 | + ## for residual computation | ||
| 355 | + if was_list: | ||
| 356 | + for cube in [cntcube,modcube]: | ||
| 357 | + pass | ||
| 358 | + else: | ||
| 359 | + pass | ||
| 360 | + | ||
| 361 | + | ||
| 362 | + ## Calculate models of individual components if requested | ||
| 363 | + | ||
| 364 | + # otherwise, if On/Off | ||
| 365 | + elif obs[0].classname() == 'GCTAOnOffObservation': | ||
| 213 | 366 | ||
| 367 | + ## Calculate Model and residuals | ||
| 214 | 368 | ||
| 369 | + ## Calculate models of individual components if requested | ||
| 215 | 370 | ||
| 371 | + pass |