Commit 8fd062ec715801378ce9924addc1563006ab833f

Authored by L. Tibaldo
1 parent 8ef51306

treatment of 3D observations

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
... ...