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