Commit 6049bf07b4d985a8e80d55dcfcdfab710e1c4ba5
1 parent
dd903c61
Add cssrcdetect script (#1617)
Showing
13 changed files
with
1127 additions
and
191 deletions
ChangeLog
NEWS
| 1 | 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 | 6 | Introduction |
| ... | ... | @@ -206,30 +206,33 @@ The class now derives from ctlikelihood (#1828). |
| 206 | 206 | |
| 207 | 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 | 211 | "obsutils" module (#1830). |
| 212 | 212 | |
| 213 | 213 | The csroot2caldb script now takes the zenith and azimuth angles as parameters |
| 214 | 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 | 217 | of hours that a given position on the sky is observable under a given zenith |
| 218 | 218 | angle by an IACT array during a given time period. The tool implements so far |
| 219 | 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 | 224 | Fix energy boundary handling in cssens that has been corrupted during code |
| 225 | 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 | 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 | 231 | definition XML file (#1881). |
| 232 | 232 | |
| 233 | +Add cssrcdetect script to detect sources in a sky map (#1617). | |
| 234 | + | |
| 235 | + | |
| 233 | 236 | |
| 234 | 237 | Examples |
| 235 | 238 | -------- | ... | ... |
cscripts/Makefile.am
| ... | ... | @@ -36,6 +36,7 @@ pythonscripts = $(builddir)/__init__.py \ |
| 36 | 36 | $(srcdir)/csroot2caldb.py \ |
| 37 | 37 | $(srcdir)/cssens.py \ |
| 38 | 38 | $(srcdir)/csspec.py \ |
| 39 | + $(srcdir)/cssrcdetect.py \ | |
| 39 | 40 | $(srcdir)/cstsdist.py \ |
| 40 | 41 | $(srcdir)/cstsmapmerge.py \ |
| 41 | 42 | $(srcdir)/cstsmapsplit.py \ |
| ... | ... | @@ -65,6 +66,7 @@ parfiles = $(srcdir)/cscaldb.par \ |
| 65 | 66 | $(srcdir)/csroot2caldb.par \ |
| 66 | 67 | $(srcdir)/cssens.par \ |
| 67 | 68 | $(srcdir)/csspec.par \ |
| 69 | + $(srcdir)/cssrcdetect.par \ | |
| 68 | 70 | $(srcdir)/cstsdist.par \ |
| 69 | 71 | $(srcdir)/cstsmapmerge.par \ |
| 70 | 72 | $(srcdir)/cstsmapsplit.par \ |
| ... | ... | @@ -91,6 +93,7 @@ linkscripts = cscaldb \ |
| 91 | 93 | csroot2caldb \ |
| 92 | 94 | cssens \ |
| 93 | 95 | csspec \ |
| 96 | + cssrcdetect \ | |
| 94 | 97 | cstsdist \ |
| 95 | 98 | cstsmapmerge \ |
| 96 | 99 | cstsmapsplit \ |
| ... | ... | @@ -119,6 +122,7 @@ testscripts = $(srcdir)/tests/__init__.py \ |
| 119 | 122 | $(top_srcdir)/test/test_csroot2caldb.py \ |
| 120 | 123 | $(top_srcdir)/test/test_cssens.py \ |
| 121 | 124 | $(top_srcdir)/test/test_csspec.py \ |
| 125 | + $(top_srcdir)/test/test_cssrcdetect.py \ | |
| 122 | 126 | $(top_srcdir)/test/test_cstsdist.py \ |
| 123 | 127 | $(top_srcdir)/test/test_cstsmapmerge.py \ |
| 124 | 128 | $(top_srcdir)/test/test_cstsmapsplit.py \ |
| ... | ... | @@ -160,6 +164,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \ |
| 160 | 164 | $(top_srcdir)/test/data/pull.dat \ |
| 161 | 165 | $(top_srcdir)/test/data/sensitivity.dat \ |
| 162 | 166 | $(top_srcdir)/test/data/sensitivity_integral.dat \ |
| 167 | + $(top_srcdir)/test/data/skymap.fits \ | |
| 163 | 168 | $(top_srcdir)/test/data/spectrum.fits \ |
| 164 | 169 | $(top_srcdir)/test/data/ts.dat \ |
| 165 | 170 | $(top_srcdir)/test/data/models_spatial.xml \ | ... | ... |
cscripts/__init__.py.in
| ... | ... | @@ -39,6 +39,7 @@ __all__ = [ |
| 39 | 39 | "csroot2caldb", |
| 40 | 40 | "cssens", |
| 41 | 41 | "csspec", |
| 42 | + "cssrcdetect", | |
| 42 | 43 | "cstsdist", |
| 43 | 44 | "cstsmapmerge", |
| 44 | 45 | "cstsmapsplit", |
| ... | ... | @@ -71,6 +72,7 @@ from cscripts.csresmap import csresmap |
| 71 | 72 | from cscripts.csroot2caldb import csroot2caldb |
| 72 | 73 | from cscripts.cssens import cssens |
| 73 | 74 | from cscripts.csspec import csspec |
| 75 | +from cscripts.cssrcdetect import cssrcdetect | |
| 74 | 76 | from cscripts.cstsdist import cstsdist |
| 75 | 77 | from cscripts.cstsmapmerge import cstsmapmerge |
| 76 | 78 | from cscripts.cstsmapsplit import cstsmapsplit | ... | ... |
cscripts/csmodelinfo.py
| ... | ... | @@ -21,6 +21,7 @@ |
| 21 | 21 | import sys |
| 22 | 22 | import gammalib |
| 23 | 23 | import ctools |
| 24 | +from cscripts import modutils | |
| 24 | 25 | |
| 25 | 26 | |
| 26 | 27 | # ================= # |
| ... | ... | @@ -86,155 +87,6 @@ class csmodelinfo(ctools.cscript): |
| 86 | 87 | # Return |
| 87 | 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 | 91 | # Public methods |
| 240 | 92 | def run(self): |
| ... | ... | @@ -407,36 +259,34 @@ class csmodelinfo(ctools.cscript): |
| 407 | 259 | |
| 408 | 260 | # Log filename |
| 409 | 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 | 290 | # Return |
| 441 | 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 | 99 | # Return model container |
| 100 | 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 | 40 | :maxdepth: 1 |
| 41 | 41 | |
| 42 | 42 | cscaldb --- Lists available instrument response functions <cscaldb> |
| 43 | - csviscube --- Computes visibility cube <csviscube> | |
| 44 | 43 | cslightcrv --- Computes lightcurve <cslightcrv> |
| 45 | 44 | csmodelinfo --- Shows model container content <csmodelinfo> |
| 46 | 45 | csmodelmerge --- Merges several model containers into one file <csmodelmerge> |
| ... | ... | @@ -52,11 +51,12 @@ cscripts |
| 52 | 51 | csresmap --- Generates residual map <csresmap> |
| 53 | 52 | cssens --- Computes CTA sensitivity <cssens> |
| 54 | 53 | csspec --- Computes spectral points <csspec> |
| 54 | + cssrcdetect --- Detects sources in sky map <cssrcdetect> | |
| 55 | 55 | cstsdist --- Generates Test Statistic distribution <cstsdist> |
| 56 | 56 | cstsmapsplit --- Creates commands to split the Test Statistic map computations <cstsmapsplit> |
| 57 | 57 | cstsmapmerge --- Merges slices from Test Statistic map computations <cstsmapmerge> |
| 58 | - | |
| 59 | - | |
| 58 | + csviscube --- Computes visibility cube <csviscube> | |
| 59 | + | |
| 60 | 60 | |
| 61 | 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 | 40 | import test_csroot2caldb |
| 41 | 41 | import test_cssens |
| 42 | 42 | import test_csspec |
| 43 | +import test_cssrcdetect | |
| 43 | 44 | import test_cstsdist |
| 44 | 45 | import test_cstsmapmerge |
| 45 | 46 | import test_cstsmapsplit |
| ... | ... | @@ -126,6 +127,7 @@ def test(installed=False, debug=False): |
| 126 | 127 | test_csroot2caldb.Test(), |
| 127 | 128 | test_cssens.Test(), |
| 128 | 129 | test_csspec.Test(), |
| 130 | + test_cssrcdetect.Test(), | |
| 129 | 131 | test_cstsdist.Test(), |
| 130 | 132 | test_cstsmapmerge.Test(), |
| 131 | 133 | test_cstsmapsplit.Test(), | ... | ... |