Commit acab87bd6e374ea91e0826b2fd45fbddd077b671
1 parent
f28fae01
Added On/Off analysis
Showing
3 changed files
with
168 additions
and
126 deletions
cscripts/csscs.par
| @@ -18,14 +18,14 @@ | @@ -18,14 +18,14 @@ | ||
| 18 | #============================ | 18 | #============================ |
| 19 | inobs, f, a, events.fits,,, "Input event list, counts cube, or observation definition XML file" | 19 | inobs, f, a, events.fits,,, "Input event list, counts cube, or observation definition XML file" |
| 20 | inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file" | 20 | inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file" |
| 21 | -srcnames, s, a, Crab,,, "Semicolon separated list of sources" | 21 | +srcnames, s, a, Crab,,, "Semicolon-separated list of sources" |
| 22 | expcube, f, a, NONE,,, "Input exposure cube file" | 22 | expcube, f, a, NONE,,, "Input exposure cube file" |
| 23 | psfcube, f, a, NONE,,, "Input PSF cube file" | 23 | psfcube, f, a, NONE,,, "Input PSF cube file" |
| 24 | edispcube, f, a, NONE,,, "Input energy dispersion cube file" | 24 | edispcube, f, a, NONE,,, "Input energy dispersion cube file" |
| 25 | bkgcube, f, a, NONE,,, "Input background cube file" | 25 | bkgcube, f, a, NONE,,, "Input background cube file" |
| 26 | caldb, s, a, prod2,,, "Calibration database" | 26 | caldb, s, a, prod2,,, "Calibration database" |
| 27 | irf, s, a, South_0.5h,,, "Instrument response function" | 27 | irf, s, a, South_0.5h,,, "Instrument response function" |
| 28 | -inexclusion, f, h, NONE,,, "Exclusion regions" | 28 | +inexclusion, f, a, NONE,,, "Exclusion regions TODO: add meaningful default" |
| 29 | edisp, b, h, no,,, "Apply energy dispersion?" | 29 | edisp, b, h, no,,, "Apply energy dispersion?" |
| 30 | outfile, f, a, map.fits,,, "Output map" | 30 | outfile, f, a, map.fits,,, "Output map" |
| 31 | 31 | ||
| @@ -39,9 +39,9 @@ yref, r, a, 22.51,-90,90, "Second coordinate of image center in degrees | @@ -39,9 +39,9 @@ yref, r, a, 22.51,-90,90, "Second coordinate of image center in degrees | ||
| 39 | nxpix, i, a, 20,,, "Size of the X axis in pixels" | 39 | nxpix, i, a, 20,,, "Size of the X axis in pixels" |
| 40 | nypix, i, a, 20,,, "Size of the Y axis in pixels" | 40 | nypix, i, a, 20,,, "Size of the Y axis in pixels" |
| 41 | binsz, r, a, 0.1,,, "Pixel size (deg/pixel)" | 41 | binsz, r, a, 0.1,,, "Pixel size (deg/pixel)" |
| 42 | -roisz, r, a, 2.,1.42,1000, "Size of the region of interest (numbers of binsz)" | 42 | +rad, r, a, 0.2,,, "Radius of the region for component separation (deg)" |
| 43 | statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic" | 43 | statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic" |
| 44 | -method, s, a, 3D, 3D|ONOFF,, "Analysis method" | 44 | +method, s, a, UNBINNED, UNBINNED|ONOFF,, "Analysis method" |
| 45 | ebinalg, s, a, LOG,FILE|LIN|LOG,, "Algorithm for defining energy bins" | 45 | ebinalg, s, a, LOG,FILE|LIN|LOG,, "Algorithm for defining energy bins" |
| 46 | emin, r, a, 0.1,,, "Lower energy limit (TeV)" | 46 | emin, r, a, 0.1,,, "Lower energy limit (TeV)" |
| 47 | emax, r, a, 100.0,,, "Upper energy limit (TeV)" | 47 | emax, r, a, 100.0,,, "Upper energy limit (TeV)" |
| @@ -49,6 +49,7 @@ enumbins, i, a, 20,,, "Number of energy bins" | @@ -49,6 +49,7 @@ enumbins, i, a, 20,,, "Number of energy bins" | ||
| 49 | bkgmethod, s, h, REFLECTED,REFLECTED,, "Method for background estimation" | 49 | bkgmethod, s, h, REFLECTED,REFLECTED,, "Method for background estimation" |
| 50 | srcshape, s, h, CIRCLE,CIRCLE,, "Shape of the source region" | 50 | srcshape, s, h, CIRCLE,CIRCLE,, "Shape of the source region" |
| 51 | bkgregmin, i, h, 2,1,1000, "Minimum number of background regions" | 51 | bkgregmin, i, h, 2,1,1000, "Minimum number of background regions" |
| 52 | +bkgregskip, i, h, 1,0,10, "Number of background regions to skip next to On region" | ||
| 52 | use_model_bkg, b, h, yes,,, "Use background model?" | 53 | use_model_bkg, b, h, yes,,, "Use background model?" |
| 53 | maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation" | 54 | maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation" |
| 54 | etruemin, r, h, 0.01,,, "Minimum true energy (TeV)" | 55 | etruemin, r, h, 0.01,,, "Minimum true energy (TeV)" |
cscripts/csscs.py
| @@ -50,8 +50,8 @@ class csscs(ctools.csobservation): | @@ -50,8 +50,8 @@ class csscs(ctools.csobservation): | ||
| 50 | 50 | ||
| 51 | # Initialise data members | 51 | # Initialise data members |
| 52 | self._nthreads = 0 | 52 | self._nthreads = 0 |
| 53 | - self._roisz = 0. | ||
| 54 | self._srcnames = [] | 53 | self._srcnames = [] |
| 54 | + self._method = None | ||
| 55 | self._fits = None | 55 | self._fits = None |
| 56 | self._excl_reg_map = None # Exclusion region map for on/off analysis | 56 | self._excl_reg_map = None # Exclusion region map for on/off analysis |
| 57 | 57 | ||
| @@ -72,7 +72,7 @@ class csscs(ctools.csobservation): | @@ -72,7 +72,7 @@ class csscs(ctools.csobservation): | ||
| 72 | # Set pickled dictionary | 72 | # Set pickled dictionary |
| 73 | state = {'base' : ctools.csobservation.__getstate__(self), | 73 | state = {'base' : ctools.csobservation.__getstate__(self), |
| 74 | 'nthreads' : self._nthreads, | 74 | 'nthreads' : self._nthreads, |
| 75 | - 'roisz' : self._roisz, | 75 | + 'method' : self._method, |
| 76 | 'srcnames' : self._srcnames, | 76 | 'srcnames' : self._srcnames, |
| 77 | 'fits' : self._fits, | 77 | 'fits' : self._fits, |
| 78 | 'excl_reg_map' : self._excl_reg_map} | 78 | 'excl_reg_map' : self._excl_reg_map} |
| @@ -92,8 +92,8 @@ class csscs(ctools.csobservation): | @@ -92,8 +92,8 @@ class csscs(ctools.csobservation): | ||
| 92 | # Set state | 92 | # Set state |
| 93 | ctools.csobservation.__setstate__(self, state['base']) | 93 | ctools.csobservation.__setstate__(self, state['base']) |
| 94 | self._nthreads = state['nthreads'] | 94 | self._nthreads = state['nthreads'] |
| 95 | - self._roisz = state['roisz'] | ||
| 96 | self._srcnames = state['srcnames'] | 95 | self._srcnames = state['srcnames'] |
| 96 | + self._method = state['method'] | ||
| 97 | self._fits = state['fits'] | 97 | self._fits = state['fits'] |
| 98 | self._excl_reg_map = state['excl_reg_map'] | 98 | self._excl_reg_map = state['excl_reg_map'] |
| 99 | 99 | ||
| @@ -102,6 +102,39 @@ class csscs(ctools.csobservation): | @@ -102,6 +102,39 @@ class csscs(ctools.csobservation): | ||
| 102 | 102 | ||
| 103 | 103 | ||
| 104 | # Private methods | 104 | # Private methods |
| 105 | + def _get_onoff_parameters(self): | ||
| 106 | + """ | ||
| 107 | + Get On/Off analysis parameters. | ||
| 108 | + This is done here rather then in ctools.is_onoff | ||
| 109 | + because the exclusion map needs to be handled differently | ||
| 110 | + as an automatic parameter but queried only if not already set | ||
| 111 | + and we need to verify is not empty. | ||
| 112 | + Also many parameters are already queried for all methods | ||
| 113 | + TODO: verify if this can be made more uniform with other scripts | ||
| 114 | + """ | ||
| 115 | + # Exclusion map | ||
| 116 | + if (self._excl_reg_map is not None) and (self._excl_reg_map.map().npix() > 0): | ||
| 117 | + # Exclusion map set and is not empty | ||
| 118 | + pass | ||
| 119 | + elif self['inexclusion'].is_valid(): | ||
| 120 | + inexclusion = self['inexclusion'].filename() | ||
| 121 | + self._excl_reg_map = gammalib.GSkyRegionMap(inexclusion) | ||
| 122 | + else: | ||
| 123 | + msg = 'csscs in On/Off mode requires input exclusion region.' | ||
| 124 | + raise RuntimeError(msg) | ||
| 125 | + | ||
| 126 | + # Other csphagen parameters | ||
| 127 | + self["enumbins"].integer() | ||
| 128 | + if self["bkgmethod"].string() == "REFLECTED": | ||
| 129 | + self["bkgregmin"].integer() | ||
| 130 | + self['bkgregskip'].integer() | ||
| 131 | + self["maxoffset"].real() | ||
| 132 | + self["etruemin"].real() | ||
| 133 | + self["etruemax"].real() | ||
| 134 | + self["etruebins"].integer() | ||
| 135 | + | ||
| 136 | + return | ||
| 137 | + | ||
| 105 | def _get_parameters(self): | 138 | def _get_parameters(self): |
| 106 | """ | 139 | """ |
| 107 | Get parameters from parfile | 140 | Get parameters from parfile |
| @@ -111,6 +144,9 @@ class csscs(ctools.csobservation): | @@ -111,6 +144,9 @@ class csscs(ctools.csobservation): | ||
| 111 | if self.obs().is_empty(): | 144 | if self.obs().is_empty(): |
| 112 | self.obs(self._get_observations()) | 145 | self.obs(self._get_observations()) |
| 113 | 146 | ||
| 147 | + # Set observation statistic | ||
| 148 | + self._set_obs_statistic(gammalib.toupper(self['statistic'].string())) | ||
| 149 | + | ||
| 114 | # Collect number of unbinned, binned and On/Off observations in | 150 | # Collect number of unbinned, binned and On/Off observations in |
| 115 | # observation container | 151 | # observation container |
| 116 | n_unbinned = 0 | 152 | n_unbinned = 0 |
| @@ -124,24 +160,25 @@ class csscs(ctools.csobservation): | @@ -124,24 +160,25 @@ class csscs(ctools.csobservation): | ||
| 124 | n_unbinned += 1 | 160 | n_unbinned += 1 |
| 125 | elif obs.classname() == 'GCTAOnOffObservation': | 161 | elif obs.classname() == 'GCTAOnOffObservation': |
| 126 | n_onoff += 1 | 162 | n_onoff += 1 |
| 163 | + n_cta = n_unbinned + n_binned + n_onoff | ||
| 164 | + n_other = self.obs().size() - n_cta | ||
| 127 | 165 | ||
| 128 | - # Check that we have at least one unbinned or binned CTA observation | ||
| 129 | - if n_unbinned == 0 and n_binned == 0: | ||
| 130 | - msg = 'No unbinned or binned CTA observations found. ' \ | ||
| 131 | - 'Please provide at least one unbinned or binned ' \ | ||
| 132 | - 'CTA observation.' | 166 | + # Check if there are On/Off or non-IACT observations |
| 167 | + if n_onoff > 0 or n_other > 0: | ||
| 168 | + msg = 'On/Off or non-CTA observations found. '\ | ||
| 169 | + 'csscs does not support this type of observations.' | ||
| 170 | + raise RuntimeError(msg) | ||
| 171 | + # Otherwise check if there is a mix of binned and unbinned | ||
| 172 | + elif n_unbinned > 0 and n_binned>0: | ||
| 173 | + msg = 'Mix of unbinned and binned CTA observations ' \ | ||
| 174 | + 'found in observation container. csscs does not ' \ | ||
| 175 | + 'support this mix.' | ||
| 133 | raise RuntimeError(msg) | 176 | raise RuntimeError(msg) |
| 134 | - | ||
| 135 | - # Set observation statistic | ||
| 136 | - self._set_obs_statistic(gammalib.toupper(self['statistic'].string())) | ||
| 137 | 177 | ||
| 138 | # Set models if there are none in the container | 178 | # Set models if there are none in the container |
| 139 | if self.obs().models().size() == 0: | 179 | if self.obs().models().size() == 0: |
| 140 | self.obs().models(self['inmodel'].filename()) | 180 | self.obs().models(self['inmodel'].filename()) |
| 141 | 181 | ||
| 142 | - # Query exclusion region | ||
| 143 | - self["inexclusion"].query() | ||
| 144 | - | ||
| 145 | # Query the map definition parameters | 182 | # Query the map definition parameters |
| 146 | self['xref'].real() | 183 | self['xref'].real() |
| 147 | self['yref'].real() | 184 | self['yref'].real() |
| @@ -149,29 +186,24 @@ class csscs(ctools.csobservation): | @@ -149,29 +186,24 @@ class csscs(ctools.csobservation): | ||
| 149 | self['proj'].string() | 186 | self['proj'].string() |
| 150 | self['nxpix'].integer() | 187 | self['nxpix'].integer() |
| 151 | self['nypix'].integer() | 188 | self['nypix'].integer() |
| 189 | + self['binsz'].real() | ||
| 152 | 190 | ||
| 153 | - # Compute the size of the ROIs for the analysis in deg | ||
| 154 | - self._roisz = self['binsz'].real() * self['roisz'].real() | 191 | + # Radius of ROI for component separation |
| 192 | + self['rad'].real() | ||
| 155 | 193 | ||
| 156 | - # Query energy boundaries | 194 | + # Energy boundaries |
| 157 | self["emin"].real() | 195 | self["emin"].real() |
| 158 | self["emax"].real() | 196 | self["emax"].real() |
| 159 | 197 | ||
| 160 | - # If we have unbinned observations query the analysis method | 198 | + # If we have unbinned observations query analysis method |
| 161 | if n_unbinned > 0: | 199 | if n_unbinned > 0: |
| 162 | - # If method is ONOFF query some csphagen parameters | ||
| 163 | - if self['method'].string() == 'ONOFF': | ||
| 164 | - pass | ||
| 165 | - # TODO: implement Onoff analysis | ||
| 166 | - # self["enumbins"].integer() | ||
| 167 | - # self['use_model_bkg'].boolean() | ||
| 168 | - # if self["bkgmethod"].string() == "REFLECTED": | ||
| 169 | - # self["srcshape"].string() | ||
| 170 | - # self["bkgregmin"].integer() | ||
| 171 | - # self["maxoffset"].real() | ||
| 172 | - # self["etruemin"].real() | ||
| 173 | - # self["etruemax"].real() | ||
| 174 | - # self["etruebins"].integer() | 200 | + self._method = self['method'].string() |
| 201 | + # If method is Onoff query csphagen parameters | ||
| 202 | + if self._method == 'ONOFF': | ||
| 203 | + self._get_onoff_parameters() | ||
| 204 | + # Otherwise set method to binned | ||
| 205 | + else: | ||
| 206 | + self._method = 'BINNED' | ||
| 175 | 207 | ||
| 176 | # Query target source names | 208 | # Query target source names |
| 177 | srcnames = self['srcnames'].string() | 209 | srcnames = self['srcnames'].string() |
| @@ -190,7 +222,27 @@ class csscs(ctools.csobservation): | @@ -190,7 +222,27 @@ class csscs(ctools.csobservation): | ||
| 190 | msg = 'Not all target sources are present in input model.' | 222 | msg = 'Not all target sources are present in input model.' |
| 191 | raise RuntimeError(msg) | 223 | raise RuntimeError(msg) |
| 192 | 224 | ||
| 193 | - # Query the hidden parameters, just in case | 225 | + # For On/Off analysis check the number of gamma-ray sources in the model |
| 226 | + if self._method == 'ONOFF': | ||
| 227 | + nsources = 0 | ||
| 228 | + # Verify if model is of type GModelSky | ||
| 229 | + for model in self.obs().models(): | ||
| 230 | + try: | ||
| 231 | + gammalib.GModelSky(model) | ||
| 232 | + nsources +=1 | ||
| 233 | + except: | ||
| 234 | + pass | ||
| 235 | + # If there are background gamma-ray sources in the model | ||
| 236 | + # throw runtime error | ||
| 237 | + if nsources > len(self._srcnames): | ||
| 238 | + msg = 'Background gamma-ray sources found in the model. ' \ | ||
| 239 | + 'On/Off analysis does not support this feature.' | ||
| 240 | + raise RuntimeError(msg) | ||
| 241 | + # Otherwise continue | ||
| 242 | + else: | ||
| 243 | + pass | ||
| 244 | + | ||
| 245 | + # Query other hidden parameters, just in case | ||
| 194 | self['edisp'].boolean() | 246 | self['edisp'].boolean() |
| 195 | self['calc_ulim'].boolean() | 247 | self['calc_ulim'].boolean() |
| 196 | self['calc_ts'].boolean() | 248 | self['calc_ts'].boolean() |
| @@ -243,6 +295,24 @@ class csscs(ctools.csobservation): | @@ -243,6 +295,24 @@ class csscs(ctools.csobservation): | ||
| 243 | # Log model name | 295 | # Log model name |
| 244 | self._log_header3(gammalib.EXPLICIT, model.name()) | 296 | self._log_header3(gammalib.EXPLICIT, model.name()) |
| 245 | 297 | ||
| 298 | + # In On/Off mode we cannot support multiple source morphologies | ||
| 299 | + # Thus, if we have more than one target we will set them all | ||
| 300 | + # to isotropic | ||
| 301 | + if self._method == 'ONOFF' and len(self._srcnames) > 1: | ||
| 302 | + # Check if it is a source | ||
| 303 | + try: | ||
| 304 | + gammalib.GModelSky(model) | ||
| 305 | + # In this case check if the spatial model is already isotropic | ||
| 306 | + if model.spatial() == 'DiffuseIsotropic': | ||
| 307 | + pass | ||
| 308 | + # Otherwise change it to isotropic | ||
| 309 | + else: | ||
| 310 | + msg = ' Setting spatial model to diffuse isotropic' | ||
| 311 | + self._log_string(gammalib.EXPLICIT, msg) | ||
| 312 | + model.spatial(gammalib.GModelSpatialDiffuseConst()) | ||
| 313 | + except: | ||
| 314 | + pass | ||
| 315 | + | ||
| 246 | # Freeze all parameters except the normalization | 316 | # Freeze all parameters except the normalization |
| 247 | # which is assumed to be the first spectral parameter | 317 | # which is assumed to be the first spectral parameter |
| 248 | normpar = model.spectral()[0] | 318 | normpar = model.spectral()[0] |
| @@ -298,26 +368,29 @@ class csscs(ctools.csobservation): | @@ -298,26 +368,29 @@ class csscs(ctools.csobservation): | ||
| 298 | 368 | ||
| 299 | Returns | 369 | Returns |
| 300 | ------- | 370 | ------- |
| 301 | - new_obs : `gammalib.GCTAObservation` | ||
| 302 | - Output observation with masked cube | 371 | + new_obs : `gammalib.GCTAObservations` |
| 372 | + Output observations with masked cubes | ||
| 303 | """ | 373 | """ |
| 304 | 374 | ||
| 305 | - # Put observation in container | ||
| 306 | - container = gammalib.GObservations() | ||
| 307 | - container.append(obs) | ||
| 308 | - | ||
| 309 | # Filter cube according to ROI and user energy range | 375 | # Filter cube according to ROI and user energy range |
| 310 | - cubemask = ctools.ctcubemask(container) | 376 | + cubemask = ctools.ctcubemask(obs) |
| 311 | cubemask['ra'] = ra | 377 | cubemask['ra'] = ra |
| 312 | cubemask['dec'] = dec | 378 | cubemask['dec'] = dec |
| 313 | cubemask['rad'] = rad | 379 | cubemask['rad'] = rad |
| 314 | cubemask['regfile'] = 'NONE' | 380 | cubemask['regfile'] = 'NONE' |
| 315 | cubemask['emin'] = self['emin'].real() | 381 | cubemask['emin'] = self['emin'].real() |
| 316 | cubemask['emax'] = self['emax'].real() | 382 | cubemask['emax'] = self['emax'].real() |
| 383 | + | ||
| 384 | + # If chatter level is verbose and debugging is requested then | ||
| 385 | + # switch also on the debug model in ctcubemask | ||
| 386 | + if self._logVerbose() and self._logDebug(): | ||
| 387 | + cubemask['debug'] = True | ||
| 388 | + | ||
| 389 | + # Mask cube | ||
| 317 | cubemask.run() | 390 | cubemask.run() |
| 318 | 391 | ||
| 319 | - # Get deep copy of filtered observation | ||
| 320 | - new_obs = cubemask.obs()[0].copy() | 392 | + # Get deep copy of filtered observations |
| 393 | + new_obs = cubemask.obs().copy() | ||
| 321 | 394 | ||
| 322 | # Return | 395 | # Return |
| 323 | return new_obs | 396 | return new_obs |
| @@ -328,23 +401,19 @@ class csscs(ctools.csobservation): | @@ -328,23 +401,19 @@ class csscs(ctools.csobservation): | ||
| 328 | 401 | ||
| 329 | Parameters | 402 | Parameters |
| 330 | ---------- | 403 | ---------- |
| 331 | - obs : `gammalib.GCTAObservation` Input observation of type cube | 404 | + obs : `gammalib.GCTAObservations` Input observations of type event list |
| 332 | ra : `float` R.A. (deg) | 405 | ra : `float` R.A. (deg) |
| 333 | dec : `float` Dec (deg) | 406 | dec : `float` Dec (deg) |
| 334 | rad : `float` radius (deg) | 407 | rad : `float` radius (deg) |
| 335 | 408 | ||
| 336 | Returns | 409 | Returns |
| 337 | ------- | 410 | ------- |
| 338 | - new_obs : `gammalib.GCTAObservation` | ||
| 339 | - Output observation with masked event list | 411 | + new_obs : `gammalib.GCTAObservations` |
| 412 | + Output observations with masked event lists | ||
| 340 | """ | 413 | """ |
| 341 | 414 | ||
| 342 | - # Put observation in container | ||
| 343 | - container = gammalib.GObservations() | ||
| 344 | - container.append(obs) | ||
| 345 | - | ||
| 346 | # Filter event list according to ROI and user energy range | 415 | # Filter event list according to ROI and user energy range |
| 347 | - select = ctools.ctselect(container) | 416 | + select = ctools.ctselect(obs) |
| 348 | select['ra'] = ra | 417 | select['ra'] = ra |
| 349 | select['dec'] = dec | 418 | select['dec'] = dec |
| 350 | select['rad'] = rad | 419 | select['rad'] = rad |
| @@ -352,40 +421,21 @@ class csscs(ctools.csobservation): | @@ -352,40 +421,21 @@ class csscs(ctools.csobservation): | ||
| 352 | select['emax'] = self['emax'].real() | 421 | select['emax'] = self['emax'].real() |
| 353 | select['tmin'] = 'INDEF' | 422 | select['tmin'] = 'INDEF' |
| 354 | select['tmax'] = 'INDEF' | 423 | select['tmax'] = 'INDEF' |
| 424 | + | ||
| 425 | + # If chatter level is verbose and debugging is requested then | ||
| 426 | + # switch also on the debug model in ctcubemask | ||
| 427 | + if self._logVerbose() and self._logDebug(): | ||
| 428 | + select['debug'] = True | ||
| 429 | + | ||
| 430 | + # Select event list | ||
| 355 | select.run() | 431 | select.run() |
| 356 | 432 | ||
| 357 | # Get deep copy of filtered observation | 433 | # Get deep copy of filtered observation |
| 358 | - new_obs = select.obs()[0].copy() | 434 | + new_obs = select.obs().copy() |
| 359 | 435 | ||
| 360 | # Return | 436 | # Return |
| 361 | return new_obs | 437 | return new_obs |
| 362 | 438 | ||
| 363 | - def _mask_onoff(self,obs,ra,dec,rad): | ||
| 364 | - """ | ||
| 365 | - Create On/Off observation | ||
| 366 | - with On region matching the required mask | ||
| 367 | - | ||
| 368 | - Parameters | ||
| 369 | - ---------- | ||
| 370 | - obs : `gammalib.GCTAObservation` Input observation of type cube | ||
| 371 | - ra : `float` R.A. (deg) | ||
| 372 | - dec : `float` Dec (deg) | ||
| 373 | - rad : `float` radius (deg) | ||
| 374 | - | ||
| 375 | - Returns | ||
| 376 | - ------- | ||
| 377 | - new_obs : `gammalib.GCTAOnoffObservation` | ||
| 378 | - Output On/Off observation | ||
| 379 | - """ | ||
| 380 | - | ||
| 381 | - # Put observation in container | ||
| 382 | - container = gammalib.GObservations() | ||
| 383 | - container.append(obs) | ||
| 384 | - | ||
| 385 | - onoff_obs = obsutils.get_onoff_obs(self, container, ra=ra, dec=dec, rad=rad) | ||
| 386 | - | ||
| 387 | - return onoff_obs[0] | ||
| 388 | - | ||
| 389 | def _mask_observations(self,ra,dec,rad): | 439 | def _mask_observations(self,ra,dec,rad): |
| 390 | """ | 440 | """ |
| 391 | Create observations restricted to circular ROI | 441 | Create observations restricted to circular ROI |
| @@ -402,29 +452,16 @@ class csscs(ctools.csobservation): | @@ -402,29 +452,16 @@ class csscs(ctools.csobservation): | ||
| 402 | Observations in circular ROI | 452 | Observations in circular ROI |
| 403 | """ | 453 | """ |
| 404 | 454 | ||
| 405 | - # Create output observation container | ||
| 406 | - new_obs = gammalib.GObservations() | ||
| 407 | - | ||
| 408 | - # Loop over input observations | ||
| 409 | - for obs in self.obs(): | ||
| 410 | - if obs.classname() == 'GCTAObservation': | ||
| 411 | - if obs.eventtype() == 'CountsCube': | ||
| 412 | - masked_obs = self._mask_cube(obs,ra,dec,rad) | ||
| 413 | - else: | ||
| 414 | - if self['method'].string() == '3D': | ||
| 415 | - masked_obs = self._mask_evlist(obs,ra,dec,rad) | ||
| 416 | - elif self['method'].string() == 'ONOFF': | ||
| 417 | - pass | ||
| 418 | - pass | ||
| 419 | - # TODO: implement Onoff analysis | ||
| 420 | - # masked_obs = self._mask_onoff(obs, ra, dec, rad) | ||
| 421 | - new_obs.append(masked_obs) | ||
| 422 | - # Skip On-Off and non-CTA observations | ||
| 423 | - else: | ||
| 424 | - pass | ||
| 425 | - | ||
| 426 | - # Append models to output observations | ||
| 427 | - new_obs.models(self.obs().models()) | 455 | + # Determine type of masking according |
| 456 | + # to observation type and analysis method | ||
| 457 | + if self._method == 'UNBINNED': | ||
| 458 | + new_obs = self._mask_evlist(self.obs(), ra, dec, rad) | ||
| 459 | + elif self._method == 'BINNED': | ||
| 460 | + new_obs = self._mask_cube(self.obs(), ra, dec, rad) | ||
| 461 | + elif self._method == 'ONOFF': | ||
| 462 | + new_obs = obsutils.get_onoff_obs(self,self.obs(),nthreads=1, | ||
| 463 | + ra = ra, dec = dec, | ||
| 464 | + srcname = self._srcnames[0]) | ||
| 428 | 465 | ||
| 429 | # Return | 466 | # Return |
| 430 | return new_obs | 467 | return new_obs |
| @@ -465,7 +502,7 @@ class csscs(ctools.csobservation): | @@ -465,7 +502,7 @@ class csscs(ctools.csobservation): | ||
| 465 | 502 | ||
| 466 | # Mask observations | 503 | # Mask observations |
| 467 | self._log_header3(gammalib.EXPLICIT, 'Masking observations') | 504 | self._log_header3(gammalib.EXPLICIT, 'Masking observations') |
| 468 | - masked_obs = self._mask_observations(ra,dec,self._roisz) | 505 | + masked_obs = self._mask_observations(ra,dec,self['rad'].real()) |
| 469 | 506 | ||
| 470 | # Set up likelihood analysis | 507 | # Set up likelihood analysis |
| 471 | self._log_header3(gammalib.EXPLICIT, 'Performing fit in energy bin') | 508 | self._log_header3(gammalib.EXPLICIT, 'Performing fit in energy bin') |
| @@ -488,7 +525,7 @@ class csscs(ctools.csobservation): | @@ -488,7 +525,7 @@ class csscs(ctools.csobservation): | ||
| 488 | # ROI | 525 | # ROI |
| 489 | centre = gammalib.GSkyDir() | 526 | centre = gammalib.GSkyDir() |
| 490 | centre.radec_deg(ra,dec) | 527 | centre.radec_deg(ra,dec) |
| 491 | - roi = gammalib.GSkyRegionCircle(centre,self._roisz) | 528 | + roi = gammalib.GSkyRegionCircle(centre,self['rad'].real()) |
| 492 | # Energy boundaries | 529 | # Energy boundaries |
| 493 | emin = gammalib.GEnergy(self['emin'].real(),'TeV') | 530 | emin = gammalib.GEnergy(self['emin'].real(),'TeV') |
| 494 | emax = gammalib.GEnergy(self['emax'].real(), 'TeV') | 531 | emax = gammalib.GEnergy(self['emax'].real(), 'TeV') |
| @@ -508,7 +545,7 @@ class csscs(ctools.csobservation): | @@ -508,7 +545,7 @@ class csscs(ctools.csobservation): | ||
| 508 | # Multiply by flux from spatial component in ROI | 545 | # Multiply by flux from spatial component in ROI |
| 509 | flux *= source.spatial().flux(roi) | 546 | flux *= source.spatial().flux(roi) |
| 510 | # Divide by solid angle of ROI | 547 | # Divide by solid angle of ROI |
| 511 | - flux /= gammalib.twopi * (1 - math.cos(math.radians(self._roisz))) | 548 | + flux /= gammalib.twopi * (1 - math.cos(math.radians(self['rad'].real()))) |
| 512 | result[name]['flux'] = flux | 549 | result[name]['flux'] = flux |
| 513 | 550 | ||
| 514 | # Get flux error | 551 | # Get flux error |
| @@ -561,7 +598,7 @@ class csscs(ctools.csobservation): | @@ -561,7 +598,7 @@ class csscs(ctools.csobservation): | ||
| 561 | # Prepare some header cards | 598 | # Prepare some header cards |
| 562 | # Flux units | 599 | # Flux units |
| 563 | fcard = gammalib.GFitsHeaderCard('BUNIT', 'ph/cm2/s/sr' , 'Photon flux') | 600 | fcard = gammalib.GFitsHeaderCard('BUNIT', 'ph/cm2/s/sr' , 'Photon flux') |
| 564 | - txt = 'Correlation radius %f deg' %(self._roisz) | 601 | + txt = 'Correlation radius %f deg' %(self['rad'].real()) |
| 565 | roiszcard = gammalib.GFitsHeaderCard('COMMENT', '' , txt) | 602 | roiszcard = gammalib.GFitsHeaderCard('COMMENT', '' , txt) |
| 566 | 603 | ||
| 567 | # Loop over target sources | 604 | # Loop over target sources |
| @@ -646,14 +683,6 @@ class csscs(ctools.csobservation): | @@ -646,14 +683,6 @@ class csscs(ctools.csobservation): | ||
| 646 | for i in range(npix): | 683 | for i in range(npix): |
| 647 | results.append(self._pixel_analysis(i)) | 684 | results.append(self._pixel_analysis(i)) |
| 648 | 685 | ||
| 649 | - # # Loop over pixels and extract results | ||
| 650 | - # results = [] | ||
| 651 | - # for inx in range(self._create_map().npix()): | ||
| 652 | - # | ||
| 653 | - # # Analyse ROI | ||
| 654 | - # result = self._pixel_analysis(inx) | ||
| 655 | - # results.append(result) | ||
| 656 | - | ||
| 657 | # Fill output Fits | 686 | # Fill output Fits |
| 658 | self._fits = self._fill_fits(results) | 687 | self._fits = self._fill_fits(results) |
| 659 | 688 |
cscripts/obsutils.py
| @@ -745,7 +745,7 @@ def get_stacked_obs(cls, obs, nthreads=0): | @@ -745,7 +745,7 @@ def get_stacked_obs(cls, obs, nthreads=0): | ||
| 745 | # ================================ # | 745 | # ================================ # |
| 746 | # Get On/Off observation container # | 746 | # Get On/Off observation container # |
| 747 | # ================================ # | 747 | # ================================ # |
| 748 | -def get_onoff_obs(cls, obs, nthreads=0): | 748 | +def get_onoff_obs(cls, obs, nthreads=0, ra = None, dec = None, srcname = ''): |
| 749 | """ | 749 | """ |
| 750 | Create On/Off observations container from given observations | 750 | Create On/Off observations container from given observations |
| 751 | 751 | ||
| @@ -757,6 +757,12 @@ def get_onoff_obs(cls, obs, nthreads=0): | @@ -757,6 +757,12 @@ def get_onoff_obs(cls, obs, nthreads=0): | ||
| 757 | Observation container | 757 | Observation container |
| 758 | nthreads : str, optional | 758 | nthreads : str, optional |
| 759 | Number of parallel processes for On/Off spectra computation (0=all available CPUs) | 759 | Number of parallel processes for On/Off spectra computation (0=all available CPUs) |
| 760 | + ra : float, optional | ||
| 761 | + R.A. of source region centre | ||
| 762 | + dec : float, optional | ||
| 763 | + Dec. of source region centre | ||
| 764 | + srcname : str, optional | ||
| 765 | + Source name | ||
| 760 | 766 | ||
| 761 | Returns | 767 | Returns |
| 762 | ------- | 768 | ------- |
| @@ -767,9 +773,8 @@ def get_onoff_obs(cls, obs, nthreads=0): | @@ -767,9 +773,8 @@ def get_onoff_obs(cls, obs, nthreads=0): | ||
| 767 | if cls._logExplicit(): | 773 | if cls._logExplicit(): |
| 768 | cls._log.header3('Creating On/Off observations') | 774 | cls._log.header3('Creating On/Off observations') |
| 769 | 775 | ||
| 770 | - # Initialise inmodel, srcname, inexclusion and use_model_bkg | 776 | + # Initialise inmodel, inexclusion and use_model_bkg |
| 771 | inmodel = 'NONE' | 777 | inmodel = 'NONE' |
| 772 | - srcname = '' | ||
| 773 | inexclusion = 'NONE' | 778 | inexclusion = 'NONE' |
| 774 | use_model_bkg = True | 779 | use_model_bkg = True |
| 775 | 780 | ||
| @@ -777,7 +782,7 @@ def get_onoff_obs(cls, obs, nthreads=0): | @@ -777,7 +782,7 @@ def get_onoff_obs(cls, obs, nthreads=0): | ||
| 777 | if cls.has_par('inmodel') and obs.models().size() == 0: | 782 | if cls.has_par('inmodel') and obs.models().size() == 0: |
| 778 | if cls['inmodel'].is_valid(): | 783 | if cls['inmodel'].is_valid(): |
| 779 | inmodel = cls['inmodel'].value() | 784 | inmodel = cls['inmodel'].value() |
| 780 | - if cls.has_par('srcname'): | 785 | + if srcname == '' and cls.has_par('srcname'): |
| 781 | srcname = cls['srcname'].value() | 786 | srcname = cls['srcname'].value() |
| 782 | if cls.has_par('inexclusion'): | 787 | if cls.has_par('inexclusion'): |
| 783 | if cls['inexclusion'].is_valid(): | 788 | if cls['inexclusion'].is_valid(): |
| @@ -795,13 +800,20 @@ def get_onoff_obs(cls, obs, nthreads=0): | @@ -795,13 +800,20 @@ def get_onoff_obs(cls, obs, nthreads=0): | ||
| 795 | phagen['enumbins'] = cls['enumbins'].integer() | 800 | phagen['enumbins'] = cls['enumbins'].integer() |
| 796 | phagen['ebinalg'] = 'LOG' | 801 | phagen['ebinalg'] = 'LOG' |
| 797 | phagen['srcshape'] = cls['srcshape'].string() | 802 | phagen['srcshape'] = cls['srcshape'].string() |
| 798 | - phagen['coordsys'] = cls['coordsys'].string() | ||
| 799 | - if cls['coordsys'].string() == 'CEL': | ||
| 800 | - phagen['ra'] = cls['xref'].real() | ||
| 801 | - phagen['dec'] = cls['yref'].real() | ||
| 802 | - elif cls['coordsys'].string() == 'GAL': | ||
| 803 | - phagen['glon'] = cls['xref'].real() | ||
| 804 | - phagen['glat'] = cls['yref'].real() | 803 | + # User has specified custom centre for the source region |
| 804 | + if ra != None and dec != None: | ||
| 805 | + phagen['coordsys'] = 'CEL' | ||
| 806 | + phagen['ra'] = ra | ||
| 807 | + phagen['dec'] = dec | ||
| 808 | + # Otherwise use default in class | ||
| 809 | + else: | ||
| 810 | + phagen['coordsys'] = cls['coordsys'].string() | ||
| 811 | + if cls['coordsys'].string() == 'CEL': | ||
| 812 | + phagen['ra'] = cls['xref'].real() | ||
| 813 | + phagen['dec'] = cls['yref'].real() | ||
| 814 | + elif cls['coordsys'].string() == 'GAL': | ||
| 815 | + phagen['glon'] = cls['xref'].real() | ||
| 816 | + phagen['glat'] = cls['yref'].real() | ||
| 805 | if cls['srcshape'].string() == 'CIRCLE': | 817 | if cls['srcshape'].string() == 'CIRCLE': |
| 806 | phagen['rad'] = cls['rad'].real() | 818 | phagen['rad'] = cls['rad'].real() |
| 807 | phagen['bkgmethod'] = cls['bkgmethod'].string() | 819 | phagen['bkgmethod'] = cls['bkgmethod'].string() |