Commit 6049bf07b4d985a8e80d55dcfcdfab710e1c4ba5
1 parent
dd903c61
Add cssrcdetect script (#1617)
Showing
13 changed files
with
1127 additions
and
191 deletions
ChangeLog
| 1 | -2016-11-25 | 1 | +2016-12-01 |
| 2 | 2 | ||
| 3 | * Version 1.2.0 released | 3 | * Version 1.2.0 released |
| 4 | ======================== | 4 | ======================== |
| 5 | 5 | ||
| 6 | + Add cssrcdetect script (#1617) | ||
| 6 | Add background subtraction to ctskymap tool (#1882) | 7 | Add background subtraction to ctskymap tool (#1882) |
| 7 | Add csobsselect script (#1881) | 8 | Add csobsselect script (#1881) |
| 8 | Add csmodelselect script (#1879) | 9 | Add csmodelselect script (#1879) |
NEWS
| 1 | New Features and Important Changes in ctools 1.2.0 | 1 | New Features and Important Changes in ctools 1.2.0 |
| 2 | 2 | ||
| 3 | -25 November 2016 | 3 | +1 December 2016 |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | Introduction | 6 | Introduction |
| @@ -206,30 +206,33 @@ The class now derives from ctlikelihood (#1828). | @@ -206,30 +206,33 @@ The class now derives from ctlikelihood (#1828). | ||
| 206 | 206 | ||
| 207 | cscripts | 207 | cscripts |
| 208 | -------- | 208 | -------- |
| 209 | -Added the support modules "modutils" and "ioutils" to the cscripts module and | ||
| 210 | -removed the obsolete functions fit(), cterror(), cntmap() and modmap() from the | 209 | +Add the support modules "modutils" and "ioutils" to the cscripts module and |
| 210 | +remove the obsolete functions fit(), cterror(), cntmap() and modmap() from the | ||
| 211 | "obsutils" module (#1830). | 211 | "obsutils" module (#1830). |
| 212 | 212 | ||
| 213 | The csroot2caldb script now takes the zenith and azimuth angles as parameters | 213 | The csroot2caldb script now takes the zenith and azimuth angles as parameters |
| 214 | and writes them into the calibration database (#1835). | 214 | and writes them into the calibration database (#1835). |
| 215 | 215 | ||
| 216 | -Added a csviscube script that computes the visibility cube providing the number | 216 | +Add csviscube script that computes the visibility cube providing the number |
| 217 | of hours that a given position on the sky is observable under a given zenith | 217 | of hours that a given position on the sky is observable under a given zenith |
| 218 | angle by an IACT array during a given time period. The tool implements so far | 218 | angle by an IACT array during a given time period. The tool implements so far |
| 219 | a Sun constraint, but no Moon constraint (#1835). | 219 | a Sun constraint, but no Moon constraint (#1835). |
| 220 | 220 | ||
| 221 | -A publish() method has been added to cslightcrv and csspec that publishes the | ||
| 222 | -results on a VO hub (#1871). | 221 | +Add publish() method to cslightcrv and csspec that publishes the results on a |
| 222 | +VO hub (#1871). | ||
| 223 | 223 | ||
| 224 | Fix energy boundary handling in cssens that has been corrupted during code | 224 | Fix energy boundary handling in cssens that has been corrupted during code |
| 225 | refactoring (#1873). | 225 | refactoring (#1873). |
| 226 | 226 | ||
| 227 | -Added csmodelselect script that allows selecting models from a model definition | 227 | +Add csmodelselect script that allows selecting models from a model definition |
| 228 | XML file (#1879). | 228 | XML file (#1879). |
| 229 | 229 | ||
| 230 | -Added csobsselect script that allows selecting observations from an observation | 230 | +Add csobsselect script that allows selecting observations from an observation |
| 231 | definition XML file (#1881). | 231 | definition XML file (#1881). |
| 232 | 232 | ||
| 233 | +Add cssrcdetect script to detect sources in a sky map (#1617). | ||
| 234 | + | ||
| 235 | + | ||
| 233 | 236 | ||
| 234 | Examples | 237 | Examples |
| 235 | -------- | 238 | -------- |
cscripts/Makefile.am
| @@ -36,6 +36,7 @@ pythonscripts = $(builddir)/__init__.py \ | @@ -36,6 +36,7 @@ pythonscripts = $(builddir)/__init__.py \ | ||
| 36 | $(srcdir)/csroot2caldb.py \ | 36 | $(srcdir)/csroot2caldb.py \ |
| 37 | $(srcdir)/cssens.py \ | 37 | $(srcdir)/cssens.py \ |
| 38 | $(srcdir)/csspec.py \ | 38 | $(srcdir)/csspec.py \ |
| 39 | + $(srcdir)/cssrcdetect.py \ | ||
| 39 | $(srcdir)/cstsdist.py \ | 40 | $(srcdir)/cstsdist.py \ |
| 40 | $(srcdir)/cstsmapmerge.py \ | 41 | $(srcdir)/cstsmapmerge.py \ |
| 41 | $(srcdir)/cstsmapsplit.py \ | 42 | $(srcdir)/cstsmapsplit.py \ |
| @@ -65,6 +66,7 @@ parfiles = $(srcdir)/cscaldb.par \ | @@ -65,6 +66,7 @@ parfiles = $(srcdir)/cscaldb.par \ | ||
| 65 | $(srcdir)/csroot2caldb.par \ | 66 | $(srcdir)/csroot2caldb.par \ |
| 66 | $(srcdir)/cssens.par \ | 67 | $(srcdir)/cssens.par \ |
| 67 | $(srcdir)/csspec.par \ | 68 | $(srcdir)/csspec.par \ |
| 69 | + $(srcdir)/cssrcdetect.par \ | ||
| 68 | $(srcdir)/cstsdist.par \ | 70 | $(srcdir)/cstsdist.par \ |
| 69 | $(srcdir)/cstsmapmerge.par \ | 71 | $(srcdir)/cstsmapmerge.par \ |
| 70 | $(srcdir)/cstsmapsplit.par \ | 72 | $(srcdir)/cstsmapsplit.par \ |
| @@ -91,6 +93,7 @@ linkscripts = cscaldb \ | @@ -91,6 +93,7 @@ linkscripts = cscaldb \ | ||
| 91 | csroot2caldb \ | 93 | csroot2caldb \ |
| 92 | cssens \ | 94 | cssens \ |
| 93 | csspec \ | 95 | csspec \ |
| 96 | + cssrcdetect \ | ||
| 94 | cstsdist \ | 97 | cstsdist \ |
| 95 | cstsmapmerge \ | 98 | cstsmapmerge \ |
| 96 | cstsmapsplit \ | 99 | cstsmapsplit \ |
| @@ -119,6 +122,7 @@ testscripts = $(srcdir)/tests/__init__.py \ | @@ -119,6 +122,7 @@ testscripts = $(srcdir)/tests/__init__.py \ | ||
| 119 | $(top_srcdir)/test/test_csroot2caldb.py \ | 122 | $(top_srcdir)/test/test_csroot2caldb.py \ |
| 120 | $(top_srcdir)/test/test_cssens.py \ | 123 | $(top_srcdir)/test/test_cssens.py \ |
| 121 | $(top_srcdir)/test/test_csspec.py \ | 124 | $(top_srcdir)/test/test_csspec.py \ |
| 125 | + $(top_srcdir)/test/test_cssrcdetect.py \ | ||
| 122 | $(top_srcdir)/test/test_cstsdist.py \ | 126 | $(top_srcdir)/test/test_cstsdist.py \ |
| 123 | $(top_srcdir)/test/test_cstsmapmerge.py \ | 127 | $(top_srcdir)/test/test_cstsmapmerge.py \ |
| 124 | $(top_srcdir)/test/test_cstsmapsplit.py \ | 128 | $(top_srcdir)/test/test_cstsmapsplit.py \ |
| @@ -160,6 +164,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \ | @@ -160,6 +164,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \ | ||
| 160 | $(top_srcdir)/test/data/pull.dat \ | 164 | $(top_srcdir)/test/data/pull.dat \ |
| 161 | $(top_srcdir)/test/data/sensitivity.dat \ | 165 | $(top_srcdir)/test/data/sensitivity.dat \ |
| 162 | $(top_srcdir)/test/data/sensitivity_integral.dat \ | 166 | $(top_srcdir)/test/data/sensitivity_integral.dat \ |
| 167 | + $(top_srcdir)/test/data/skymap.fits \ | ||
| 163 | $(top_srcdir)/test/data/spectrum.fits \ | 168 | $(top_srcdir)/test/data/spectrum.fits \ |
| 164 | $(top_srcdir)/test/data/ts.dat \ | 169 | $(top_srcdir)/test/data/ts.dat \ |
| 165 | $(top_srcdir)/test/data/models_spatial.xml \ | 170 | $(top_srcdir)/test/data/models_spatial.xml \ |
cscripts/__init__.py.in
| @@ -39,6 +39,7 @@ __all__ = [ | @@ -39,6 +39,7 @@ __all__ = [ | ||
| 39 | "csroot2caldb", | 39 | "csroot2caldb", |
| 40 | "cssens", | 40 | "cssens", |
| 41 | "csspec", | 41 | "csspec", |
| 42 | + "cssrcdetect", | ||
| 42 | "cstsdist", | 43 | "cstsdist", |
| 43 | "cstsmapmerge", | 44 | "cstsmapmerge", |
| 44 | "cstsmapsplit", | 45 | "cstsmapsplit", |
| @@ -71,6 +72,7 @@ from cscripts.csresmap import csresmap | @@ -71,6 +72,7 @@ from cscripts.csresmap import csresmap | ||
| 71 | from cscripts.csroot2caldb import csroot2caldb | 72 | from cscripts.csroot2caldb import csroot2caldb |
| 72 | from cscripts.cssens import cssens | 73 | from cscripts.cssens import cssens |
| 73 | from cscripts.csspec import csspec | 74 | from cscripts.csspec import csspec |
| 75 | +from cscripts.cssrcdetect import cssrcdetect | ||
| 74 | from cscripts.cstsdist import cstsdist | 76 | from cscripts.cstsdist import cstsdist |
| 75 | from cscripts.cstsmapmerge import cstsmapmerge | 77 | from cscripts.cstsmapmerge import cstsmapmerge |
| 76 | from cscripts.cstsmapsplit import cstsmapsplit | 78 | from cscripts.cstsmapsplit import cstsmapsplit |
cscripts/csmodelinfo.py
| @@ -21,6 +21,7 @@ | @@ -21,6 +21,7 @@ | ||
| 21 | import sys | 21 | import sys |
| 22 | import gammalib | 22 | import gammalib |
| 23 | import ctools | 23 | import ctools |
| 24 | +from cscripts import modutils | ||
| 24 | 25 | ||
| 25 | 26 | ||
| 26 | # ================= # | 27 | # ================= # |
| @@ -86,155 +87,6 @@ class csmodelinfo(ctools.cscript): | @@ -86,155 +87,6 @@ class csmodelinfo(ctools.cscript): | ||
| 86 | # Return | 87 | # Return |
| 87 | return | 88 | return |
| 88 | 89 | ||
| 89 | - def _ds9attributes(self, model): | ||
| 90 | - """ | ||
| 91 | - Returns attributes for DS9 string | ||
| 92 | - Parameters | ||
| 93 | - ---------- | ||
| 94 | - model : `~gammalib.GModel` | ||
| 95 | - Model | ||
| 96 | - | ||
| 97 | - Returns | ||
| 98 | - ------- | ||
| 99 | - attributes : str | ||
| 100 | - DS9 attribute string | ||
| 101 | - """ | ||
| 102 | - # Determine region color. A model where all parameters are fixed will | ||
| 103 | - # get the color defined by "fixed_color", a model with at least one | ||
| 104 | - # free parameter will get the color defined by "free_color". | ||
| 105 | - color = self['fixed_color'].string() | ||
| 106 | - for par in model: | ||
| 107 | - if par.is_free(): | ||
| 108 | - color = self['free_color'].string() | ||
| 109 | - break | ||
| 110 | - | ||
| 111 | - # Set DS9 attributes | ||
| 112 | - attributes = (' color=%s width=%d font=%s %d %s %s' % | ||
| 113 | - (color, self['width'].integer(), | ||
| 114 | - self['fontfamily'].string(), self['fontsize'].integer(), | ||
| 115 | - self['fontweight'].string(), self['fontslant'].string())) | ||
| 116 | - | ||
| 117 | - # Return attributes | ||
| 118 | - return attributes | ||
| 119 | - | ||
| 120 | - def _model2ds9string(self, model): | ||
| 121 | - """ | ||
| 122 | - Converts model into a DS9 region string | ||
| 123 | - | ||
| 124 | - Parameters | ||
| 125 | - ---------- | ||
| 126 | - model : `~gammalib.GModel` | ||
| 127 | - Model | ||
| 128 | - | ||
| 129 | - Returns | ||
| 130 | - ------- | ||
| 131 | - ds9string : str | ||
| 132 | - DS9 region string | ||
| 133 | - """ | ||
| 134 | - # Initialise DS9 region string | ||
| 135 | - ds9string = '' | ||
| 136 | - | ||
| 137 | - # Retrieve model sky direction. The model is skipped in case that | ||
| 138 | - # it does not provide a sky direction. | ||
| 139 | - is_valid = True | ||
| 140 | - try: | ||
| 141 | - modelpos = model.spatial().dir() | ||
| 142 | - except AttributeError: | ||
| 143 | - msg = ('Skip model "%s" since it has no sky direction.\n' % | ||
| 144 | - model.name()) | ||
| 145 | - self._log(msg) | ||
| 146 | - is_valid = False | ||
| 147 | - | ||
| 148 | - # Continue only if sky direction was found | ||
| 149 | - if is_valid: | ||
| 150 | - | ||
| 151 | - # Retrieve model type and name | ||
| 152 | - modeltype = model.type() | ||
| 153 | - modelname = model.name() | ||
| 154 | - | ||
| 155 | - # Handle point source | ||
| 156 | - if modeltype == 'PointSource': | ||
| 157 | - | ||
| 158 | - # Append point with Right Ascension and Declination to the DS9 | ||
| 159 | - # string. The type of the point is specified by the "pnt_type" | ||
| 160 | - # parameter, the size of the point by the "pnt_mark_size" | ||
| 161 | - # parameter | ||
| 162 | - ds9string += ('point(%.6f,%.6f) # point=%s %d' % | ||
| 163 | - (modelpos.ra_deg(), modelpos.dec_deg(), | ||
| 164 | - self['pnt_type'].string(), | ||
| 165 | - self['pnt_mark_size'].integer())) | ||
| 166 | - | ||
| 167 | - # Handle extended sources | ||
| 168 | - elif modeltype == "ExtendedSource": | ||
| 169 | - | ||
| 170 | - # Retrieve spatial model | ||
| 171 | - spatial = model.spatial() | ||
| 172 | - classname = spatial.classname() | ||
| 173 | - | ||
| 174 | - # Handle radial sources | ||
| 175 | - if 'Radial' in classname: | ||
| 176 | - | ||
| 177 | - # Retrieve short name of model class (e.g. "Gauss" | ||
| 178 | - # or "Disk) | ||
| 179 | - shorttype = classname.split('Radial')[-1] | ||
| 180 | - | ||
| 181 | - # Handle Disk and Shell model | ||
| 182 | - if (classname == 'GModelSpatialRadialDisk' or | ||
| 183 | - classname == 'GModelSpatialRadialShell'): | ||
| 184 | - size = spatial.radius() | ||
| 185 | - | ||
| 186 | - # Handle Gauss Model | ||
| 187 | - elif classname == 'GModelSpatialRadialGauss': | ||
| 188 | - size = spatial.sigma() | ||
| 189 | - | ||
| 190 | - # Skip if source is unknown | ||
| 191 | - else: | ||
| 192 | - msg = ('Skip model "%s" since the radial model "%s" ' | ||
| 193 | - 'is unknown.\n' % (model.name(), classname)) | ||
| 194 | - self._log(msg) | ||
| 195 | - is_valid = False | ||
| 196 | - | ||
| 197 | - # Append circle to DS9 string | ||
| 198 | - if is_valid: | ||
| 199 | - ds9string += ('circle(%.6f,%.6f,%.6f) #' % | ||
| 200 | - (modelpos.ra_deg(), modelpos.dec_deg(), | ||
| 201 | - size*3600.0)) | ||
| 202 | - | ||
| 203 | - # Handle elliptical sources | ||
| 204 | - elif 'Elliptical' in classname: | ||
| 205 | - | ||
| 206 | - # Retrieve short name and source size | ||
| 207 | - shorttype = classname.split('Elliptical')[-1] | ||
| 208 | - size1 = spatial.semimajor() | ||
| 209 | - size2 = spatial.semiminor() | ||
| 210 | - angle = spatial.posangle() | ||
| 211 | - | ||
| 212 | - # Append ellipse to DS9 string | ||
| 213 | - ds9string += ('ellipse(%.6f,%.6f,%.6f,%.6f,%.6f) #' % | ||
| 214 | - (modelpos.ra_deg(), modelpos.dec_deg(), | ||
| 215 | - size1*3600.0, size2*3600.0, angle+90.0)) | ||
| 216 | - | ||
| 217 | - # Skip if source is neither radial nor elliptical | ||
| 218 | - else: | ||
| 219 | - msg = ('Skip model "%s" since the model "%s" is neither ' | ||
| 220 | - 'a point source, a radial source, nor an elliptical ' | ||
| 221 | - 'source.\n' % (model.name(), classname)) | ||
| 222 | - self._log(msg) | ||
| 223 | - is_valid = False | ||
| 224 | - | ||
| 225 | - # Add short model type to modelname | ||
| 226 | - if self['show_ext_type'].boolean(): | ||
| 227 | - modelname +=' ('+shorttype+')' | ||
| 228 | - | ||
| 229 | - # Add DS9 attributes | ||
| 230 | - if is_valid: | ||
| 231 | - ds9string += self._ds9attributes(model) | ||
| 232 | - if self['show_labels'].boolean(): | ||
| 233 | - ds9string += ' text={'+modelname+'}' | ||
| 234 | - | ||
| 235 | - # Return string | ||
| 236 | - return ds9string | ||
| 237 | - | ||
| 238 | 90 | ||
| 239 | # Public methods | 91 | # Public methods |
| 240 | def run(self): | 92 | def run(self): |
| @@ -407,36 +259,34 @@ class csmodelinfo(ctools.cscript): | @@ -407,36 +259,34 @@ class csmodelinfo(ctools.cscript): | ||
| 407 | 259 | ||
| 408 | # Log filename | 260 | # Log filename |
| 409 | self._log_value(gammalib.NORMAL, 'DS9 filename', ds9file.url()) | 261 | self._log_value(gammalib.NORMAL, 'DS9 filename', ds9file.url()) |
| 410 | - | ||
| 411 | - # Open file | ||
| 412 | - f = open(ds9file.url(), 'w') | ||
| 413 | - | ||
| 414 | - # Write coordinate system | ||
| 415 | - f.write('fk5\n') | ||
| 416 | - | ||
| 417 | - # Loop over models | ||
| 418 | - for model in self._models: | ||
| 419 | - | ||
| 420 | - # Continue only if point source or extended source model | ||
| 421 | - if (model.type() == 'PointSource' or | ||
| 422 | - model.type() == 'ExtendedSource'): | ||
| 423 | - line = self._model2ds9string(model) | ||
| 424 | - if len(line): | ||
| 425 | - f.write(line+'\n') | ||
| 426 | - | ||
| 427 | - # Logging for diffuse components | ||
| 428 | - elif model.type() == 'DiffuseSource': | ||
| 429 | - self._log('Skipping diffuse model "'+model.name()+'".\n') | ||
| 430 | - | ||
| 431 | - # Logging for background components | ||
| 432 | - else: | ||
| 433 | - if self._logExplicit(): | ||
| 434 | - self._log('Skipping background model "'+model.name()+ | ||
| 435 | - '".\n') | ||
| 436 | - | ||
| 437 | - # Close file | ||
| 438 | - f.close() | ||
| 439 | - | 262 | + |
| 263 | + # Get ds9 parameters | ||
| 264 | + pnt_type = self['pnt_type'].string() | ||
| 265 | + pnt_mark_size = self['pnt_mark_size'].integer() | ||
| 266 | + free_color = self['free_color'].string() | ||
| 267 | + fixed_color = self['fixed_color'].string() | ||
| 268 | + width = self['width'].integer() | ||
| 269 | + fontfamily = self['fontfamily'].string() | ||
| 270 | + fontsize = self['fontsize'].integer() | ||
| 271 | + fontweight = self['fontweight'].string() | ||
| 272 | + fontslant = self['fontslant'].string() | ||
| 273 | + | ||
| 274 | + # Save DS9 region file | ||
| 275 | + errors = modutils.models2ds9file(self._models, ds9file.url(), | ||
| 276 | + pnt_type=pnt_type, | ||
| 277 | + pnt_mark_size=pnt_mark_size, | ||
| 278 | + free_color=free_color, | ||
| 279 | + fixed_color=fixed_color, | ||
| 280 | + width=width, | ||
| 281 | + fontfamily=fontfamily, | ||
| 282 | + fontsize=fontsize, | ||
| 283 | + fontweight=fontweight, | ||
| 284 | + fontslant=fontslant) | ||
| 285 | + | ||
| 286 | + # Log errors | ||
| 287 | + if len(errors): | ||
| 288 | + self._log(errors) | ||
| 289 | + | ||
| 440 | # Return | 290 | # Return |
| 441 | return | 291 | return |
| 442 | 292 |
cscripts/cssrcdetect.par
0 → 100644
| 1 | +############################################################################# | ||
| 2 | +# # | ||
| 3 | +# CTA Science Analysis Tools # | ||
| 4 | +# # | ||
| 5 | +# ------------------------------------------------------------------------- # | ||
| 6 | +# # | ||
| 7 | +# File: cssrcdetect.par # | ||
| 8 | +# # | ||
| 9 | +# Author: Juergen Knoedlseder # | ||
| 10 | +# jknodlseder@irap.omp.eu # | ||
| 11 | +# IRAP, Toulouse, France # | ||
| 12 | +# # | ||
| 13 | +# Purpose: Parameter file for the cssrcdetect script # | ||
| 14 | +# # | ||
| 15 | +############################################################################# | ||
| 16 | +# | ||
| 17 | +# Input and output parameters | ||
| 18 | +#============================ | ||
| 19 | +inmap, f, a, skymap.fits,,, "Input sky map file" | ||
| 20 | +outmodel, f, a, models.xml,,, "Output model definition XML file" | ||
| 21 | +outds9file, f, a, ds9.reg,,, "Output DS9 region file" | ||
| 22 | + | ||
| 23 | +# | ||
| 24 | +# Script parameters | ||
| 25 | +#================== | ||
| 26 | +srcmodel, s, a, POINT,POINT,, "Source model type" | ||
| 27 | +bkgmodel, s, a, NONE,NONE|IRF|AEFF|CUBE,, "Background model type" | ||
| 28 | +threshold, r, a, 5.0,,, "Detection threshold (Gaussian sigma)" | ||
| 29 | +maxsrcs, i, h, 20,1,1000, "Maximum number of sources" | ||
| 30 | +exclrad, r, h, 0.2,,, "Exclusion radius (deg)" | ||
| 31 | +fit_pos, b, h, yes,,, "Fit source positions?" | ||
| 32 | +fit_shape, b, h, yes,,, "Fit source shapes?" | ||
| 33 | + | ||
| 34 | +# | ||
| 35 | +# Standard parameters | ||
| 36 | +#==================== | ||
| 37 | +chatter, i, h, 2,0,4, "Chattiness of output" | ||
| 38 | +clobber, b, h, yes,,, "Overwrite existing output files with new output files?" | ||
| 39 | +debug, b, h, no,,, "Debugging mode activated" | ||
| 40 | +mode, s, h, ql,,, "Mode of automatic parameters" | ||
| 41 | +logfile, f, h, cssrcdetect.log,,, "Log filename" |
cscripts/cssrcdetect.py
0 → 100755
| 1 | +#!/usr/bin/env python | ||
| 2 | +# ========================================================================== | ||
| 3 | +# Detects sources in a sky map | ||
| 4 | +# | ||
| 5 | +# Copyright (C) 2016 Juergen Knoedlseder | ||
| 6 | +# | ||
| 7 | +# This program is free software: you can redistribute it and/or modify | ||
| 8 | +# it under the terms of the GNU General Public License as published by | ||
| 9 | +# the Free Software Foundation, either version 3 of the License, or | ||
| 10 | +# (at your option) any later version. | ||
| 11 | +# | ||
| 12 | +# This program is distributed in the hope that it will be useful, | ||
| 13 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | +# GNU General Public License for more details. | ||
| 16 | +# | ||
| 17 | +# You should have received a copy of the GNU General Public License | ||
| 18 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | +# | ||
| 20 | +# ========================================================================== | ||
| 21 | +import sys | ||
| 22 | +import math | ||
| 23 | +import gammalib | ||
| 24 | +import ctools | ||
| 25 | +from cscripts import modutils | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +# ================= # | ||
| 29 | +# cssrcdetect class # | ||
| 30 | +# ================= # | ||
| 31 | +class cssrcdetect(ctools.cscript): | ||
| 32 | + """ | ||
| 33 | + Detects sources in a sky map | ||
| 34 | + """ | ||
| 35 | + | ||
| 36 | + # Constructor | ||
| 37 | + def __init__(self, *argv): | ||
| 38 | + """ | ||
| 39 | + Constructor | ||
| 40 | + """ | ||
| 41 | + # Set name | ||
| 42 | + self._name = 'cssrcdetect' | ||
| 43 | + self._version = '1.2.0' | ||
| 44 | + | ||
| 45 | + # Set protected members | ||
| 46 | + self._models = gammalib.GModels() | ||
| 47 | + | ||
| 48 | + # Initialise application by calling the appropriate class constructor | ||
| 49 | + self._init_cscript(argv) | ||
| 50 | + | ||
| 51 | + # Return | ||
| 52 | + return | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + # Private methods | ||
| 56 | + def _get_parameters(self): | ||
| 57 | + """ | ||
| 58 | + Get parameters from parfile | ||
| 59 | + """ | ||
| 60 | + # Query input parameters | ||
| 61 | + self['inmap'].filename() | ||
| 62 | + | ||
| 63 | + # Query further parameters | ||
| 64 | + self['srcmodel'].string() | ||
| 65 | + self['bkgmodel'].string() | ||
| 66 | + self['threshold'].real() | ||
| 67 | + self['maxsrcs'].integer() | ||
| 68 | + self['exclrad'].real() | ||
| 69 | + self['fit_pos'].boolean() | ||
| 70 | + self['fit_shape'].boolean() | ||
| 71 | + | ||
| 72 | + # Query ahead output model filename | ||
| 73 | + if self._read_ahead(): | ||
| 74 | + self['outmodel'].filename() | ||
| 75 | + self['outds9file'].filename() | ||
| 76 | + | ||
| 77 | + # Write input parameters into logger | ||
| 78 | + self._log_parameters(gammalib.TERSE) | ||
| 79 | + | ||
| 80 | + # Return | ||
| 81 | + return | ||
| 82 | + | ||
| 83 | + def _detect_sources(self, counts): | ||
| 84 | + """ | ||
| 85 | + Detect sources in counts map | ||
| 86 | + | ||
| 87 | + Parameters | ||
| 88 | + ---------- | ||
| 89 | + counts : `~gammalib.GSkyMap()` | ||
| 90 | + Counts map | ||
| 91 | + """ | ||
| 92 | + # Source detection iterations | ||
| 93 | + for i in range(self['maxsrcs'].integer()): | ||
| 94 | + | ||
| 95 | + # Write header | ||
| 96 | + self._log_header3(gammalib.NORMAL, 'Iteration '+str(i+1)) | ||
| 97 | + | ||
| 98 | + # Get map moments | ||
| 99 | + mean, std = self._map_moments(counts) | ||
| 100 | + | ||
| 101 | + # Log map moments | ||
| 102 | + self._log_value(gammalib.NORMAL, 'Map mean', mean) | ||
| 103 | + self._log_value(gammalib.NORMAL, 'Map standard deviation', std) | ||
| 104 | + | ||
| 105 | + # Compute threshold | ||
| 106 | + threshold = mean + self['threshold'].real() * std | ||
| 107 | + | ||
| 108 | + # Log map threshold | ||
| 109 | + self._log_value(gammalib.NORMAL, 'Map threshold', threshold) | ||
| 110 | + | ||
| 111 | + # Get maximum value and corresponding sky direction | ||
| 112 | + value, dir = self._find_maximum(counts, threshold=threshold) | ||
| 113 | + | ||
| 114 | + # If maximum found then log maximum and add model | ||
| 115 | + if dir is not None: | ||
| 116 | + | ||
| 117 | + # Set source name | ||
| 118 | + name = 'Src%3.3d' % (i+1) | ||
| 119 | + | ||
| 120 | + # Log maximum | ||
| 121 | + self._log_value(gammalib.NORMAL, 'Map maximum', str(value)) | ||
| 122 | + self._log_value(gammalib.NORMAL, name+' position', str(dir)) | ||
| 123 | + | ||
| 124 | + # Add model | ||
| 125 | + self._add_model(dir, name) | ||
| 126 | + | ||
| 127 | + # Remove maximum from map | ||
| 128 | + counts = self._remove_maximum(counts, dir, mean, | ||
| 129 | + radius=self['exclrad'].real()) | ||
| 130 | + | ||
| 131 | + # ... otherwise log that no maximum was found and break iterations | ||
| 132 | + else: | ||
| 133 | + | ||
| 134 | + # Log than no maximum was found | ||
| 135 | + self._log_value(gammalib.NORMAL, 'Map maximum', | ||
| 136 | + 'None above threshold') | ||
| 137 | + | ||
| 138 | + # Break | ||
| 139 | + break | ||
| 140 | + | ||
| 141 | + # Return | ||
| 142 | + return | ||
| 143 | + | ||
| 144 | + def _find_maximum(self, map, threshold=0.0): | ||
| 145 | + """ | ||
| 146 | + Find maximum in a sky map | ||
| 147 | + | ||
| 148 | + Parameters | ||
| 149 | + ---------- | ||
| 150 | + map : `~gammalib.GSkyMap()` | ||
| 151 | + Sky map | ||
| 152 | + threshold : float, optional | ||
| 153 | + Threshold for maximum value | ||
| 154 | + | ||
| 155 | + Returns | ||
| 156 | + ------- | ||
| 157 | + value, dir : tuple of float and `~gammalib.GSkyDir()` | ||
| 158 | + Maximum sky map value and corresponding sky direction | ||
| 159 | + """ | ||
| 160 | + # Initialise maximum pixel value and sky direction | ||
| 161 | + value = threshold | ||
| 162 | + dir = None | ||
| 163 | + | ||
| 164 | + # Loop over all pixels and find maximum | ||
| 165 | + for i in range(map.npix()): | ||
| 166 | + if map[i] > value: | ||
| 167 | + value = map[i] | ||
| 168 | + dir = map.inx2dir(i) | ||
| 169 | + | ||
| 170 | + # Return sky direction of maximum | ||
| 171 | + return value, dir | ||
| 172 | + | ||
| 173 | + def _remove_maximum(self, map, dir, value=0.0, radius=0.1): | ||
| 174 | + """ | ||
| 175 | + Remove maximum from sky map by replacing pixels with a given value | ||
| 176 | + | ||
| 177 | + Parameters | ||
| 178 | + ---------- | ||
| 179 | + map : `~gammalib.GSkyMap()` | ||
| 180 | + Sky map | ||
| 181 | + dir : `~gammalib.GSkyDir()` | ||
| 182 | + Sky direction of maximum | ||
| 183 | + value : float, optional | ||
| 184 | + Replacement value | ||
| 185 | + radius : float, optional | ||
| 186 | + Radius within which pixel values are replaced | ||
| 187 | + | ||
| 188 | + Returns | ||
| 189 | + ------- | ||
| 190 | + map : `~gammalib.GSkyMap()` | ||
| 191 | + Sky map with maximum removed | ||
| 192 | + """ | ||
| 193 | + # Copy skymap | ||
| 194 | + map_copy = map.copy() | ||
| 195 | + | ||
| 196 | + # Loop over all pixels and find maximum | ||
| 197 | + for i in range(map_copy.npix()): | ||
| 198 | + mapdir = map_copy.inx2dir(i) | ||
| 199 | + if mapdir.dist_deg(dir) < radius: | ||
| 200 | + map_copy[i] = value | ||
| 201 | + | ||
| 202 | + # Return copy of map | ||
| 203 | + return map_copy | ||
| 204 | + | ||
| 205 | + def _map_moments(self, map): | ||
| 206 | + """ | ||
| 207 | + Determine moments of sky map pixels | ||
| 208 | + | ||
| 209 | + Parameters | ||
| 210 | + ---------- | ||
| 211 | + map : `~gammalib.GSkyMap()` | ||
| 212 | + Sky map | ||
| 213 | + | ||
| 214 | + Returns | ||
| 215 | + ------- | ||
| 216 | + mean, std : tuple of float | ||
| 217 | + Mean and standard deviation of pixel values | ||
| 218 | + """ | ||
| 219 | + # Initialise mean and standard deviation | ||
| 220 | + mean = 0.0 | ||
| 221 | + std = 0.0 | ||
| 222 | + | ||
| 223 | + # Loop over all pixels | ||
| 224 | + for i in range(map.npix()): | ||
| 225 | + mean += map[i] | ||
| 226 | + std += map[i]*map[i] | ||
| 227 | + | ||
| 228 | + # Compute mean and standard deviation | ||
| 229 | + mean /= float(map.npix()) | ||
| 230 | + std /= float(map.npix()) | ||
| 231 | + std -= mean | ||
| 232 | + std = math.sqrt(std) | ||
| 233 | + | ||
| 234 | + # Return mean and standard deviation | ||
| 235 | + return mean, std | ||
| 236 | + | ||
| 237 | + def _add_model(self, dir, name): | ||
| 238 | + """ | ||
| 239 | + Add model to model container | ||
| 240 | + | ||
| 241 | + Parameters | ||
| 242 | + ---------- | ||
| 243 | + dir : `~gammalib.GSkyDir()` | ||
| 244 | + Sky direction of model | ||
| 245 | + name : str | ||
| 246 | + Model name | ||
| 247 | + """ | ||
| 248 | + # Set point source model | ||
| 249 | + model = self._set_ptsrc(dir) | ||
| 250 | + | ||
| 251 | + # Set model name | ||
| 252 | + model.name(name) | ||
| 253 | + | ||
| 254 | + # Append model to container | ||
| 255 | + self._models.append(model) | ||
| 256 | + | ||
| 257 | + # Return | ||
| 258 | + return | ||
| 259 | + | ||
| 260 | + def _set_bkg(self, type): | ||
| 261 | + """ | ||
| 262 | + Set background model | ||
| 263 | + | ||
| 264 | + Parameters | ||
| 265 | + ---------- | ||
| 266 | + type : str | ||
| 267 | + Model type ('IRF', 'AEFF' or 'CUBE') | ||
| 268 | + | ||
| 269 | + Returns | ||
| 270 | + ------- | ||
| 271 | + model : `~gammalib.GModelData()` | ||
| 272 | + Background model | ||
| 273 | + """ | ||
| 274 | + # Set spectral component | ||
| 275 | + spectral = gammalib.GModelSpectralPlaw(1.0, 0.0, | ||
| 276 | + gammalib.GEnergy(1.0, 'TeV')) | ||
| 277 | + | ||
| 278 | + # Set background model | ||
| 279 | + if type == 'IRF': | ||
| 280 | + model = gammalib.GCTAModelIrfBackground(spectral) | ||
| 281 | + elif type == 'AEFF': | ||
| 282 | + model = gammalib.GCTAModelAeffBackground(spectral) | ||
| 283 | + elif type == 'CUBE': | ||
| 284 | + model = gammalib.GCTAModelCubeBackground(spectral) | ||
| 285 | + else: | ||
| 286 | + model = None | ||
| 287 | + | ||
| 288 | + # Set background name | ||
| 289 | + if model is not None: | ||
| 290 | + model.name('Background') | ||
| 291 | + | ||
| 292 | + # Return model | ||
| 293 | + return model | ||
| 294 | + | ||
| 295 | + def _set_ptsrc(self, dir): | ||
| 296 | + """ | ||
| 297 | + Set point source model | ||
| 298 | + | ||
| 299 | + Parameters | ||
| 300 | + ---------- | ||
| 301 | + dir : `~gammalib.GSkyDir()` | ||
| 302 | + Sky direction of model | ||
| 303 | + | ||
| 304 | + Returns | ||
| 305 | + ------- | ||
| 306 | + model : `~gammalib.GModelSky()` | ||
| 307 | + Point source model | ||
| 308 | + """ | ||
| 309 | + # Set spatial component | ||
| 310 | + spatial = gammalib.GModelSpatialPointSource(dir) | ||
| 311 | + | ||
| 312 | + # Get fit parameters | ||
| 313 | + fit_pos = self['fit_pos'].boolean() | ||
| 314 | + fit_shape = self['fit_shape'].boolean() | ||
| 315 | + | ||
| 316 | + # Loop over all parameters | ||
| 317 | + for par in spatial: | ||
| 318 | + | ||
| 319 | + # Handle position parameters | ||
| 320 | + if par.name() == 'RA' or par.name() == 'DEC': | ||
| 321 | + if fit_pos: | ||
| 322 | + par.free() | ||
| 323 | + else: | ||
| 324 | + par.fix() | ||
| 325 | + | ||
| 326 | + # Set spectral component (powerlaw with 1% of Crab) | ||
| 327 | + spectral = gammalib.GModelSpectralPlaw(5.7e-18, -2.48, | ||
| 328 | + gammalib.GEnergy(0.3, 'TeV')) | ||
| 329 | + | ||
| 330 | + # Set sky model | ||
| 331 | + model = gammalib.GModelSky(spatial, spectral) | ||
| 332 | + | ||
| 333 | + # Return model | ||
| 334 | + return model | ||
| 335 | + | ||
| 336 | + | ||
| 337 | + # Public methods | ||
| 338 | + def run(self): | ||
| 339 | + """ | ||
| 340 | + Run the script | ||
| 341 | + """ | ||
| 342 | + # Clear model container | ||
| 343 | + self._models.clear() | ||
| 344 | + | ||
| 345 | + # Switch screen logging on in debug mode | ||
| 346 | + if self._logDebug(): | ||
| 347 | + self._log.cout(True) | ||
| 348 | + | ||
| 349 | + # Get parameters | ||
| 350 | + self._get_parameters() | ||
| 351 | + | ||
| 352 | + # Write header | ||
| 353 | + self._log_header1(gammalib.NORMAL, 'Source detection') | ||
| 354 | + | ||
| 355 | + # Get skymap filename | ||
| 356 | + inmap = self['inmap'].filename() | ||
| 357 | + | ||
| 358 | + # Open sky map file | ||
| 359 | + fits = gammalib.GFits(inmap) | ||
| 360 | + | ||
| 361 | + # Extract primary extension as counts map | ||
| 362 | + counts = gammalib.GSkyMap(fits.image(0)) | ||
| 363 | + | ||
| 364 | + # Detect sources | ||
| 365 | + self._detect_sources(counts) | ||
| 366 | + | ||
| 367 | + # Close sky map file | ||
| 368 | + fits.close() | ||
| 369 | + | ||
| 370 | + # Write detected sources as models into logger | ||
| 371 | + self._log_models(gammalib.NORMAL, self._models, 'Detected source model') | ||
| 372 | + | ||
| 373 | + # Write header | ||
| 374 | + self._log_header1(gammalib.NORMAL, 'Add background model') | ||
| 375 | + | ||
| 376 | + # Add background model if requested | ||
| 377 | + bkgmodel = self._set_bkg(self['bkgmodel'].string()) | ||
| 378 | + if bkgmodel is not None: | ||
| 379 | + | ||
| 380 | + # Append model | ||
| 381 | + self._models.append(bkgmodel) | ||
| 382 | + | ||
| 383 | + # Log model | ||
| 384 | + self._log_string(gammalib.NORMAL, str(bkgmodel)) | ||
| 385 | + | ||
| 386 | + # ... otherwise notify that no background model is added | ||
| 387 | + else: | ||
| 388 | + self._log_value(gammalib.NORMAL, 'Background model', 'None') | ||
| 389 | + | ||
| 390 | + # Return | ||
| 391 | + return | ||
| 392 | + | ||
| 393 | + def save(self): | ||
| 394 | + """ | ||
| 395 | + Save sources into model definition XML file and ds9 file | ||
| 396 | + """ | ||
| 397 | + # Write header | ||
| 398 | + self._log_header1(gammalib.TERSE, 'Save sources') | ||
| 399 | + | ||
| 400 | + # Get output filenames | ||
| 401 | + outmodel = self['outmodel'].filename() | ||
| 402 | + outds9file = self['outds9file'].filename() | ||
| 403 | + | ||
| 404 | + # If model definition XML file exists and clobber flag is false then | ||
| 405 | + # raise an exception | ||
| 406 | + if outmodel.exists() and not self._clobber: | ||
| 407 | + msg = ('Cannot save "'+outmodel.url()+'": File already exists. ' | ||
| 408 | + 'Use parameter clobber=yes to allow overwriting of files.') | ||
| 409 | + raise RuntimeError(msg) | ||
| 410 | + | ||
| 411 | + # If ds9 file exists and clobber flag is false then raise an exception | ||
| 412 | + elif outds9file.exists() and not self._clobber: | ||
| 413 | + msg = ('Cannot save "'+outds9file.url()+'": File already exists. ' | ||
| 414 | + 'Use parameter clobber=yes to allow overwriting of files.') | ||
| 415 | + raise RuntimeError(msg) | ||
| 416 | + | ||
| 417 | + # Otherwise log filename and save file | ||
| 418 | + else: | ||
| 419 | + # Log model definition XML filename | ||
| 420 | + self._log_value(gammalib.NORMAL, 'Model definition XML file', | ||
| 421 | + outmodel.url()) | ||
| 422 | + | ||
| 423 | + # Save models | ||
| 424 | + self._models.save(outmodel) | ||
| 425 | + | ||
| 426 | + # Log ds9 filename | ||
| 427 | + self._log_value(gammalib.NORMAL, 'DS9 region file', | ||
| 428 | + outds9file.url()) | ||
| 429 | + | ||
| 430 | + # Save models as ds9 region file | ||
| 431 | + modutils.models2ds9file(self._models, outds9file.url()) | ||
| 432 | + | ||
| 433 | + # Return | ||
| 434 | + return | ||
| 435 | + | ||
| 436 | + def execute(self): | ||
| 437 | + """ | ||
| 438 | + Execute the script | ||
| 439 | + """ | ||
| 440 | + # Open logfile | ||
| 441 | + self.logFileOpen() | ||
| 442 | + | ||
| 443 | + # Read ahead output parameters | ||
| 444 | + self._read_ahead(True) | ||
| 445 | + | ||
| 446 | + # Run the script | ||
| 447 | + self.run() | ||
| 448 | + | ||
| 449 | + # Save observation definition file | ||
| 450 | + self.save() | ||
| 451 | + | ||
| 452 | + # Return | ||
| 453 | + return | ||
| 454 | + | ||
| 455 | + | ||
| 456 | +# ======================== # | ||
| 457 | +# Main routine entry point # | ||
| 458 | +# ======================== # | ||
| 459 | +if __name__ == '__main__': | ||
| 460 | + | ||
| 461 | + # Create instance of application | ||
| 462 | + app = cssrcdetect(sys.argv) | ||
| 463 | + | ||
| 464 | + # Execute application | ||
| 465 | + app.execute() |
cscripts/modutils.py
| @@ -99,3 +99,298 @@ def test_source(models, srcname, ra=None, dec=None, fitspat=False, | @@ -99,3 +99,298 @@ def test_source(models, srcname, ra=None, dec=None, fitspat=False, | ||
| 99 | # Return model container | 99 | # Return model container |
| 100 | return outmodels | 100 | return outmodels |
| 101 | 101 | ||
| 102 | + | ||
| 103 | +# ================================= # | ||
| 104 | +# Returns attributes for ds9 string # | ||
| 105 | +# ================================= # | ||
| 106 | +def ds9attributes(model, free_color='green', fixed_color='magenta', | ||
| 107 | + width=2, fontfamily='helvetica', fontsize=12, | ||
| 108 | + fontweight='normal', fontslant='roman'): | ||
| 109 | + """ | ||
| 110 | + Returns attributes for DS9 string | ||
| 111 | + | ||
| 112 | + Parameters | ||
| 113 | + ---------- | ||
| 114 | + model : `~gammalib.GModel` | ||
| 115 | + Model | ||
| 116 | + free_color : str, optional | ||
| 117 | + Color for sources with free parameters (any DS9 color or hex code) | ||
| 118 | + fixed_color : str, optional | ||
| 119 | + Color for source without free parameters (any DS9 color or hex code) | ||
| 120 | + width : int, optional | ||
| 121 | + Line width for regions | ||
| 122 | + fontfamily : str, optional | ||
| 123 | + Font family for source labels (helvetica,times,courier) | ||
| 124 | + fontsize : int, optional | ||
| 125 | + Font size for source labels | ||
| 126 | + fontweight : str, optional | ||
| 127 | + Font weight for source labels (normal,bold) | ||
| 128 | + fontslant : str, optional | ||
| 129 | + Font slant for source labels (roman,italic) | ||
| 130 | + | ||
| 131 | + Returns | ||
| 132 | + ------- | ||
| 133 | + attributes : str | ||
| 134 | + DS9 attribute string | ||
| 135 | + """ | ||
| 136 | + # Determine region color. A model where all parameters are fixed will | ||
| 137 | + # get the color defined by "fixed_color", a model with at least one | ||
| 138 | + # free parameter will get the color defined by "free_color". | ||
| 139 | + color = fixed_color | ||
| 140 | + for par in model: | ||
| 141 | + if par.is_free(): | ||
| 142 | + color = free_color | ||
| 143 | + break | ||
| 144 | + | ||
| 145 | + # Set DS9 attributes | ||
| 146 | + attributes = (' color=%s width=%d font="%s %d %s %s"' % | ||
| 147 | + (color, width, fontfamily, fontsize, fontweight, fontslant)) | ||
| 148 | + | ||
| 149 | + # Return attributes | ||
| 150 | + return attributes | ||
| 151 | + | ||
| 152 | + | ||
| 153 | +# =========================== # | ||
| 154 | +# Convert model to ds9 string # | ||
| 155 | +# =========================== # | ||
| 156 | +def model2ds9string(model, pnt_type='cross', pnt_mark_size=12, | ||
| 157 | + show_labels=True, show_ext_type=True, | ||
| 158 | + free_color='green', fixed_color='magenta', | ||
| 159 | + width=2, fontfamily='helvetica', fontsize=12, | ||
| 160 | + fontweight='normal', fontslant='roman'): | ||
| 161 | + """ | ||
| 162 | + Converts model into a DS9 region string | ||
| 163 | + | ||
| 164 | + Parameters | ||
| 165 | + ---------- | ||
| 166 | + model : `~gammalib.GModel` | ||
| 167 | + Model | ||
| 168 | + pnt_type : str, optional | ||
| 169 | + Marker type for point sources (circle,box,diamond,cross,x,arrow,boxcircle) | ||
| 170 | + pnt_mark_size : integer, optional | ||
| 171 | + Marker size for point sources | ||
| 172 | + show_labels : boolean, optional | ||
| 173 | + Add source labels? | ||
| 174 | + show_ext_type : boolean, optional | ||
| 175 | + Show type of extended model in source name? | ||
| 176 | + free_color : str, optional | ||
| 177 | + Color for sources with free parameters (any DS9 color or hex code) | ||
| 178 | + fixed_color : str, optional | ||
| 179 | + Color for source without free parameters (any DS9 color or hex code) | ||
| 180 | + width : int, optional | ||
| 181 | + Line width for regions | ||
| 182 | + fontfamily : str, optional | ||
| 183 | + Font family for source labels (helvetica,times,courier) | ||
| 184 | + fontsize : int, optional | ||
| 185 | + Font size for source labels | ||
| 186 | + fontweight : str, optional | ||
| 187 | + Font weight for source labels (normal,bold) | ||
| 188 | + fontslant : str, optional | ||
| 189 | + Font slant for source labels (roman,italic) | ||
| 190 | + | ||
| 191 | + Returns | ||
| 192 | + ------- | ||
| 193 | + ds9string : str | ||
| 194 | + DS9 region string | ||
| 195 | + msg : str | ||
| 196 | + Error message | ||
| 197 | + """ | ||
| 198 | + # Initialise DS9 region string and message string | ||
| 199 | + ds9string = '' | ||
| 200 | + msg = '' | ||
| 201 | + | ||
| 202 | + # Retrieve model sky direction. The model is skipped in case that | ||
| 203 | + # it does not provide a sky direction. | ||
| 204 | + is_valid = True | ||
| 205 | + try: | ||
| 206 | + modelpos = model.spatial().dir() | ||
| 207 | + except AttributeError: | ||
| 208 | + msg = ('Skip model "%s" since it has no sky direction.\n' % | ||
| 209 | + model.name()) | ||
| 210 | + is_valid = False | ||
| 211 | + | ||
| 212 | + # Continue only if sky direction was found | ||
| 213 | + if is_valid: | ||
| 214 | + | ||
| 215 | + # Retrieve model type and name | ||
| 216 | + modeltype = model.type() | ||
| 217 | + modelname = model.name() | ||
| 218 | + | ||
| 219 | + # Handle point source | ||
| 220 | + if modeltype == 'PointSource': | ||
| 221 | + | ||
| 222 | + # Append point with Right Ascension and Declination to the DS9 | ||
| 223 | + # string. The type of the point is specified by the "pnt_type" | ||
| 224 | + # parameter, the size of the point by the "pnt_mark_size" | ||
| 225 | + # parameter | ||
| 226 | + ds9string += ('point(%.6f,%.6f) # point=%s %d' % | ||
| 227 | + (modelpos.ra_deg(), modelpos.dec_deg(), | ||
| 228 | + pnt_type, pnt_mark_size)) | ||
| 229 | + | ||
| 230 | + # Handle extended sources | ||
| 231 | + elif modeltype == "ExtendedSource": | ||
| 232 | + | ||
| 233 | + # Retrieve spatial model | ||
| 234 | + spatial = model.spatial() | ||
| 235 | + classname = spatial.classname() | ||
| 236 | + | ||
| 237 | + # Handle radial sources | ||
| 238 | + if 'Radial' in classname: | ||
| 239 | + | ||
| 240 | + # Retrieve short name of model class (e.g. "Gauss" | ||
| 241 | + # or "Disk) | ||
| 242 | + shorttype = classname.split('Radial')[-1] | ||
| 243 | + | ||
| 244 | + # Handle Disk and Shell model | ||
| 245 | + if (classname == 'GModelSpatialRadialDisk' or | ||
| 246 | + classname == 'GModelSpatialRadialShell'): | ||
| 247 | + size = spatial.radius() | ||
| 248 | + | ||
| 249 | + # Handle Gauss Model | ||
| 250 | + elif classname == 'GModelSpatialRadialGauss': | ||
| 251 | + size = spatial.sigma() | ||
| 252 | + | ||
| 253 | + # Skip if source is unknown | ||
| 254 | + else: | ||
| 255 | + msg = ('Skip model "%s" since the radial model "%s" ' | ||
| 256 | + 'is unknown.\n' % (model.name(), classname)) | ||
| 257 | + is_valid = False | ||
| 258 | + | ||
| 259 | + # Append circle to DS9 string | ||
| 260 | + if is_valid: | ||
| 261 | + ds9string += ('circle(%.6f,%.6f,%.6f) #' % | ||
| 262 | + (modelpos.ra_deg(), modelpos.dec_deg(), | ||
| 263 | + size*3600.0)) | ||
| 264 | + | ||
| 265 | + # Handle elliptical sources | ||
| 266 | + elif 'Elliptical' in classname: | ||
| 267 | + | ||
| 268 | + # Retrieve short name and source size | ||
| 269 | + shorttype = classname.split('Elliptical')[-1] | ||
| 270 | + size1 = spatial.semimajor() | ||
| 271 | + size2 = spatial.semiminor() | ||
| 272 | + angle = spatial.posangle() | ||
| 273 | + | ||
| 274 | + # Append ellipse to DS9 string | ||
| 275 | + ds9string += ('ellipse(%.6f,%.6f,%.6f,%.6f,%.6f) #' % | ||
| 276 | + (modelpos.ra_deg(), modelpos.dec_deg(), | ||
| 277 | + size1*3600.0, size2*3600.0, angle+90.0)) | ||
| 278 | + | ||
| 279 | + # Skip if source is neither radial nor elliptical | ||
| 280 | + else: | ||
| 281 | + msg = ('Skip model "%s" since the model "%s" is neither ' | ||
| 282 | + 'a point source, a radial source, nor an elliptical ' | ||
| 283 | + 'source.\n' % (model.name(), classname)) | ||
| 284 | + is_valid = False | ||
| 285 | + | ||
| 286 | + # Add short model type to modelname | ||
| 287 | + if show_ext_type: | ||
| 288 | + modelname +=' ('+shorttype+')' | ||
| 289 | + | ||
| 290 | + # Add DS9 attributes | ||
| 291 | + if is_valid: | ||
| 292 | + ds9string += ds9attributes(model, free_color=free_color, | ||
| 293 | + fixed_color=fixed_color, | ||
| 294 | + width=width, | ||
| 295 | + fontfamily=fontfamily, | ||
| 296 | + fontsize=fontsize, | ||
| 297 | + fontweight=fontweight, | ||
| 298 | + fontslant=fontslant) | ||
| 299 | + if show_labels: | ||
| 300 | + ds9string += ' text={'+modelname+'}' | ||
| 301 | + else: | ||
| 302 | + ds9string = '' | ||
| 303 | + | ||
| 304 | + # Return ds9 and message strings | ||
| 305 | + return ds9string, msg | ||
| 306 | + | ||
| 307 | + | ||
| 308 | +# ========================= # | ||
| 309 | +# Save models into ds9 file # | ||
| 310 | +# ========================= # | ||
| 311 | +def models2ds9file(models, filename, | ||
| 312 | + pnt_type='cross', pnt_mark_size=12, | ||
| 313 | + show_labels=True, show_ext_type=True, | ||
| 314 | + free_color='green', fixed_color='magenta', | ||
| 315 | + width=2, fontfamily='helvetica', fontsize=12, | ||
| 316 | + fontweight='normal', fontslant='roman'): | ||
| 317 | + """ | ||
| 318 | + Save models in a DS9 region file | ||
| 319 | + | ||
| 320 | + Parameters | ||
| 321 | + ---------- | ||
| 322 | + model : `~gammalib.GModel` | ||
| 323 | + Model | ||
| 324 | + filename : str | ||
| 325 | + DS9 region file name | ||
| 326 | + pnt_type : str, optional | ||
| 327 | + Marker type for point sources (circle,box,diamond,cross,x,arrow,boxcircle) | ||
| 328 | + pnt_mark_size : integer, optional | ||
| 329 | + Marker size for point sources | ||
| 330 | + show_labels : boolean, optional | ||
| 331 | + Add source labels? | ||
| 332 | + show_ext_type : boolean, optional | ||
| 333 | + Show type of extended model in source name? | ||
| 334 | + free_color : str, optional | ||
| 335 | + Color for sources with free parameters (any DS9 color or hex code) | ||
| 336 | + fixed_color : str, optional | ||
| 337 | + Color for source without free parameters (any DS9 color or hex code) | ||
| 338 | + width : int, optional | ||
| 339 | + Line width for regions | ||
| 340 | + fontfamily : str, optional | ||
| 341 | + Font family for source labels (helvetica,times,courier) | ||
| 342 | + fontsize : int, optional | ||
| 343 | + Font size for source labels | ||
| 344 | + fontweight : str, optional | ||
| 345 | + Font weight for source labels (normal,bold) | ||
| 346 | + fontslant : str, optional | ||
| 347 | + Font slant for source labels (roman,italic) | ||
| 348 | + | ||
| 349 | + Returns | ||
| 350 | + ------- | ||
| 351 | + errors : str | ||
| 352 | + Error message | ||
| 353 | + """ | ||
| 354 | + # Initialise error string | ||
| 355 | + errors = '' | ||
| 356 | + | ||
| 357 | + # Open file | ||
| 358 | + f = open(filename, 'w') | ||
| 359 | + | ||
| 360 | + # Write coordinate system | ||
| 361 | + f.write('fk5\n') | ||
| 362 | + | ||
| 363 | + # Loop over models | ||
| 364 | + for model in models: | ||
| 365 | + | ||
| 366 | + # Continue only if point source or extended source model | ||
| 367 | + if (model.type() == 'PointSource' or | ||
| 368 | + model.type() == 'ExtendedSource'): | ||
| 369 | + line, msg = model2ds9string(model, | ||
| 370 | + pnt_type=pnt_type, | ||
| 371 | + pnt_mark_size=pnt_mark_size, | ||
| 372 | + free_color=free_color, | ||
| 373 | + fixed_color=fixed_color, | ||
| 374 | + width=width, | ||
| 375 | + fontfamily=fontfamily, | ||
| 376 | + fontsize=fontsize, | ||
| 377 | + fontweight=fontweight, | ||
| 378 | + fontslant=fontslant) | ||
| 379 | + if len(line): | ||
| 380 | + f.write(line+'\n') | ||
| 381 | + elif len(msg): | ||
| 382 | + errors += msg+'\n' | ||
| 383 | + | ||
| 384 | + # Logging for diffuse components | ||
| 385 | + elif model.type() == 'DiffuseSource': | ||
| 386 | + errors += 'Skipping diffuse model "'+model.name()+'".\n' | ||
| 387 | + | ||
| 388 | + # Logging for background components | ||
| 389 | + else: | ||
| 390 | + errors += 'Skipping background model "'+model.name()+'".\n' | ||
| 391 | + | ||
| 392 | + # Close file | ||
| 393 | + f.close() | ||
| 394 | + | ||
| 395 | + # Return error message string | ||
| 396 | + return errors |
doc/source/users/reference_manual/cssrcdetect.rst
0 → 100644
| 1 | +.. _cssrcdetect: | ||
| 2 | + | ||
| 3 | +cssrcdetect | ||
| 4 | +=========== | ||
| 5 | + | ||
| 6 | +Detects sources in sky map and writes results into a model definition XML file. | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +Synopsis | ||
| 10 | +-------- | ||
| 11 | + | ||
| 12 | +This script detects sources in a sky map using a peak detection method and | ||
| 13 | +writes the detected sources into a model definition XML file. In addition, | ||
| 14 | +the script produces also a DS9 region file of the detected sources that can | ||
| 15 | +be overlayed over a sky map displayed with DS9. | ||
| 16 | + | ||
| 17 | +For the moment, the script writes out all sources as point sources. Optionally, | ||
| 18 | +a background model can be added to the model definition XML file so that the | ||
| 19 | +output file can be used directly as input of a :doc:`ctlike` run. | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +General parameters | ||
| 23 | +------------------ | ||
| 24 | + | ||
| 25 | +``inmap [file]`` | ||
| 26 | + Input sky map file. | ||
| 27 | + | ||
| 28 | +``outmodel [file]`` | ||
| 29 | + Output model definition XML file. | ||
| 30 | + | ||
| 31 | +``outds9file [file]`` | ||
| 32 | + Output DS9 region file. | ||
| 33 | + | ||
| 34 | +``srcmodel <POINT> [string]`` | ||
| 35 | + Source model type. For the moment, all detected sources will be added as | ||
| 36 | + point sources with power law spectral shapes to the model definition | ||
| 37 | + XML file. | ||
| 38 | + | ||
| 39 | +``bkgmodel <NONE|IRF|AEFF|CUBE> [string]`` | ||
| 40 | + Background model type. Using ``NONE`` no background model will be added | ||
| 41 | + to the model definition XML file. Using ``IRF`` a background model based | ||
| 42 | + on the template information in the Instrument Response Function will be | ||
| 43 | + added. Using ``AEFF`` a background model based on the shape of the effective | ||
| 44 | + area will be added. Using ``CUBE`` a background model for stacked analysis | ||
| 45 | + will be added. | ||
| 46 | + | ||
| 47 | +``threshold [real]`` | ||
| 48 | + Detection threshold (Gaussian sigma). Only sources above this detection | ||
| 49 | + threshold will be added to the model definiton XML file. | ||
| 50 | + | ||
| 51 | +``(maxsrcs = 20) [integer]`` | ||
| 52 | + Maximum number of sources that should be detected. | ||
| 53 | + | ||
| 54 | +``(exclrad = 0.2) [real]`` | ||
| 55 | + Radius around a detected source that is excluded from further source | ||
| 56 | + detection (degrees). | ||
| 57 | + | ||
| 58 | +``(fit_pos = yes) [boolean]`` | ||
| 59 | + Enable fitting of source positions in model definiton XML file? | ||
| 60 | + | ||
| 61 | +``(fit_shape = yes) [boolean]`` | ||
| 62 | + Enable fitting of source shapes in model definiton XML file? | ||
| 63 | + | ||
| 64 | + | ||
| 65 | +Standard parameters | ||
| 66 | +------------------- | ||
| 67 | + | ||
| 68 | +``(chatter = 2) [integer]`` | ||
| 69 | + Verbosity of the executable: | ||
| 70 | + chatter = 0: no information will be logged | ||
| 71 | + | ||
| 72 | + chatter = 1: only errors will be logged | ||
| 73 | + | ||
| 74 | + chatter = 2: errors and actions will be logged | ||
| 75 | + | ||
| 76 | + chatter = 3: report about the task execution | ||
| 77 | + | ||
| 78 | + chatter = 4: detailed report about the task execution | ||
| 79 | + | ||
| 80 | +``(clobber = yes) [boolean]`` | ||
| 81 | + Specifies whether an existing output model file should be overwritten. | ||
| 82 | + | ||
| 83 | +``(debug = no) [boolean]`` | ||
| 84 | + Enables debug mode. In debug mode the executable will dump any log file | ||
| 85 | + output to the console. | ||
| 86 | + | ||
| 87 | +``(mode = ql) [string]`` | ||
| 88 | + Mode of automatic parameters (default is "ql", i.e. "query and learn"). | ||
| 89 | + | ||
| 90 | +``(logfile = cssrcdetect.log) [filename]`` | ||
| 91 | + Log filename. | ||
| 92 | + | ||
| 93 | + | ||
| 94 | +Related tools or scripts | ||
| 95 | +------------------------ | ||
| 96 | + | ||
| 97 | +None |
doc/source/users/reference_manual/index.rst
| @@ -40,7 +40,6 @@ cscripts | @@ -40,7 +40,6 @@ cscripts | ||
| 40 | :maxdepth: 1 | 40 | :maxdepth: 1 |
| 41 | 41 | ||
| 42 | cscaldb --- Lists available instrument response functions <cscaldb> | 42 | cscaldb --- Lists available instrument response functions <cscaldb> |
| 43 | - csviscube --- Computes visibility cube <csviscube> | ||
| 44 | cslightcrv --- Computes lightcurve <cslightcrv> | 43 | cslightcrv --- Computes lightcurve <cslightcrv> |
| 45 | csmodelinfo --- Shows model container content <csmodelinfo> | 44 | csmodelinfo --- Shows model container content <csmodelinfo> |
| 46 | csmodelmerge --- Merges several model containers into one file <csmodelmerge> | 45 | csmodelmerge --- Merges several model containers into one file <csmodelmerge> |
| @@ -52,11 +51,12 @@ cscripts | @@ -52,11 +51,12 @@ cscripts | ||
| 52 | csresmap --- Generates residual map <csresmap> | 51 | csresmap --- Generates residual map <csresmap> |
| 53 | cssens --- Computes CTA sensitivity <cssens> | 52 | cssens --- Computes CTA sensitivity <cssens> |
| 54 | csspec --- Computes spectral points <csspec> | 53 | csspec --- Computes spectral points <csspec> |
| 54 | + cssrcdetect --- Detects sources in sky map <cssrcdetect> | ||
| 55 | cstsdist --- Generates Test Statistic distribution <cstsdist> | 55 | cstsdist --- Generates Test Statistic distribution <cstsdist> |
| 56 | cstsmapsplit --- Creates commands to split the Test Statistic map computations <cstsmapsplit> | 56 | cstsmapsplit --- Creates commands to split the Test Statistic map computations <cstsmapsplit> |
| 57 | cstsmapmerge --- Merges slices from Test Statistic map computations <cstsmapmerge> | 57 | cstsmapmerge --- Merges slices from Test Statistic map computations <cstsmapmerge> |
| 58 | - | ||
| 59 | - | 58 | + csviscube --- Computes visibility cube <csviscube> |
| 59 | + | ||
| 60 | 60 | ||
| 61 | Scripts to manage an IACT database | 61 | Scripts to manage an IACT database |
| 62 | ---------------------------------- | 62 | ---------------------------------- |
test/data/skymap.fits
0 → 100644
No preview for this file type
test/test_cssrcdetect.py
0 → 100755
| 1 | +#! /usr/bin/env python | ||
| 2 | +# ========================================================================== | ||
| 3 | +# This scripts performs unit tests for the cssrcdetect script | ||
| 4 | +# | ||
| 5 | +# Copyright (C) 2016 Juergen Knoedlseder | ||
| 6 | +# | ||
| 7 | +# This program is free software: you can redistribute it and/or modify | ||
| 8 | +# it under the terms of the GNU General Public License as published by | ||
| 9 | +# the Free Software Foundation, either version 3 of the License, or | ||
| 10 | +# (at your option) any later version. | ||
| 11 | +# | ||
| 12 | +# This program is distributed in the hope that it will be useful, | ||
| 13 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | +# GNU General Public License for more details. | ||
| 16 | +# | ||
| 17 | +# You should have received a copy of the GNU General Public License | ||
| 18 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | +# | ||
| 20 | +# ========================================================================== | ||
| 21 | +import os | ||
| 22 | +import gammalib | ||
| 23 | +import cscripts | ||
| 24 | +from testing import test | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +# ================================= # | ||
| 28 | +# Test class for cssrcdetect script # | ||
| 29 | +# ================================= # | ||
| 30 | +class Test(test): | ||
| 31 | + """ | ||
| 32 | + Test class for cssrcdetect script | ||
| 33 | + | ||
| 34 | + This test class makes unit tests for the cssrcdetect script by using it | ||
| 35 | + from the command line and from Python. | ||
| 36 | + """ | ||
| 37 | + | ||
| 38 | + # Constructor | ||
| 39 | + def __init__(self): | ||
| 40 | + """ | ||
| 41 | + Constructor. | ||
| 42 | + """ | ||
| 43 | + # Call base class constructor | ||
| 44 | + test.__init__(self) | ||
| 45 | + | ||
| 46 | + # Set members | ||
| 47 | + self._map = self._datadir + '/skymap.fits' | ||
| 48 | + | ||
| 49 | + # Return | ||
| 50 | + return | ||
| 51 | + | ||
| 52 | + # Set test functions | ||
| 53 | + def set(self): | ||
| 54 | + """ | ||
| 55 | + Set all test functions. | ||
| 56 | + """ | ||
| 57 | + # Set test name | ||
| 58 | + self.name('cssrcdetect') | ||
| 59 | + | ||
| 60 | + # Append tests | ||
| 61 | + self.append(self._test_cmd, 'Test cssrcdetect on command line') | ||
| 62 | + self.append(self._test_python, 'Test cssrcdetect from Python') | ||
| 63 | + | ||
| 64 | + # Return | ||
| 65 | + return | ||
| 66 | + | ||
| 67 | + # Test cssrcdetect on command line | ||
| 68 | + def _test_cmd(self): | ||
| 69 | + """ | ||
| 70 | + Test cssrcdetect on the command line | ||
| 71 | + """ | ||
| 72 | + # Set script name | ||
| 73 | + cssrcdetect = self._script('cssrcdetect') | ||
| 74 | + | ||
| 75 | + # Setup cssrcdetect command | ||
| 76 | + cmd = cssrcdetect+' inmap="'+self._map+'"'+ \ | ||
| 77 | + ' outmodel="cssrcdetect_cmd1.xml"'+ \ | ||
| 78 | + ' outds9file="cssrcdetect_cmd1.reg"'+ \ | ||
| 79 | + ' srcmodel="POINT" bkgmodel="NONE" threshold=10.0'+ \ | ||
| 80 | + ' logfile="cssrcdetect_cmd1.log" chatter=1' | ||
| 81 | + | ||
| 82 | + # Check if execution of wrong command fails | ||
| 83 | + self.test_assert(self._execute('command_that_does_not_exist') != 0, | ||
| 84 | + 'Self test of test script') | ||
| 85 | + | ||
| 86 | + # Check if execution was successful | ||
| 87 | + self.test_assert(self._execute(cmd) == 0, | ||
| 88 | + 'Check successful execution from command line') | ||
| 89 | + | ||
| 90 | + # Check model file | ||
| 91 | + self._check_model_file('cssrcdetect_cmd1.xml', 2) | ||
| 92 | + | ||
| 93 | + # Setup cssrcdetect command | ||
| 94 | + cmd = cssrcdetect+' inmap="skymap_that_does_not_exist"'+ \ | ||
| 95 | + ' outmodel="cssrcdetect_cmd1.xml"'+ \ | ||
| 96 | + ' outds9file="cssrcdetect_cmd1.reg"'+ \ | ||
| 97 | + ' srcmodel="POINT" bkgmodel="NONE" threshold=10.0'+ \ | ||
| 98 | + ' logfile="cssrcdetect_cmd2.log" chatter=2' | ||
| 99 | + | ||
| 100 | + # Check if execution failed | ||
| 101 | + self.test_assert(self._execute(cmd) != 0, | ||
| 102 | + 'Check invalid input file when executed from command line') | ||
| 103 | + | ||
| 104 | + # Return | ||
| 105 | + return | ||
| 106 | + | ||
| 107 | + # Test cssrcdetect from Python | ||
| 108 | + def _test_python(self): | ||
| 109 | + """ | ||
| 110 | + Test cssrcdetect from Python | ||
| 111 | + """ | ||
| 112 | + # Allocate empty cssrcdetect script | ||
| 113 | + srcdetect = cscripts.cssrcdetect() | ||
| 114 | + | ||
| 115 | + # Check that saving does not nothing | ||
| 116 | + srcdetect['outmodel'] = 'cssrcdetect_py0.xml' | ||
| 117 | + srcdetect['outds9file'] = 'cssrcdetect_py0.reg' | ||
| 118 | + srcdetect['logfile'] = 'cssrcdetect_py0.log' | ||
| 119 | + srcdetect.logFileOpen() | ||
| 120 | + srcdetect.save() | ||
| 121 | + | ||
| 122 | + # Check than an empty model definition file has been created | ||
| 123 | + self._check_model_file('cssrcdetect_py0.xml', 0) | ||
| 124 | + | ||
| 125 | + # Check that clearing does not lead to an exception or segfault | ||
| 126 | + srcdetect.clear() | ||
| 127 | + | ||
| 128 | + # Now set cssrcdetect parameters | ||
| 129 | + srcdetect['inmap'] = self._map | ||
| 130 | + srcdetect['outmodel'] = 'cssrcdetect_py1.xml' | ||
| 131 | + srcdetect['outds9file'] = 'cssrcdetect_py1.reg' | ||
| 132 | + srcdetect['srcmodel'] = 'POINT' | ||
| 133 | + srcdetect['bkgmodel'] = 'NONE' | ||
| 134 | + srcdetect['threshold'] = 10.0 | ||
| 135 | + srcdetect['logfile'] = 'cssrcdetect_py1.log' | ||
| 136 | + srcdetect['chatter'] = 2 | ||
| 137 | + | ||
| 138 | + # Run cssrcdetect script and save models | ||
| 139 | + srcdetect.logFileOpen() # Make sure we get a log file | ||
| 140 | + srcdetect.run() | ||
| 141 | + srcdetect.save() | ||
| 142 | + | ||
| 143 | + # Check model file | ||
| 144 | + self._check_model_file('cssrcdetect_py1.xml', 2) | ||
| 145 | + | ||
| 146 | + # Execute cssrcdetect script again, now with a higher chatter level | ||
| 147 | + # than before | ||
| 148 | + srcdetect['outmodel'] = 'cssrcdetect_py2.xml' | ||
| 149 | + srcdetect['outds9file'] = 'cssrcdetect_py2.reg' | ||
| 150 | + srcdetect['bkgmodel'] = 'IRF' | ||
| 151 | + srcdetect['logfile'] = 'cssrcdetect_py2.log' | ||
| 152 | + srcdetect['chatter'] = 3 | ||
| 153 | + srcdetect.logFileOpen() # Needed to get a new log file | ||
| 154 | + srcdetect.execute() | ||
| 155 | + | ||
| 156 | + # Check model file | ||
| 157 | + self._check_model_file('cssrcdetect_py2.xml', 3) | ||
| 158 | + | ||
| 159 | + # Return | ||
| 160 | + return | ||
| 161 | + | ||
| 162 | + # Check model file | ||
| 163 | + def _check_model_file(self, filename, number): | ||
| 164 | + """ | ||
| 165 | + Check model file | ||
| 166 | + """ | ||
| 167 | + # Open models | ||
| 168 | + models = gammalib.GModels(filename) | ||
| 169 | + | ||
| 170 | + # Check number of models | ||
| 171 | + self.test_value(models.size(), number, 'Check for '+str(number)+ | ||
| 172 | + ' models in XML file') | ||
| 173 | + | ||
| 174 | + # Return | ||
| 175 | + return |
test/test_python_cscripts.py
| @@ -40,6 +40,7 @@ import test_csresmap | @@ -40,6 +40,7 @@ import test_csresmap | ||
| 40 | import test_csroot2caldb | 40 | import test_csroot2caldb |
| 41 | import test_cssens | 41 | import test_cssens |
| 42 | import test_csspec | 42 | import test_csspec |
| 43 | +import test_cssrcdetect | ||
| 43 | import test_cstsdist | 44 | import test_cstsdist |
| 44 | import test_cstsmapmerge | 45 | import test_cstsmapmerge |
| 45 | import test_cstsmapsplit | 46 | import test_cstsmapsplit |
| @@ -126,6 +127,7 @@ def test(installed=False, debug=False): | @@ -126,6 +127,7 @@ def test(installed=False, debug=False): | ||
| 126 | test_csroot2caldb.Test(), | 127 | test_csroot2caldb.Test(), |
| 127 | test_cssens.Test(), | 128 | test_cssens.Test(), |
| 128 | test_csspec.Test(), | 129 | test_csspec.Test(), |
| 130 | + test_cssrcdetect.Test(), | ||
| 129 | test_cstsdist.Test(), | 131 | test_cstsdist.Test(), |
| 130 | test_cstsmapmerge.Test(), | 132 | test_cstsmapmerge.Test(), |
| 131 | test_cstsmapsplit.Test(), | 133 | test_cstsmapsplit.Test(), |