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 | 51 | dec, r, a, 22.01,-90,90, "Dec for ROI centre (degrees)" |
| 52 | 52 | rad, r, a, 3.0,0,180, "Radius of ROI (degrees)" |
| 53 | 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 | 57 | # Standard parameters | ... | ... |
cscripts/csresspec.py
| ... | ... | @@ -81,9 +81,10 @@ class csresspec(ctools.csobservation): |
| 81 | 81 | self._log_value(gammalib.TERSE, 'Binned CTA observations', n_binned) |
| 82 | 82 | self._log_value(gammalib.TERSE, 'On/off CTA observations', n_onoff) |
| 83 | 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 | 89 | # Query wether to compute model for individual components |
| 89 | 90 | components = self['components'].boolean() |
| ... | ... | @@ -109,8 +110,8 @@ class csresspec(ctools.csobservation): |
| 109 | 110 | self._log_value(gammalib.TERSE, msg) |
| 110 | 111 | if n_cta > n_unbinned: |
| 111 | 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 | 115 | self._log_value(gammalib.TERSE, msg) |
| 115 | 116 | |
| 116 | 117 | # If there is more than one observation, and observations are all |
| ... | ... | @@ -150,8 +151,17 @@ class csresspec(ctools.csobservation): |
| 150 | 151 | self['dec'].real() |
| 151 | 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 | 166 | # Read ahead output parameters |
| 157 | 167 | if self._read_ahead(): |
| ... | ... | @@ -164,28 +174,118 @@ class csresspec(ctools.csobservation): |
| 164 | 174 | return |
| 165 | 175 | |
| 166 | 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 | 183 | # Use first observation to determine the type and apply the |
| 168 | - # approptiate stacking method | |
| 184 | + # appropriate stacking method | |
| 169 | 185 | |
| 170 | 186 | # If On/Off use the GCTAOnOffObservation constructor |
| 171 | 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 | 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 | 192 | # If event list then bin observations |
| 181 | 193 | elif self.obs()[0].classname() == 'GCTAObservation': |
| 182 | 194 | msg = 'Stacking %s event lists.' % (self.obs().size()) |
| 183 | 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 | 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 | 290 | # Public methods |
| 191 | 291 | def run(self): |
| ... | ... | @@ -204,12 +304,68 @@ class csresspec(ctools.csobservation): |
| 204 | 304 | |
| 205 | 305 | # Stack observations if requested |
| 206 | 306 | if self._stack: |
| 207 | - self._stack_observations() | |
| 307 | + self.obs(self._stack_observations()) | |
| 208 | 308 | |
| 209 | 309 | # Loop over observations and calculate residuals |
| 210 | 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 | ... | ... |