Commit 25a6a46b78a0f2a94fe8860a7160881863551302

Authored by Jürgen Knödlseder
1 parent 85273f2e

Adapt to new GApplication interface with private methods and add par file.

cscripts/Makefile.am
... ... @@ -10,7 +10,10 @@ pythonscripts = __init__.py \
10 10 obsutils.py
11 11  
12 12 # Parfiles to be distributed in $(prefix)/syspfiles
13   -parfiles = cscaldb.par
  13 +parfiles = cspull.par cssens.par cstsdist.par cscaldb.par csresmap.par \
  14 + csobsdef.par csspec.par csobsinfo.par \
  15 + csmodelinfo.par csmodelmerge.par cstsmapmerge.par \
  16 + csobs2caldb.par
14 17  
15 18 # Scripts for which symbolic links will be created in bindir
16 19 linkscripts = cspull cssens cstsdist cscaldb csresmap \
... ...
cscripts/cscaldb.par
... ... @@ -7,7 +7,7 @@
7 7 # File: cscaldb.par #
8 8 # Version: 1.1.0 #
9 9 # #
10   -# Author: Jurgen Knodlseder #
  10 +# Author: Juergen Knoedlseder #
11 11 # Jurgen.Knodlseder@irap.omp.eu #
12 12 # IRAP #
13 13 # #
... ... @@ -20,5 +20,5 @@
20 20 chatter, i, h, 2,0,4, "Chattiness of output"
21 21 clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
22 22 debug, b, h, no,,, "Debugging mode activated"
23   -mode, s, h, "ql",,, "Mode of automatic parameters"
24   -logfile, f, h, "cscaldb.log",,, "Log filename"
  23 +mode, s, h, ql,,, "Mode of automatic parameters"
  24 +logfile, f, h, cscaldb.log,,, "Log filename"
... ...
cscripts/csmodelinfo.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csmodelinfo.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csmodelinfo script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inmodel, f, a, model.xml,,, "Input model XML file"
  21 +ds9file, f, h, NONE,,, "Output DS9 region file containing models"
  22 +
  23 +#
  24 +# Script parameters
  25 +#==================
  26 +pnt_type, s, h, cross,circle|box|diamond|cross|x|arrow|boxcircle,, "Marker type for point sources"
  27 +pnt_mark_size, i, h, 12,,, "Marker size for point sources"
  28 +show_labels, b, h, yes,yes|no,, "Add source labels?"
  29 +width, i, h, 2,,, "Line width for regions"
  30 +fontfamily, s, h, helvetica,helvetica|times|courier,, "Font family for source labels"
  31 +fontsize, i, h, 12,,, "Font size for source labels"
  32 +fontweight, s, h, normal,normal|bold,, "Font weight for source labels"
  33 +fontslant, s, h, roman,roman|italic,, "Font slant for source labels"
  34 +show_ext_type, b, h, yes,,, "Show type of extended model in source name?"
  35 +free_color, s, h, green,,, "Color for sources with free parameters (any DS9 color or hex code)"
  36 +fixed_color, s, h, magenta,,, "Color for source without free parameters (any DS9 color or hex code)"
  37 +
  38 +#
  39 +# Standard parameters
  40 +#====================
  41 +chatter, i, h, 2,0,4, "Chattiness of output"
  42 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  43 +debug, b, h, no,,, "Debugging mode activated"
  44 +mode, s, h, ql,,, "Mode of automatic parameters"
  45 +logfile, f, h, csmodelinfo.log,,, "Log filename"
... ...
cscripts/csmodelinfo.py
1 1 #!/usr/bin/env python
2 2 # ==========================================================================
3   -# Dump information about models into log file
  3 +# Shows the content of a model container
4 4 #
5 5 # Copyright (C) 2015-2016 Michael Mayer
6 6 #
... ... @@ -28,34 +28,50 @@ import sys
28 28 # ================= #
29 29 class csmodelinfo(ctools.cscript):
30 30 """
31   - This class dumps information about a model container into a logfile or
32   - on screen. This might be helpful for quick access to the model container,
33   - showing, e.g., number of sources, background models, free and fixed
34   - parameters etc. The tool can also dump the models into a region file to
35   - display e.g. with ds9.
  31 + Shows the content of a model container.
  32 +
  33 + The script is useful to show, e.g., the number of sources in a model
  34 + container, the background models, free and fixed parameters etc.
  35 + The script can also dump the models into a region file to for display
  36 + with DS9.
36 37 """
  38 +
  39 + # Constructors and destructors
37 40 def __init__(self, *argv):
38 41 """
39 42 Constructor.
40 43 """
41 44 # Set name
42   - self.name = "csmodelinfo"
43   - self.version = "1.1.0"
  45 + self._name = "csmodelinfo"
  46 + self._version = "1.1.0"
44 47  
45   - # Make sure that parfile exists
46   - file = self.parfile()
  48 + # Initialise class members
  49 + self._models = gammalib.GModels()
  50 + self._ds9file = "NONE"
  51 + self._pnt_type = ""
  52 + self._pnt_mark_size = 12
  53 + self._show_labels = True
  54 + self._width = 2
  55 + self._fontfamily = "helvetica"
  56 + self._fontsize = 12
  57 + self._fontweight = "normal"
  58 + self._fontslant = "roman"
  59 + self._show_ext_type = True
  60 + self._free_color = "green"
  61 + self._fixed_color = "magenta"
  62 + self._read_ahead = False
47 63  
48 64 # Initialise application
49 65 if len(argv) == 0:
50   - ctools.cscript.__init__(self, self.name, self.version)
  66 + ctools.cscript.__init__(self, self._name, self._version)
51 67 elif len(argv) == 1:
52   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  68 + ctools.cscript.__init__(self, self._name, self._version, *argv)
53 69 else:
54 70 raise TypeError("Invalid number of arguments given.")
55 71  
56 72 # Set logger properties
57   - self.log_header()
58   - self.log.date(True)
  73 + self._log_header()
  74 + self._log.date(True)
59 75  
60 76 # Return
61 77 return
... ... @@ -67,92 +83,197 @@ class csmodelinfo(ctools.cscript):
67 83 # Return
68 84 return
69 85  
70   - def parfile(self):
71   - """
72   - Check if parfile exists. If parfile does not exist then create a
73   - default parfile. This kluge avoids shipping the cscript with a
74   - parfile.
75   - """
76   -
77   - # Set parfile name
78   - parfile = self.name+".par"
79   -
80   - try:
81   - pars = gammalib.GApplicationPars(parfile)
82   - except:
83   - # Signal that parfile was not found
84   - print("Parfile \""+parfile+"\" not found. Create default parfile.")
85   -
86   - # Create default parfile
87   - pars = gammalib.GApplicationPars()
88   - pars.append(gammalib.GApplicationPar("inmodel","f","a","model.xml","","","Input model XML file"))
89   - pars.append(gammalib.GApplicationPar("ds9file","f","h","NONE","","","DS9 region file containing models"))
90   - pars.append(gammalib.GApplicationPar("pnt_type","s","h","cross","circle|box|diamond|cross|x|arrow|boxcircle","","Marker type for point sources"))
91   - pars.append(gammalib.GApplicationPar("pnt_mark_size","i","h","12","","","Marker size for point sources"))
92   - pars.append(gammalib.GApplicationPar("show_labels","b","h","yes","yes|no","","Add source labels?"))
93   - pars.append(gammalib.GApplicationPar("width","i","h","2","","","Line width for regions"))
94   - pars.append(gammalib.GApplicationPar("font","s","h","helvetica","helvetica|times|courier","","Font for source labels"))
95   - pars.append(gammalib.GApplicationPar("fontsize","i","h","12","","","Font size for source labels"))
96   - pars.append(gammalib.GApplicationPar("fonttype","s","h","normal","normal|bold","","Use normal or bold font?"))
97   - pars.append(gammalib.GApplicationPar("fonttype2","s","h","roman","roman|italic","","Use roman or italic font?"))
98   - pars.append(gammalib.GApplicationPar("show_ext_type","b","h","yes","yes|no","","Show type of extended model in source name"))
99   - pars.append(gammalib.GApplicationPar("free_color","s","h","green","","","Color for sources with free parameters (any ds9 color or hex code)"))
100   - pars.append(gammalib.GApplicationPar("fixed_color","s","h","magenta","","","Color for source without free parameters (any ds9 color or hex code)"))
101   - pars.append_standard()
102   - pars.append(gammalib.GApplicationPar("logfile","f","h","csmodelinfo.log","","","Log filename"))
103   - pars.save(parfile)
104 86  
105   - # Return
106   - return
107   -
108   - def get_parameters(self):
  87 + # Private methods
  88 + def _get_parameters(self):
109 89 """
110 90 Get parameters from parfile and setup the observation.
111 91 """
112 92 # Get models
113   - self.models = gammalib.GModels(self["inmodel"].filename())
114   -
115   - # Get ds9 filename
116   - self.ds9file = self["ds9file"].filename()
  93 + self._models = gammalib.GModels(self["inmodel"].filename())
117 94  
118 95 # Get hidden parameters for region file
119   - self.pnt_type = self["pnt_type"].string()
120   - self.pnt_mark_size = self["pnt_mark_size"].integer()
121   - self.show_labels = self["show_labels"].boolean()
122   - self.width = self["width"].integer()
123   - self.font = self["font"].string()
124   - self.fontsize = self["fontsize"].integer()
125   - self.fonttype = self["fonttype"].string()
126   - self.fonttype2 = self["fonttype2"].string()
127   - self.show_ext_type = self["show_ext_type"].boolean()
128   - self.free_color = self["free_color"].string()
129   - self.fixed_color = self["fixed_color"].string()
  96 + self._pnt_type = self["pnt_type"].string()
  97 + self._pnt_mark_size = self["pnt_mark_size"].integer()
  98 + self._show_labels = self["show_labels"].boolean()
  99 + self._width = self["width"].integer()
  100 + self._fontfamily = self["fontfamily"].string()
  101 + self._fontsize = self["fontsize"].integer()
  102 + self._fontweight = self["fontweight"].string()
  103 + self._fontslant = self["fontslant"].string()
  104 + self._show_ext_type = self["show_ext_type"].boolean()
  105 + self._free_color = self["free_color"].string()
  106 + self._fixed_color = self["fixed_color"].string()
  107 +
  108 + # Read ahead DS9 filename
  109 + if self._read_ahead:
  110 + self._ds9file = self["ds9file"].filename()
  111 +
  112 + # Write input parameters into logger
  113 + if self._logTerse():
  114 + self._log_parameters()
  115 + self._log("\n")
130 116  
131 117 # Return
132 118 return
  119 +
  120 + def _model2ds9string(self, model):
  121 + """
  122 + Converts model into a DS9 region string.
  123 +
  124 + Args:
  125 + model: Model.
  126 +
  127 + Returns:
  128 + DS9 region string.
  129 + """
  130 + # Initialise DS9 region string
  131 + ds9string = ""
  132 +
  133 + # Determine region color. The color will change between a model
  134 + # where all parameters are fixed and a model with at least one
  135 + # free parameter.
  136 + color = self._fixed_color
  137 + for par in model:
  138 + if par.is_free():
  139 + color = self._free_color
  140 + break
  141 +
  142 + # Retrieve model sky direction. The model is skipped in case that
  143 + # it does not provide a sky direction.
  144 + is_valid = True
  145 + try:
  146 + modelpos = model.spatial().dir()
  147 + except AttributeError:
  148 + self._log("Skip source \""+model.name()+"\" since no "
  149 + "sky direction is associated with that model.\n")
  150 + is_valid = False
  151 +
  152 + # Continue only if sky direction was found
  153 + if is_valid:
  154 +
  155 + # Retrieve model type and name
  156 + modeltype = model.type()
  157 + modelname = model.name()
  158 +
  159 + # Handle point source
  160 + if modeltype == "PointSource":
  161 +
  162 + # Append point to DS9 string
  163 + ds9string += "point("
  164 + ds9string += str(modelpos.ra_deg())
  165 + ds9string += ","
  166 + ds9string += str(modelpos.dec_deg())
  167 + ds9string += ") # "
  168 + ds9string += "point="+self._pnt_type+" "
  169 + ds9string += str(self._pnt_mark_size)+" "
  170 +
  171 + # Handle extended sources
  172 + elif modeltype == "ExtendedSource":
  173 +
  174 + # Retrieve spatial model
  175 + spatial = model.spatial()
  176 + classname = spatial.classname()
  177 +
  178 + # Handle radial sources
  179 + if "Radial" in classname:
  180 +
  181 + # Retrieve short name of model class (e.g. "Gauss"
  182 + # or "Disk)
  183 + shorttype = classname.split("Radial")[-1]
  184 +
  185 + # Handle Disk and Shell model
  186 + if (classname == "GModelSpatialRadialDisk" or
  187 + classname == "GModelSpatialRadialShell"):
  188 + size = spatial.radius()
  189 +
  190 + # Handle Gauss Model
  191 + elif classname == "GModelSpatialRadialGauss":
  192 + size = spatial.sigma()
  193 +
  194 + # Skip if source is unknown
  195 + else:
  196 + self._log("Skip source \""+model.name()+"\""
  197 + " since the radial model \""+classname+
  198 + "\" is unkown.\n")
  199 + is_valid = False
  200 +
  201 + # Append circle to DS9 string
  202 + if is_valid:
  203 + ds9string += "circle("
  204 + ds9string += str(modelpos.ra_deg())
  205 + ds9string += ","
  206 + ds9string += str(modelpos.dec_deg())
  207 + ds9string += ","
  208 + ds9string += str(size*3600.)+"\") # "
  209 +
  210 + # Handle elliptical sources
  211 + elif "Elliptical" in classname:
  212 +
  213 + # Retrieve short name and source size
  214 + shorttype = classname.split("Elliptical")[-1]
  215 + size1 = spatial.semimajor()
  216 + size2 = spatial.semiminor()
  217 + angle = spatial.posangle()
  218 +
  219 + # Append ellipse to DS9 string
  220 + ds9string += "ellipse("
  221 + ds9string += str(modelpos.ra_deg())
  222 + ds9string += ","
  223 + ds9string += str(modelpos.dec_deg())
  224 + ds9string += ","
  225 + ds9string += str(size1*3600.)+"\""
  226 + ds9string += ","
  227 + ds9string += str(size2*3600.)+"\""
  228 + ds9string += ","
  229 + ds9string += str(angle+90.0)+") # "
  230 +
  231 + # Skip if source is neither radial nor elliptical
  232 + else:
  233 + self._log("Skip source \""+model.name()+"\" "
  234 + "since the model \""+classname+"\" is "
  235 + "neither a point source, a radial source, "
  236 + "nor a elliptical source.\n")
  237 + is_valid = False
  238 +
  239 + # Add short model type to modelname
  240 + if self._show_ext_type:
  241 + modelname +=" ("+shorttype+")"
  242 +
  243 + # Add DS9 attributes
  244 + if is_valid:
  245 + ds9string += "color="+color+" "
  246 + ds9string += "width="+str(self._width)+" "
  247 + ds9string += "font=\""+self._fontfamily+" "
  248 + ds9string += str(self._fontsize)+" "
  249 + ds9string += self._fontweight+" "
  250 + ds9string += self._fontslant+"\""
  251 + if self._show_labels:
  252 + ds9string += " text={"+modelname+"}"
  253 +
  254 + # Return string
  255 + return ds9string
133 256  
  257 +
  258 + # Public methods
134 259 def run(self):
135 260 """
136 261 Run the script.
137 262 """
138 263 # Switch screen logging on in debug mode
139   - if self.logDebug():
140   - self.log.cout(True)
  264 + if self._logDebug():
  265 + self._log.cout(True)
141 266  
142 267 # Get parameters
143   - self.get_parameters()
  268 + self._get_parameters()
144 269  
145   - # Write input parameters into logger
146   - if self.logTerse():
147   - self.log_parameters()
148   - self.log("\n")
149   -
150   - if self.logVerbose():
151   - self.log("\n")
152   - self.log.header1("Models in detail")
153   - self.log("\n")
154   - self.log(str(self.models))
155   - self.log("\n")
  270 + # Optionally log models
  271 + if self._logVerbose():
  272 + self._log("\n")
  273 + self._log.header1("Models")
  274 + self._log("\n")
  275 + self._log(str(self._models))
  276 + self._log("\n")
156 277  
157 278 # Initialise output values
158 279 types = {}
... ... @@ -171,13 +292,13 @@ class csmodelinfo(ctools.cscript):
171 292 n_par_free_temp = 0
172 293  
173 294 # Loop over models and initialise dictionaries with model names
174   - for model in self.models:
  295 + for model in self._models:
175 296 types[model.type()] = 0
176 297 instruments[model.instruments()] = 0
177 298 pars_at_limit[model.name()] = []
178 299  
179 300 # Loop over models again and fill values to be dumped
180   - for model in self.models:
  301 + for model in self._models:
181 302  
182 303 # Model types
183 304 types[model.type()] += 1
... ... @@ -218,7 +339,8 @@ class csmodelinfo(ctools.cscript):
218 339 else:
219 340 n_par_fixed += 1
220 341  
221   - # If sky model, divide free parameters into spatial, spectral and temporal
  342 + # If sky model, divide free parameters into spatial, spectral
  343 + # and temporal
222 344 if skymodel:
223 345 for par in model.spectral():
224 346 if par.is_free():
... ... @@ -229,90 +351,93 @@ class csmodelinfo(ctools.cscript):
229 351 for par in model.temporal():
230 352 if par.is_free():
231 353 n_par_free_temp += 1
  354 +
  355 + # Optionally log model information
  356 + if self._logTerse():
232 357  
233   - # Log summary
234   - self.log("\n")
235   - self.log.header1("Summary")
  358 + # Log summary
  359 + self._log("\n")
  360 + self._log.header1("Summary")
236 361  
237   - # Log instruments
238   - self.log.header3("Instrument specific models")
239   - for inst, n_inst in instruments.items():
240   - if inst == "":
241   - inst = "All"
242   - self.log.parformat(inst)
243   - self.log(str(n_inst))
244   - self.log("\n")
  362 + # Log instruments
  363 + self._log.header3("Instrument specific models")
  364 + for inst, n_inst in instruments.items():
  365 + if inst == "":
  366 + inst = "All"
  367 + self._log.parformat(inst)
  368 + self._log(str(n_inst))
  369 + self._log("\n")
245 370  
246   - # Log model types
247   - self.log.header3("Model types")
248   - for modeltype, n_types in types.items():
249   - self.log.parformat(modeltype)
250   - self.log(str(n_types))
251   - self.log("\n")
  371 + # Log model types
  372 + self._log.header3("Model types")
  373 + for modeltype, n_types in types.items():
  374 + self._log.parformat(modeltype)
  375 + self._log(str(n_types))
  376 + self._log("\n")
252 377  
253   - # Log parameter information
254   - self.log("\n")
255   - self.log.header1("Parameter information")
256   - self.log.parformat("All parameters")
257   - self.log(str(n_par_total))
258   - self.log("\n")
259   - self.log.parformat("Fixed parameters")
260   - self.log(str(n_par_fixed))
261   - self.log("\n")
262   - self.log.parformat("Free parameters (total)")
263   - self.log(str(n_par_free))
264   - self.log("\n")
265   - self.log.parformat("Free background parameters")
266   - self.log(str(n_par_free_bkg))
267   - self.log("\n")
268   - self.log.parformat("Free source parameters")
269   - self.log(str(n_par_free_src))
270   - self.log("\n")
271   - if n_par_free_spec > 0:
272   - self.log.parformat("Free spectral parameters")
273   - self.log(str(n_par_free_spec))
274   - self.log("\n")
275   - if n_par_free_spat > 0:
276   - self.log.parformat("Free spatial parameters")
277   - self.log(str(n_par_free_spat))
278   - self.log("\n")
279   - if n_par_free_temp > 0:
280   - self.log.parformat("Free temporal parameters")
281   - self.log(str(n_par_free_temp))
282   - self.log("\n")
283   - self.log.parformat("Parameters at limit")
284   - self.log(str(n_par_at_limit))
285   - self.log("\n")
  378 + # Log model parameter information
  379 + self._log("\n")
  380 + self._log.header1("Parameter information")
  381 + self._log.parformat("All parameters")
  382 + self._log(str(n_par_total))
  383 + self._log("\n")
  384 + self._log.parformat("Fixed parameters")
  385 + self._log(str(n_par_fixed))
  386 + self._log("\n")
  387 + self._log.parformat("Free parameters (total)")
  388 + self._log(str(n_par_free))
  389 + self._log("\n")
  390 + self._log.parformat("Free background parameters")
  391 + self._log(str(n_par_free_bkg))
  392 + self._log("\n")
  393 + self._log.parformat("Free source parameters")
  394 + self._log(str(n_par_free_src))
  395 + self._log("\n")
  396 + if n_par_free_spec > 0:
  397 + self._log.parformat("Free spectral parameters")
  398 + self._log(str(n_par_free_spec))
  399 + self._log("\n")
  400 + if n_par_free_spat > 0:
  401 + self._log.parformat("Free spatial parameters")
  402 + self._log(str(n_par_free_spat))
  403 + self._log("\n")
  404 + if n_par_free_temp > 0:
  405 + self._log.parformat("Free temporal parameters")
  406 + self._log(str(n_par_free_temp))
  407 + self._log("\n")
  408 + self._log.parformat("Parameters at limit")
  409 + self._log(str(n_par_at_limit))
  410 + self._log("\n")
286 411  
287   - # Log parameters at limit (if any)
288   - if n_par_at_limit > 0:
289   - self.log.header3("Parameters at limit")
290   - for source, parameter in pars_at_limit.items():
291   - if len(parameter):
292   - for par in parameter:
293   - self.log.parformat(source)
294   - self.log(par)
295   - self.log("\n")
  412 + # Log parameters at limit (if any)
  413 + if n_par_at_limit > 0:
  414 + self._log.header3("Parameters at limit")
  415 + for source, parameter in pars_at_limit.items():
  416 + if len(parameter):
  417 + for par in parameter:
  418 + self._log.parformat(source)
  419 + self._log(par)
  420 + self._log("\n")
296 421  
297   - # Log free parameters if requested
298   - if self.logExplicit() and len(free_src_pars):
299   - self.log("\n")
300   - self.log.header2("Free source parameters")
301   - for source, parameter in free_src_pars.items():
302   - if len(parameter):
303   - self.log.header3(source)
304   - for par in parameter:
305   - self.log(str(par))
306   - self.log("\n")
307   - self.log("\n")
  422 + # Optionally log free parameters
  423 + if self._logExplicit() and len(free_src_pars):
  424 + self._log("\n")
  425 + self._log.header2("Free source parameters")
  426 + for source, parameter in free_src_pars.items():
  427 + if len(parameter):
  428 + self._log.header3(source)
  429 + for par in parameter:
  430 + self._log(str(par))
  431 + self._log("\n")
  432 + self._log("\n")
308 433  
309   - # Log TS values if available
310   - if len(ts):
311   - self.log.header3("Test statistics")
312   - for source,tsvalue in ts.items():
313   - self.log.parformat(source)
314   - self.log("%.2f"%tsvalue)
315   - self.log("\n")
  434 + # Log TS values if available
  435 + if len(ts):
  436 + self._log.header3("Test statistics")
  437 + for source,tsvalue in ts.items():
  438 + self._log.parformat(source)
  439 + self._log("%.2f"%tsvalue)
  440 + self._log("\n")
316 441  
317 442 # Return
318 443 return
... ... @@ -321,37 +446,49 @@ class csmodelinfo(ctools.cscript):
321 446 """
322 447 Save models to ds9 region file if required
323 448 """
324   -
325   - # Check if ds9 file is valid
326   - if not self.ds9file == "NONE":
  449 + # Write header
  450 + if self._logTerse():
  451 + self._log("\n")
  452 + self._log.header1("Save models in DS9 file")
  453 +
  454 + # Get output filename in case it was not read ahead
  455 + self._ds9file = self["ds9file"].filename()
  456 +
  457 + # Check if DS9 file is valid
  458 + if self._ds9file != "NONE":
327 459  
328   - self.log("\n")
329   - self.log.header1("Save region file")
  460 + # Log filename
  461 + if self._logTerse():
  462 + self._log(gammalib.parformat("DS9 filename"))
  463 + self._log(self._ds9file.url())
  464 + self._log("\n")
330 465  
331 466 # Open file
332   - f = open(self.ds9file.url(),"w")
  467 + f = open(self._ds9file.url(),"w")
333 468  
334 469 # Write coordinate system
335 470 f.write("fk5\n")
336 471  
337 472 # Loop over models
338   - for model in self.models:
  473 + for model in self._models:
339 474  
340 475 # Continue only if point source or extended source model
341   - if model.type() == "PointSource" or model.type() == "ExtendedSource":
342   -
343   - line = self.model2ds9string(model)
  476 + if (model.type() == "PointSource" or
  477 + model.type() == "ExtendedSource"):
  478 + line = self._model2ds9string(model)
344 479 if len(line):
345 480 f.write(line+"\n")
346 481  
347 482 # Logging for diffuse components
348 483 elif model.type() == "DiffuseSource":
349   - self.log("Skipping diffuse model "+model.name()+"\n")
  484 + self._log("Skipping diffuse model \""+model.name()+
  485 + "\"\n")
350 486  
351 487 # Logging for background components
352 488 else:
353   - if self.logExplicit():
354   - self.log("Skipping background model "+model.name()+"\n")
  489 + if self._logExplicit():
  490 + self._log("Skipping background model \""+
  491 + model.name()+"\"\n")
355 492  
356 493 # Close file
357 494 f.close()
... ... @@ -359,127 +496,13 @@ class csmodelinfo(ctools.cscript):
359 496 # Return
360 497 return
361 498  
362   - def model2ds9string(self, model):
363   - """
364   - Converts model instance to ds9 region string
365   - """
366   -
367   - # Initialise return string
368   - ds9string = ""
369   -
370   - # Determine color
371   - color = self.fixed_color
372   - for par in model:
373   - if par.is_free():
374   - color = self.free_color
375   - break
376   -
377   - # Initialise flag ot continue
378   - is_valid = True
379   -
380   - # Retrieve model position
381   - try:
382   - modelpos = model.spatial().dir()
383   - except AttributeError:
384   - self.log("Skipping source "+model.name()+": Could not determine sky position\n")
385   - is_valid = False
386   -
387   - # Continue only if position was found
388   - if is_valid:
389   -
390   - # Retrieve model type and name
391   - modeltype = model.type()
392   - modelname = model.name()
393   -
394   - # Handle point source
395   - if modeltype == "PointSource":
396   -
397   - ds9string += "point("
398   - ds9string += str(modelpos.ra_deg())
399   - ds9string += ","
400   - ds9string += str(modelpos.dec_deg())
401   - ds9string += ") # "
402   - ds9string += "point="+self.pnt_type+" "
403   - ds9string += str(self.pnt_mark_size)+" "
404   -
405   - # Hanlde extended sources
406   - elif modeltype == "ExtendedSource":
407   -
408   - # Retrieve spatial model
409   - spatial = model.spatial()
410   - classname = spatial.classname()
411   -
412   - # Handle radial sources
413   - if "Radial" in classname:
414   -
415   - # Retrieve short name of model class (e.g. "Gauss" or "Disk)
416   - shorttype = classname.split("Radial")[-1]
417   -
418   - # Handle Disk and Shell model
419   - if classname == "GModelSpatialRadialDisk" or classname == "GModelSpatialRadialShell":
420   - size = spatial.radius()
421   -
422   - # Handle Gauss Model
423   - elif classname == "GModelSpatialRadialGauss":
424   - size = spatial.sigma()
425   -
426   - # Skip if source is unknown
427   - else:
428   - self.log("Skipping source "+model.name()+": Spatial model \""+classname+"\" unkown\n")
429   - is_valid = False
430   -
431   - # Append radial ds9 string
432   - if is_valid:
433   - ds9string += "circle("
434   - ds9string += str(modelpos.ra_deg())
435   - ds9string += ","
436   - ds9string += str(modelpos.dec_deg())
437   - ds9string += ","
438   - ds9string += str(size*3600.)+"\") #"
439   -
440   - # Handle elliptical sources
441   - elif "Elliptical" in classname:
442   -
443   - # Retrieve short name and source size
444   - shorttype = classname.split("Elliptical")[-1]
445   - size1 = spatial.semimajor()
446   - size2 = spatial.semiminor()
447   - angle = spatial.posangle()
448   -
449   - # Append elliptical ds9 string
450   - ds9string += "ellipse("
451   - ds9string += str(modelpos.ra_deg())
452   - ds9string += ","
453   - ds9string += str(modelpos.dec_deg())
454   - ds9string += ","
455   - ds9string += str(size1*3600.)+"\""
456   - ds9string += ","
457   - ds9string += str(size2*3600.)+"\""
458   - ds9string += ","
459   - ds9string += str(angle+90.0)+") # "
460   -
461   - # Skip if source is neither radial nor elliptical
462   - else:
463   - self.log("Skipping source "+model.name()+": Spatial model neither \"Radial\" nor \"Elliptical\"\n")
464   -
465   - # Add short name to modelname
466   - if self.show_ext_type:
467   - modelname +=" ("+shorttype+")"
468   -
469   - # Add user defined attributes
470   - ds9string += "color="+color+" "
471   - ds9string += "width="+str(self.width)+" "
472   - ds9string += "font=\""+self.font+" "+str(self.fontsize)+" "+self.fonttype+" "+self.fonttype2+"\" "
473   - if self.show_labels:
474   - ds9string += "text={"+modelname+"}"
475   -
476   - # Return string
477   - return ds9string
478   -
479 499 def execute(self):
480 500 """
481 501 Execute the script.
482 502 """
  503 + # Set read ahead flag
  504 + self._read_ahead = True
  505 +
483 506 # Run the script
484 507 self.run()
485 508  
... ... @@ -494,14 +517,12 @@ class csmodelinfo(ctools.cscript):
494 517 # Main routine entry point #
495 518 # ======================== #
496 519 if __name__ == '__main__':
497   - """
498   - Generates model information output.
499   - """
  520 +
500 521 # Create instance of application
501 522 app = csmodelinfo(sys.argv)
502 523  
503 524 # Open logfile
504   - app.logFileOpen()
  525 + app._logFileOpen()
505 526  
506 527 # Execute application
507 528 app.execute()
... ...
cscripts/csmodelmerge.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csmodelmerge.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csmodelmerge script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inmodels, s, a, model.xml,,, "Input model definition XML files"
  21 +outmodel, f, a, NONE,,, "Output model definition XML file"
  22 +
  23 +#
  24 +# Standard parameters
  25 +#====================
  26 +chatter, i, h, 2,0,4, "Chattiness of output"
  27 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  28 +debug, b, h, no,,, "Debugging mode activated"
  29 +mode, s, h, ql,,, "Mode of automatic parameters"
  30 +logfile, f, h, csmodelmerge.log,,, "Log filename"
... ...
cscripts/csmodelmerge.py
1 1 #!/usr/bin/env python
2 2 # ==========================================================================
3   -# Merges model XML files into one file
  3 +# Merge model definition XML files
4 4 #
5   -# Copyright (C) 2015 Michael Mayer
  5 +# Copyright (C) 2015-2016 Michael Mayer
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -29,31 +29,38 @@ import glob
29 29 # ================== #
30 30 class csmodelmerge(ctools.cscript):
31 31 """
32   - This class merges an arbitray number of model container into one output
33   - model XML file.
  32 + Merge model definition XML files.
  33 +
  34 + An arbitrary number of model definition XML files will be merged into
  35 + a single model definition XML file.
34 36 """
  37 +
  38 + # Constructors and destructors
35 39 def __init__(self, *argv):
36 40 """
37 41 Constructor.
38 42 """
39 43 # Set name
40   - self.name = "csmodelmerge"
41   - self.version = "1.1.0"
  44 + self._name = "csmodelmerge"
  45 + self._version = "1.1.0"
42 46  
43   - # Make sure that parfile exists
44   - file = self.parfile()
  47 + # Initialise class members
  48 + self._files = None
  49 + self._models = gammalib.GModels()
  50 + self._outmodel = "NONE"
  51 + self._read_ahead = False
45 52  
46 53 # Initialise application
47 54 if len(argv) == 0:
48   - ctools.cscript.__init__(self, self.name, self.version)
  55 + ctools.cscript.__init__(self, self._name, self._version)
49 56 elif len(argv) == 1:
50   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  57 + ctools.cscript.__init__(self, self._name, self._version, *argv)
51 58 else:
52 59 raise TypeError("Invalid number of arguments given.")
53 60  
54 61 # Set logger properties
55   - self.log_header()
56   - self.log.date(True)
  62 + self._log_header()
  63 + self._log.date(True)
57 64  
58 65 # Return
59 66 return
... ... @@ -65,124 +72,122 @@ class csmodelmerge(ctools.cscript):
65 72 # Return
66 73 return
67 74  
68   - def parfile(self):
69   - """
70   - Check if parfile exists. If parfile does not exist then create a
71   - default parfile. This kluge avoids shipping the cscript with a
72   - parfile.
73   - """
74   -
75   - # Set parfile name
76   - parfile = self.name+".par"
77   -
78   - try:
79   - pars = gammalib.GApplicationPars(parfile)
80   - except:
81   - # Signal that parfile was not found
82   - print("Parfile \""+parfile+"\" not found. Create default parfile.")
83 75  
84   - # Create default parfile
85   - pars = gammalib.GApplicationPars()
86   - pars.append(gammalib.GApplicationPar("inmodels","s","a","model.xml","","","Input model XML files"))
87   - pars.append(gammalib.GApplicationPar("outmodel","f","a","NONE","","","Output model file"))
88   - pars.append_standard()
89   - pars.append(gammalib.GApplicationPar("logfile","f","h","csmodelmerge.log","","","Log filename"))
90   - pars.save(parfile)
91   -
92   - # Return
93   - return
94   -
95   - def get_parameters(self):
  76 + # Private methods
  77 + def _get_parameters(self):
96 78 """
97   - Get parameters from parfile and setup the observation.
  79 + Get parameters from parfile.
98 80 """
99   - # Get input models
100   - self.inmodels = self["inmodels"].string()
  81 + # Get input models string
  82 + inmodels = self["inmodels"].string()
101 83  
102   - # Interpet inmodels string
103 84 # Handle ascii files
104   - if "@" == self.inmodels[0]:
105   - self.files = open(self.inmodels.replace("@","")).read().splitlines()
  85 + if "@" == inmodels[0]:
  86 + self._files = open(inmodels.replace("@","")).read().splitlines()
106 87  
107 88 # Handle wild card strings
108   - elif "*" in self.inmodels:
109   - self.files = glob.glob(self.inmodels)
  89 + elif "*" in inmodels:
  90 + self._files = glob.glob(inmodels)
110 91  
111   - # Handle space-separated list
112   - elif " " in self.inmodels:
113   - self.files = self.inmodels.split(" ")
  92 + # Handle space separated list
  93 + elif " " in inmodels:
  94 + self._files = inmodels.split(" ")
114 95  
115   - # Handle comma-separated list
116   - elif "," in self.inmodels:
117   - self.files = self.inmodels.split(",")
  96 + # Handle semi-colon separated list
  97 + elif ";" in inmodels:
  98 + self._files = inmodels.split(";")
118 99  
119 100 # Throw exception if input models cannot be decoded
120 101 else:
121   - msg = "Parameter \"inmodels\" must contain either an @ASCII"+\
122   - " file, a comma-separated or whitespace-separated list"+\
123   - " of files or a wildcard string"
  102 + msg = "Parameter \"inmodels\" must contain either an @ASCII "\
  103 + "file, a semi-colon-separated or whitespace-separated "\
  104 + "list of files or a wildcard string."
124 105 raise RuntimeError(msg)
125 106  
126   - # Get output file
127   - self.outmodel = self["outmodel"].filename()
  107 + # Read ahead output filename
  108 + if self._read_ahead:
  109 + self.outmodel = self["outmodel"].filename()
  110 +
  111 + # Write input parameters into logger
  112 + if self._logTerse():
  113 + self._log_parameters()
  114 + self._log("\n")
128 115  
129 116 # Return
130 117 return
131 118  
  119 +
  120 + # Public methods
132 121 def run(self):
133 122 """
134 123 Run the script.
135 124 """
136 125 # Switch screen logging on in debug mode
137   - if self.logDebug():
138   - self.log.cout(True)
  126 + if self._logDebug():
  127 + self._log.cout(True)
139 128  
140 129 # Get parameters
141   - self.get_parameters()
  130 + self._get_parameters()
142 131  
143   - # Write input parameters into logger
144   - if self.logTerse():
145   - self.log_parameters()
146   - self.log("\n")
147   -
148   - # Dump header
149   - if self.logTerse():
150   - self.log("\n")
151   - self.log.header1("Merging Models")
152   - self.log("\n")
153   -
154   - # Initialise output model container
155   - self.models = gammalib.GModels()
  132 + # Write header
  133 + if self._logTerse():
  134 + self._log("\n")
  135 + self._log.header1("Merge models")
  136 +
  137 + # Initialise model container
  138 + self._models = gammalib.GModels()
156 139  
157 140 # Loop over model files
158   - for modelfile in self.files:
159   -
160   - # Logging
161   - self.log("Adding "+modelfile)
162   - self.log("\n")
  141 + for file in self._files:
  142 +
  143 + # Construct container from XML file
  144 + models = gammalib.GModels(file)
163 145  
164   - # Open models
165   - model_add = gammalib.GModels(modelfile)
  146 + # Log number of models to add
  147 + if self._logTerse():
  148 + nmodels = models.size()
  149 + if nmodels == 0:
  150 + self._log(gammalib.parformat("Add no model from file"))
  151 + elif nmodels == 1:
  152 + self._log(gammalib.parformat("Add 1 model from file"))
  153 + else:
  154 + self._log(gammalib.parformat("Add %d models from file" %
  155 + nmodels))
  156 + self._log(file)
  157 + self._log("\n")
166 158  
167   - # Append models to container
168   - for model in model_add:
169   - self.models.append(model)
  159 + # Extend model container by adding all models in the model file
  160 + self._models.extend(models)
170 161  
  162 + # Log total number of models
  163 + if self._logTerse():
  164 + self._log(gammalib.parformat("Models after merging"))
  165 + self._log(self._models.size())
  166 + self._log("\n")
  167 +
171 168 # Return
172 169 return
173 170  
174 171 def save(self):
175 172 """
176   - Save pointings to ds9 region file if required
  173 + Save model definition XML file
177 174 """
178   - # Logging
179   - if self.logExplicit():
180   - self.log("\n")
181   - self.log.header1("Saving model file")
182   - self.log("\n")
  175 + # Write header
  176 + if self._logTerse():
  177 + self._log("\n")
  178 + self._log.header1("Save models")
  179 +
  180 + # Get output filename in case it was not read ahead
  181 + self._outmodel = self["outmodel"].filename()
183 182  
  183 + # Log filename
  184 + if self._logTerse():
  185 + self._log(gammalib.parformat("Model definition XML file"))
  186 + self._log(self._outmodel.url())
  187 + self._log("\n")
  188 +
184 189 # Save models
185   - self.models.save(self.outmodel)
  190 + self._models.save(self._outmodel)
186 191  
187 192 # Return
188 193 return
... ... @@ -191,6 +196,9 @@ class csmodelmerge(ctools.cscript):
191 196 """
192 197 Execute the script.
193 198 """
  199 + # Set read ahead flag
  200 + self._read_ahead = True
  201 +
194 202 # Run the script
195 203 self.run()
196 204  
... ... @@ -205,14 +213,12 @@ class csmodelmerge(ctools.cscript):
205 213 # Main routine entry point #
206 214 # ======================== #
207 215 if __name__ == '__main__':
208   - """
209   - Merges model XML files
210   - """
  216 +
211 217 # Create instance of application
212 218 app = csmodelmerge(sys.argv)
213 219  
214 220 # Open logfile
215   - app.logFileOpen()
  221 + app._logFileOpen()
216 222  
217 223 # Execute application
218 224 app.execute()
... ...
cscripts/csobs2caldb.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csobs2caldb.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csobs2caldb script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# General parameters
  19 +#===================
  20 +inobs, f, a, obs.xml,,, "Observation definition XML file"
  21 +rspname, s, a, NONE,,, "Response output name (e.g. Zenith50)"
  22 +outirfs, f, h, irf_file.fits,,, "Output IRF file name"
  23 +index, i, h, 0,,, "Index of observation to be used from XML file"
  24 +
  25 +#
  26 +# Standard parameters
  27 +#====================
  28 +chatter, i, h, 2,0,4, "Chattiness of output"
  29 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  30 +debug, b, h, no,,, "Debugging mode activated"
  31 +mode, s, h, ql,,, "Mode of automatic parameters"
  32 +logfile, f, h, csobs2caldb.log,,, "Log filename"
... ...
cscripts/csobs2caldb.py
... ... @@ -30,34 +30,46 @@ import datetime
30 30 # ================= #
31 31 class csobs2caldb(ctools.cscript):
32 32 """
33   - This class implements the creation of a caldb entry for a particular
34   - observation, which might be helpful for running simulations for current
35   - IACTs. It takes an observation definition file as input and uses the
36   - first observation to create a caldb entry which can be used for
37   - simulations and sensitivity studies later on.
  33 + Creates a calibration database entry for an observation.
  34 +
  35 + The creation of a calibration database entry is useful for performing
  36 + simulations for current Imaging Air Cherenkov Telescopes (IACTs).
  37 + The class takes an observation definition XML file as input and uses
  38 + one observation to create a calibration database entry. By default
  39 + the first observation will be used, but the user can specify the
  40 + index of any observation using the hidden "index" parameter.
38 41 """
  42 +
  43 + # Constructors and destructors
39 44 def __init__(self, *argv):
40 45 """
41 46 Constructor.
42 47 """
43   - # Set name
44   - self.name = "csobs2caldb"
45   - self.version = "1.1.0"
46   -
47   - # Make sure that parfile exists
48   - file = self.parfile()
49   -
  48 + # Set name and version
  49 + self._name = "csobs2caldb"
  50 + self._version = "1.1.0"
  51 +
  52 + # Initialise members
  53 + self._obs = [] # Observation container
  54 + self._observation = None
  55 + self._mission = "CTA"
  56 + self._instrument = ""
  57 + self._rspname = ""
  58 + self._outfile = ""
  59 + self._base_dir = ""
  60 + self._cal_dir = ""
  61 +
50 62 # Initialise application
51 63 if len(argv) == 0:
52   - ctools.cscript.__init__(self, self.name, self.version)
  64 + ctools.cscript.__init__(self, self._name, self._version)
53 65 elif len(argv) == 1:
54   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  66 + ctools.cscript.__init__(self, self._name, self._version, *argv)
55 67 else:
56 68 raise TypeError("Invalid number of arguments given.")
57 69  
58 70 # Set logger properties
59   - self.log_header()
60   - self.log.date(True)
  71 + self._log_header()
  72 + self._log.date(True)
61 73  
62 74 # Return
63 75 return
... ... @@ -69,87 +81,76 @@ class csobs2caldb(ctools.cscript):
69 81 # Return
70 82 return
71 83  
72   - def parfile(self):
73   - """
74   - Check if parfile exists. If parfile does not exist then create a
75   - default parfile. This kluge avoids shipping the cscript with a
76   - parfile.
77   - """
78   -
79   - # Set parfile name
80   - parfile = self.name+".par"
81   -
82   - try:
83   - pars = gammalib.GApplicationPars(parfile)
84   - except:
85   - # Signal that parfile was not found
86   - print("Parfile \""+parfile+"\" not found. Create default parfile.")
87   -
88   - # Create default parfile
89   - pars = gammalib.GApplicationPars()
90   - pars.append(gammalib.GApplicationPar("inobs","f","a","obs.xml","","","Input observation definition file"))
91   - pars.append(gammalib.GApplicationPar("rspname","s","a","NONE","","","Response output name (e.g. Zenith50)"))
92   - pars.append(gammalib.GApplicationPar("outirfs","f","h","irf_file.fits","","","Output IRF file name"))
93   - pars.append(gammalib.GApplicationPar("index","i","h","0","","","Index of observation to be used from XML file"))
94   - pars.append_standard()
95   - pars.append(gammalib.GApplicationPar("logfile","f","h","csobs2caldb.log","","","Log filename"))
96   - pars.save(parfile)
97   -
98   - # Return
99   - return
100 84  
101   - def get_parameters(self):
  85 + # Private methods
  86 + def _get_parameters(self):
102 87 """
103 88 Get parameters from parfile and setup the observation.
104 89 """
105   - # Get parameters
106   - self.inobs = self["inobs"].filename()
107   - self.obs = gammalib.GObservations(self.inobs)
  90 + # Load observation container
  91 + self._obs = gammalib.GObservations(self["inobs"].filename())
108 92  
109 93 # Get index of observation to be used (0=default)
110 94 inx = self["index"].integer()
111 95  
112 96 # Get observation
113   - self.observation = self.obs[inx]
114   -
115   - # Set instrument mission to cta
116   - # This is to be compliant with standard gammalib caldb
117   - self.m_mission = "CTA"
  97 + self._observation = self._obs[inx]
118 98  
119 99 # Get instrument name
120   - self.m_instrument = self.observation.instrument()
  100 + self._instrument = self._observation.instrument()
121 101  
122 102 # Get config and file name
123   - self.m_rspname = self["rspname"].string()
124   - self.m_outfile = self["outirfs"].filename()
  103 + self._rspname = self["rspname"].string()
  104 + self._outfile = self["outirfs"].filename()
125 105  
126 106 # Make sure we have a cta observation
127   - if not self.observation.classname() == "GCTAObservation":
128   - raise gammalib.Exception.runtime_error("Input observation not of type \"GCTAObservation\"")
  107 + if not self._observation.classname() == "GCTAObservation":
  108 + raise RuntimeError("Input observation not of type "+
  109 + "\"GCTAObservation\".")
129 110  
130 111 # Make sure we have an irf response
131   - if not self.observation.response().classname() == "GCTAResponseIrf":
132   - raise gammalib.Exception.runtime_error("Response of input observation not of type \"GCTAResponseIrf\"")
  112 + if not self._observation.response().classname() == "GCTAResponseIrf":
  113 + raise RuntimeError("Response of input observation not of "+
  114 + "type \"GCTAResponseIrf\".")
133 115  
134 116 # Get other parameters
135   - self.m_clobber = self["clobber"].boolean()
  117 + self._clobber = self["clobber"].boolean()
136 118  
137 119 # Return
138 120 return
139 121  
140   - def make_irf_file(self):
141   -
142   - # retrieve response component
143   - rsp = self.observation.response()
144   -
145   - # Initialise FITS file
146   - fits = gammalib.GFits()
  122 + def _make_irf_file(self):
  123 + """
  124 + Creates an IRF FITS file.
  125 + """
  126 + # Write header into logger
  127 + if self._logTerse():
  128 + self._log("\n")
  129 + self._log.header2("Creating IRF file")
  130 +
  131 + # Get response for the observation
  132 + rsp = self._observation.response()
147 133  
148   - # Create GFilename instances
149   - fname_aeff = gammalib.GFilename(rsp.aeff().filename())
150   - fname_psf = gammalib.GFilename(rsp.psf().filename())
151   - fname_edisp = gammalib.GFilename(rsp.edisp().filename())
152   - fname_bkg = gammalib.GFilename(rsp.background().filename())
  134 + # Extract response file names
  135 + fname_aeff = rsp.aeff().filename()
  136 + fname_psf = rsp.psf().filename()
  137 + fname_edisp = rsp.edisp().filename()
  138 + fname_bkg = rsp.background().filename()
  139 +
  140 + # Log filename instances
  141 + if self._logTerse():
  142 + self._log(gammalib.parformat("Effective area"))
  143 + self._log(repr(fname_aeff))
  144 + self._log("\n")
  145 + self._log(gammalib.parformat("Point spread function"))
  146 + self._log(fname_psf.url())
  147 + self._log("\n")
  148 + self._log(gammalib.parformat("Energy dispersion"))
  149 + self._log(fname_edisp.url())
  150 + self._log("\n")
  151 + self._log(gammalib.parformat("Background rate"))
  152 + self._log(fname_bkg.url())
  153 + self._log("\n")
153 154  
154 155 # Open FITS files of response components
155 156 fits_aeff = gammalib.GFits(fname_aeff)
... ... @@ -157,7 +158,10 @@ class csobs2caldb(ctools.cscript):
157 158 fits_edisp = gammalib.GFits(fname_edisp)
158 159 fits_bkg = gammalib.GFits(fname_bkg)
159 160  
160   - # Bundle IRFs into one file
  161 + # Create empty FITS file
  162 + fits = gammalib.GFits()
  163 +
  164 + # Append IRF component to FITS file
161 165 fits.append(fits_aeff[fname_aeff.extname("EFFECTIVE AREA")])
162 166 fits.append(fits_psf[fname_psf.extname("POINT SPREAD FUNCTION")])
163 167 fits.append(fits_edisp[fname_edisp.extname("ENERGY DISPERSION")])
... ... @@ -166,30 +170,37 @@ class csobs2caldb(ctools.cscript):
166 170 # Return fits file
167 171 return fits
168 172  
169   - def make_caldb_index(self):
170   -
  173 + def _make_caldb_index(self):
  174 + """
  175 + Creates an IRF index FITS file.
  176 + """
  177 + # Write header into logger
  178 + if self._logTerse():
  179 + self._log("\n")
  180 + self._log.header2("Creating database index file")
  181 +
171 182 # Open calibration database index
172   - indx_file = self.base_dir+"/caldb.indx"
  183 + indx_file = self._base_dir+"/caldb.indx"
173 184  
174   - # Check if indx file exists (create if not existent)
175   - if os.path.isfile(indx_file):
176   - cif = gammalib.GFits(indx_file)
177   - else:
178   - cif = gammalib.GFits(indx_file, True)
  185 + # Open index file (or create one if it does not exist)
  186 + cif = gammalib.GFits(indx_file, True)
179 187  
180 188 # Retrieve "CIF" table
181 189 if cif.contains("CIF"):
182 190 table = cif.table("CIF")
  191 +
  192 + # ... or create binary table if no "CIF" table exists yet
183 193 else:
184   - # Create binary table if not available
  194 +
  195 + # Create empty binary table
185 196 bintable = gammalib.GFitsBinTable()
186 197 bintable.extname("CIF")
187 198 bintable.card("CIFVERSN", "1992a", "Version of CIF format")
188   - cif.append(bintable)
189   -
190   - table = cif.table("CIF")
  199 +
  200 + # Append table to FITS file and recover a reference to the
  201 + # appended table
  202 + table = cif.append(bintable)
191 203  
192   - # Copied from cta_root2caldb.py
193 204 # Attach columns. Reference: CAL/GEN/92-008
194 205 table.append(gammalib.GFitsTableStringCol("TELESCOP", 0, 10))
195 206 table.append(gammalib.GFitsTableStringCol("INSTRUME", 0, 10))
... ... @@ -215,7 +226,7 @@ class csobs2caldb(ctools.cscript):
215 226 row_index = -1
216 227 for caldir in table["CAL_DIR"]:
217 228 row_index += 1
218   - if caldir == self.cal_dir:
  229 + if caldir == self._cal_dir:
219 230 has_config = True
220 231 break
221 232  
... ... @@ -238,8 +249,8 @@ class csobs2caldb(ctools.cscript):
238 249 today, time = now.split()
239 250  
240 251 # Set element
241   - table["TELESCOP"][row] = self.m_mission
242   - table["INSTRUME"][row] = self.m_instrument
  252 + table["TELESCOP"][row] = self._mission
  253 + table["INSTRUME"][row] = self._instrument
243 254 table["DETNAM"][row] = "NONE"
244 255 table["FILTER"][row] = "NONE"
245 256 table["CAL_DEV"][row] = "ONLINE"
... ... @@ -249,115 +260,133 @@ class csobs2caldb(ctools.cscript):
249 260 table["CAL_VST"][row] = time.split(".")[0]
250 261 table["REF_TIME"][row] = 51544.0
251 262 table["CAL_QUAL"][row] = 0
252   - table["CAL_CBD"][row] = "NAME("+self.m_rspname+")"
  263 + table["CAL_CBD"][row] = "NAME("+self._rspname+")"
253 264 table["CAL_DATE"][row] = today.replace("-","/")[2:]
254   - table["CAL_DIR"][row] = self.cal_dir
255   - table["CAL_FILE"][row] = self.m_outfile
  265 + table["CAL_DIR"][row] = self._cal_dir
  266 + table["CAL_FILE"][row] = self._outfile
256 267  
257 268 # Add effective area information
258 269 row = row_index-4
259   - table["CAL_CNAM"][row] = "EFF_AREA"
260   - table["CAL_DESC"][row] = self.m_instrument+" effective area"
  270 + table["CAL_CNAM"][row] = "EFF_AREA"
  271 + table["CAL_DESC"][row] = self._instrument+" effective area"
261 272  
262 273 # Add point spread function information
263 274 row = row_index-3
264   - table["CAL_CNAM"][row] = "RPSF"
265   - table["CAL_DESC"][row] = self.m_instrument+" point spread function"
  275 + table["CAL_CNAM"][row] = "RPSF"
  276 + table["CAL_DESC"][row] = self._instrument+" point spread function"
266 277  
267 278 # Add energy dispersion information
268 279 row = row_index-2
269   - table["CAL_CNAM"][row] = "EDISP"
270   - table["CAL_DESC"][row] = self.m_instrument+" energy dispersion"
  280 + table["CAL_CNAM"][row] = "EDISP"
  281 + table["CAL_DESC"][row] = self._instrument+" energy dispersion"
271 282  
272 283 # Add background information
273 284 row = row_index-1
274   - table["CAL_CNAM"][row] = "BGD"
275   - table["CAL_DESC"][row] = self.m_instrument+" background"
  285 + table["CAL_CNAM"][row] = "BGD"
  286 + table["CAL_DESC"][row] = self._instrument+" background"
276 287  
277 288 # Return CIF FITS file
278 289 return cif
279 290  
280   - def make_dirs(self):
  291 + def _make_dirs(self):
  292 + """
  293 + Make CALDB directories.
  294 + """
  295 + # Write header into logger
  296 + if self._logTerse():
  297 + self._log("\n")
  298 + self._log.header2("Creating directory structure")
281 299  
282   - # Create gammalib Caldb
  300 + # Create calibration database
283 301 caldb = gammalib.GCaldb()
284 302  
285 303 # Set calibration directory
286   - self.cal_dir = "data"
287   - self.cal_dir += "/"+self.m_mission.lower()
288   - self.cal_dir += "/"+self.m_instrument.lower()
289   - self.cal_dir += "/bcf/"+self.m_rspname
  304 + self._cal_dir = "data"
  305 + self._cal_dir += "/"+self._mission.lower()
  306 + self._cal_dir += "/"+self._instrument.lower()
  307 + self._cal_dir += "/bcf/"+self._rspname
290 308  
291 309 # Set absolute path
292   - self.base_dir = caldb.rootdir() +"/data"
293   - self.base_dir += "/"+self.m_mission.lower()
294   - self.base_dir += "/"+self.m_instrument.lower()
  310 + self._base_dir = caldb.rootdir() +"/data"
  311 + self._base_dir += "/"+self._mission.lower()
  312 + self._base_dir += "/"+self._instrument.lower()
295 313  
296 314 # Set directory for irf file
297   - self.rsp_dir = caldb.rootdir() + "/" + self.cal_dir
  315 + self._rsp_dir = caldb.rootdir() + "/" + self._cal_dir
298 316  
299 317 # Create directories and log information
300   - if not os.path.isdir(self.rsp_dir):
301   - if self.logExplicit():
302   - self.log(gammalib.parformat("Directory"))
303   - self.log(self.rsp_dir)
304   - self.log("\n")
305   - os.makedirs(self.rsp_dir)
  318 + if not os.path.isdir(self._rsp_dir):
  319 + if self._logExplicit():
  320 + self._log(gammalib.parformat("Directory"))
  321 + self._log(self._rsp_dir)
  322 + self._log("\n")
  323 + os.makedirs(self._rsp_dir)
306 324 else:
307   - if self.logExplicit():
308   - self.log(gammalib.parformat("Directory (existing)"))
309   - self.log(self.rsp_dir)
310   - self.log("\n")
  325 + if self._logExplicit():
  326 + self._log(gammalib.parformat("Directory (existing)"))
  327 + self._log(self._rsp_dir)
  328 + self._log("\n")
311 329  
312 330 # Return
313 331 return
314 332  
  333 +
  334 + # Public methods
315 335 def run(self):
316 336 """
317 337 Run the script.
318 338 """
319 339 # Switch screen logging on in debug mode
320   - if self.logDebug():
321   - self.log.cout(True)
  340 + if self._logDebug():
  341 + self._log.cout(True)
322 342  
323 343 # Get parameters
324   - self.get_parameters()
  344 + self._get_parameters()
325 345  
326   - # Write input parameters into logger
327   - if self.logTerse():
328   - self.log_parameters()
329   - self.log("\n\n")
330   - self.log.header1("Creating directory structure")
  346 + # Write input parameters and header into logger
  347 + if self._logTerse():
  348 + self._log_parameters()
  349 + self._log("\n\n")
  350 + self._log.header1("Creating CALDB entry")
331 351  
332 352 # Create directory structure
333   - self.make_dirs()
334   -
335   - # Logging
336   - if self.logTerse():
337   - self.log("\n")
338   - self.log.header1("Updating calibration database")
  353 + self._make_dirs()
339 354  
340 355 # Create/update calibration database
341   - self.caldb_inx = self.make_caldb_index()
  356 + self._caldb_inx = self._make_caldb_index()
342 357  
343   - # Logging
344   - if self.logTerse():
345   - self.log("\n")
346   - self.log.header1("Creating response file")
347   -
348   - # Create IRF file
349   - self.irf_fits = self.make_irf_file()
  358 + # Create response file
  359 + self._irf_fits = self._make_irf_file()
350 360  
351 361 # Return
352 362 return
353 363  
354 364 def save(self):
  365 + """
  366 + Save calibration database FITS file.
  367 + """
  368 + # Write header into logger
  369 + if self._logTerse():
  370 + self._log("\n")
  371 + self._log.header1("Save calibration database")
  372 +
  373 + # Set response filename
  374 + filename = self._rsp_dir + "/" + self._outfile
  375 +
  376 + # Write filenames into logger
  377 + if self._logTerse():
  378 + self._log(gammalib.parformat("CALDB index file"))
  379 + self._log(self._caldb_inx.filename().url())
  380 + self._log("\n")
  381 + self._log(gammalib.parformat("Response file"))
  382 + self._log(filename)
  383 + self._log("\n")
355 384  
356 385 # Save caldb index file
357   - self.caldb_inx.save(self.m_clobber)
  386 + self._caldb_inx.save(self._clobber)
358 387  
359 388 # Save response file
360   - self.irf_fits.saveto(self.rsp_dir + "/" + self.m_outfile, self.m_clobber)
  389 + self._irf_fits.saveto(filename, self._clobber)
361 390  
362 391 # Return
363 392 return
... ... @@ -380,14 +409,12 @@ class csobs2caldb(ctools.cscript):
380 409 # Main routine entry point #
381 410 # ======================== #
382 411 if __name__ == '__main__':
383   - """
384   - Generates caldb entry.
385   - """
  412 +
386 413 # Create instance of application
387 414 app = csobs2caldb(sys.argv)
388 415  
389 416 # Open logfile
390   - app.logFileOpen()
  417 + app._logFileOpen()
391 418  
392 419 # Execute application
393 420 app.execute()
... ...
cscripts/csobsdef.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csobsdef.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csobsdef script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# General parameters
  19 +#===================
  20 +inpnt, f, a, NONE,,, "Input pointing definition ASCII file"
  21 +outobs, f, a, obs.xml,,, "Output observation definition XML file"
  22 +
  23 +#
  24 +# Common pointing parameter values
  25 +#=================================
  26 +duration, r, a, 1800.0,,, "Pointing duration (seconds)"
  27 +caldb, s, h, ,,, "Calibration database"
  28 +irf, s, h, ,,, "Instrument response function"
  29 +emin, r, h, UNDEF,,, "Lower energy limit (TeV)"
  30 +emax, r, h, UNDEF,,, "Upper energy limit (TeV)"
  31 +rad, r, h, UNDEF,,, "Radius of ROI (degrees)"
  32 +deadc, r, h, 0.95,,, "Deadtime correction factor"
  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, csobsdef.log,,, "Log filename"
... ...
cscripts/csobsdef.py
... ... @@ -28,11 +28,12 @@ import sys
28 28 # ============== #
29 29 class csobsdef(ctools.cscript):
30 30 """
31   - The csobsdef class generates an observation definition file from a
32   - pointing list. The pointing list is a comma-separated value ASCII
33   - file with header keywords in the first row followed by a list of
34   - pointings (one pointing per row). The following header keywords
35   - are supported (case sensitive, column order irrelevant):
  31 + Creates an observation definition file from a pointing list.
  32 +
  33 + The pointing list is a comma-separated value ASCII file with header
  34 + keywords in the first row followed by a list of pointings (one
  35 + pointing per row). The following header keywords are supported (case
  36 + sensitive, column order irrelevant):
36 37  
37 38 name - Observation name string
38 39 id - Unique observation identifier string
... ... @@ -53,53 +54,54 @@ class csobsdef(ctools.cscript):
53 54 csobsdef as user parameters. The only exception is the "duration"
54 55 keyword that will automatically be queried.
55 56  
56   - Here some usage examples:
  57 + Examples:
57 58  
58   - csobsdef
59   - Creates minimal observation definition file.
  59 + ./csobsdef
  60 + Creates minimal observation definition file.
60 61  
61   - csobsdef emin=0.1 emax=100.0
62   - Creates observation definition file with an energy range 100 GeV - 100 TeV.
  62 + ./csobsdef emin=0.1 emax=100.0
  63 + Creates observation definition file with an energy range
  64 + 100 GeV - 100 TeV.
63 65  
64   - csobsdef rad=5
65   - Creates observation definition file with a ROI radius of 5 deg.
  66 + ./csobsdef rad=5
  67 + Creates observation definition file with a ROI radius of
  68 + 5 degrees.
66 69  
67   - csobsdef caldb=dummy irf=cta_dummy_irf
68   - Creates observation definition file using the "cta_dummy_irf" IRF in the
69   - "dummy" calibration database.
  70 + ./csobsdef caldb=dummy irf=cta_dummy_irf
  71 + Creates observation definition file using the "cta_dummy_irf"
  72 + IRF in the "dummy" calibration database.
70 73 """
  74 +
  75 + # Constructors and destructors
71 76 def __init__(self, *argv):
72 77 """
73 78 Constructor.
74 79 """
75 80 # Set name and version
76   - self.name = "csobsdef"
77   - self.version = "1.0.0"
  81 + self._name = "csobsdef"
  82 + self._version = "1.1.0"
78 83  
79 84 # Initialise class members
80   - self.obs = gammalib.GObservations()
81   - self.inpnt = ""
82   - self.tmin = 0.0
83   - self.outfile = ""
84   - self.chatter = 2
85   - self.clobber = True
86   - self.debug = False
87   - self.read_ahead = False
88   -
89   - # Make sure that parfile exists
90   - file = self.parfile()
  85 + self._obs = gammalib.GObservations()
  86 + self._inpnt = ""
  87 + self._tmin = 0.0
  88 + self._outobs = "NONE"
  89 + self._chatter = 2
  90 + self._clobber = True
  91 + self._debug = False
  92 + self._read_ahead = False
91 93  
92 94 # Initialise application
93 95 if len(argv) == 0:
94   - ctools.cscript.__init__(self, self.name, self.version)
  96 + ctools.cscript.__init__(self, self._name, self._version)
95 97 elif len(argv) ==1:
96   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  98 + ctools.cscript.__init__(self, self._name, self._version, *argv)
97 99 else:
98 100 raise TypeError("Invalid number of arguments given.")
99 101  
100 102 # Set logger properties
101   - self.log_header()
102   - self.log.date(True)
  103 + self._log_header()
  104 + self._log.date(True)
103 105  
104 106 # Return
105 107 return
... ... @@ -111,92 +113,78 @@ class csobsdef(ctools.cscript):
111 113 # Return
112 114 return
113 115  
114   - def parfile(self):
115   - """
116   - Check if parfile exists. If parfile does not exist then create a
117   - default parfile. This kluge avoids shipping the cscript with a parfile.
118   - """
119   - # Set parfile name
120   - parfile = self.name+".par"
121   -
122   - # Test parfile existence, and create parfile if it does not exist
123   - try:
124   - pars = gammalib.GApplicationPars(parfile)
125   - except:
126   - # Signal that parfile was not found
127   - print("Parfile \""+parfile+"\" not found. Create default parfile.")
128   -
129   - # Create default parfile
130   - pars = gammalib.GApplicationPars()
131   - pars.append(gammalib.GApplicationPar("inpnt","f","a","NONE","","","Input pointing definition file"))
132   - pars.append(gammalib.GApplicationPar("outobs","f","a","obs.xml","","","Output observation definition XML file"))
133   - pars.append(gammalib.GApplicationPar("duration","r","a","1800.0","","","Pointing duration (seconds)"))
134   - pars.append(gammalib.GApplicationPar("caldb","s","h","","","","Calibration database"))
135   - pars.append(gammalib.GApplicationPar("irf","s","h","","","","Instrument response function"))
136   - pars.append(gammalib.GApplicationPar("emin","r","h","UNDEF","","","Lower energy limit (TeV)"))
137   - pars.append(gammalib.GApplicationPar("emax","r","h","UNDEF","","","Upper energy limit (TeV)"))
138   - pars.append(gammalib.GApplicationPar("rad","r","h","UNDEF","","","Radius of ROI (degrees)"))
139   - pars.append(gammalib.GApplicationPar("deadc","r","h","0.95","","","Deadtime correction factor"))
140   - pars.append_standard()
141   - pars.append(gammalib.GApplicationPar("logfile","f","h","csobsdef.log","","","Log filename"))
142   - pars.save(parfile)
143   -
144   - # Return
145   - return
146 116  
147   - def get_parameters(self):
  117 + # Private methods
  118 + def _get_parameters(self):
148 119 """
149 120 Get parameters from parfile.
150 121 """
151 122 # Read parameters
152   - self.inpnt = self["inpnt"].filename()
  123 + self._inpnt = self["inpnt"].filename()
153 124  
154 125 # Read ahead parameters
155   - if self.read_ahead:
156   - self.outfile = self["outobs"].filename()
  126 + if self._read_ahead:
  127 + self._outobs = self["outobs"].filename()
157 128  
158 129 # Set some fixed parameters
159   - self.chatter = self["chatter"].integer()
160   - self.clobber = self["clobber"].boolean()
161   - self.debug = self["debug"].boolean()
  130 + self._chatter = self["chatter"].integer()
  131 + self._clobber = self["clobber"].boolean()
  132 + self._debug = self["debug"].boolean()
  133 +
  134 + # Write input parameters into logger
  135 + if self._logTerse():
  136 + self._log_parameters()
  137 + self._log("\n")
162 138  
163 139 # Return
164 140 return
165 141  
166   - def execute(self):
  142 + def _set_response(self, obs, caldb, irf):
167 143 """
168   - Execute the script.
  144 + Set response for an observation.
  145 +
  146 + The method creates an XML element so that that the response XML
  147 + writer will write the database and response name into the
  148 + observation definition file.
169 149 """
170   - # Set read ahead flag
171   - self.read_ahead = True
  150 + # Create XML element
  151 + xml = gammalib.GXmlElement()
172 152  
173   - # Run the script
174   - self.run()
  153 + # Append parameter
  154 + parameter = "parameter name=\"Calibration\" database=\""+\
  155 + caldb+"\" response=\""+irf+"\""
  156 + xml.append(gammalib.GXmlElement(parameter))
175 157  
176   - # Save observation definition file
177   - self.obs.save(self.outfile)
  158 + # Create CTA response
  159 + response = gammalib.GCTAResponseIrf()
  160 + response.read(xml)
178 161  
179   - # Return
180   - return
  162 + # Attach response to observation
  163 + obs.response(response)
  164 +
  165 + # Return observation
  166 + return obs
181 167  
  168 +
  169 + # Public methods
182 170 def run(self):
183 171 """
184 172 Run the script.
185 173 """
186 174 # Switch screen logging on in debug mode
187   - if self.logDebug():
188   - self.log.cout(True)
  175 + if self._logDebug():
  176 + self._log.cout(True)
189 177  
190 178 # Get parameters
191   - self.get_parameters()
  179 + self._get_parameters()
192 180  
193   - # Write input parameters into logger
194   - if self.logTerse():
195   - self.log_parameters()
196   - self.log("\n")
  181 + # Write header into logger
  182 + if self._logTerse():
  183 + self._log("\n")
  184 + self._log.header1("Creating observation definition XML file")
197 185  
198 186 # Load pointing definition file
199   - pntdef = gammalib.GCsv(self.inpnt, ",")
  187 + pntdef = gammalib.GCsv(self._inpnt, ",")
200 188 ncols = pntdef.ncols()
201 189 npnt = pntdef.nrows()-1
202 190  
... ... @@ -205,8 +193,8 @@ class csobsdef(ctools.cscript):
205 193 raise RuntimeError("No header found in pointing definition file.")
206 194  
207 195 # Clear observation container
208   - self.obs.clear()
209   - self.id = 1
  196 + self._obs.clear()
  197 + self._id = 1
210 198  
211 199 # Extract header from pointing definition file
212 200 header = []
... ... @@ -233,8 +221,8 @@ class csobsdef(ctools.cscript):
233 221 if "id" in header:
234 222 id = pntdef[row, header.index("id")]
235 223 else:
236   - id = "%6.6d" % self.id
237   - self.id += 1
  224 + id = "%6.6d" % self._id
  225 + self._id += 1
238 226 obs.id(id)
239 227  
240 228 # Set pointing
... ... @@ -249,8 +237,8 @@ class csobsdef(ctools.cscript):
249 237 pntdir = gammalib.GSkyDir()
250 238 pntdir.lb_deg(lon,lat)
251 239 else:
252   - raise RuntimeError("No (ra,dec) or (lon,lat) columns found in pointing"
253   - " definition file.")
  240 + raise RuntimeError("No (ra,dec) or (lon,lat) columns "
  241 + "found in pointing definition file.")
254 242 obs.pointing(gammalib.GCTAPointing(pntdir))
255 243  
256 244 # Set response function
... ... @@ -263,7 +251,7 @@ class csobsdef(ctools.cscript):
263 251 else:
264 252 irf = self["irf"].string()
265 253 if caldb != "" and irf != "":
266   - obs = self.__set_response(obs, caldb, irf)
  254 + obs = self._set_response(obs, caldb, irf)
267 255  
268 256 # Set deadtime correction factor
269 257 if "deadc" in header:
... ... @@ -277,12 +265,12 @@ class csobsdef(ctools.cscript):
277 265 duration = float(pntdef[row, header.index("duration")])
278 266 else:
279 267 duration = self["duration"].real()
280   - tmin = self.tmin
281   - tmax = self.tmin + duration
282   - gti = gammalib.GGti(self._time_reference())
283   - tstart = gammalib.GTime(tmin, self._time_reference())
284   - tstop = gammalib.GTime(tmax, self._time_reference())
285   - self.tmin = tmax
  268 + tmin = self._tmin
  269 + tmax = self._tmin + duration
  270 + gti = gammalib.GGti(self._time_reference())
  271 + tstart = gammalib.GTime(tmin, self._time_reference())
  272 + tstop = gammalib.GTime(tmax, self._time_reference())
  273 + self._tmin = tmax
286 274 gti.append(tstart, tstop)
287 275 obs.ontime(gti.ontime())
288 276 obs.livetime(gti.ontime()*deadc)
... ... @@ -334,49 +322,75 @@ class csobsdef(ctools.cscript):
334 322 # Attach event list to CTA observation
335 323 obs.events(list)
336 324  
  325 + # Write observation into logger
  326 + if self._logExplicit():
  327 + self._log(str(obs))
  328 + self._log("\n")
  329 + elif self._logTerse():
  330 + self._log(gammalib.parformat(obs.instrument()+" observation"))
  331 + self._log("Name=\""+obs.name()+"\" ")
  332 + self._log("ID=\""+obs.id()+"\"\n")
  333 +
337 334 # Append observation
338   - self.obs.append(obs)
  335 + self._obs.append(obs)
339 336  
340 337 # Return
341 338 return
342 339  
343   - def __set_response(self, obs, caldb, irf):
  340 + def save(self):
344 341 """
345   - Set response for an observation. We create an XML element for that
346   - so that the response XML writer will write the database and response
347   - name into the observation definition file.
  342 + Save observation definition XML file.
348 343 """
349   - # Create XML element
350   - xml = gammalib.GXmlElement()
  344 + # Write header and filename into logger
  345 + if self._logTerse():
  346 + self._log("\n")
  347 + self._log.header1("Save observation definition XML file")
  348 +
  349 + # Get output filename in case it was not read ahead
  350 + self._outobs = self["outobs"].filename()
  351 +
  352 + # Check if observation definition XML file is valid
  353 + if self._outobs != "NONE":
  354 +
  355 + # Log filename
  356 + if self._logTerse():
  357 + self._log(gammalib.parformat("Observation XML file"))
  358 + self._log(self._outobs.url())
  359 + self._log("\n")
  360 +
  361 + # Save observation definition XML file
  362 + self._obs.save(self._outobs)
  363 +
  364 + # Return
  365 + return
351 366  
352   - # Append parameter
353   - parameter = "parameter name=\"Calibration\" database=\""+\
354   - caldb+"\" response=\""+irf+"\""
355   - xml.append(gammalib.GXmlElement(parameter))
  367 + def execute(self):
  368 + """
  369 + Execute the script.
  370 + """
  371 + # Set read ahead flag
  372 + self._read_ahead = True
356 373  
357   - # Create CTA response
358   - response = gammalib.GCTAResponseIrf()
359   - response.read(xml)
  374 + # Run the script
  375 + self.run()
360 376  
361   - # Attach response to observation
362   - obs.response(response)
  377 + # Save observation definition file
  378 + self.save()
363 379  
364   - # Return observation
365   - return obs
  380 + # Return
  381 + return
366 382  
367 383  
368 384 # ======================== #
369 385 # Main routine entry point #
370 386 # ======================== #
371 387 if __name__ == '__main__':
372   - """
373   - Generates observation definition file.
374   - """
  388 +
375 389 # Create instance of application
376 390 app = csobsdef(sys.argv)
377 391  
378 392 # Open logfile
379   - app.logFileOpen()
  393 + app._logFileOpen()
380 394  
381 395 # Execute application
382 396 app.execute()
... ...
cscripts/csobsinfo.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csobsinfo.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csobsinfo script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inobs, f, a, obs.xml,,, "Input event list, counts cube, or observation definition XML file"
  21 +ds9file, f, h, NONE,,, "Output DS9 region file containing pointings"
  22 +
  23 +#
  24 +# Script parameters
  25 +#==================
  26 +offset, b, h, no,,, "Compute offset from target to pointing positions?"
  27 +ra, r, a, 0.0,,, "Target Right Ascension (deg)"
  28 +dec, r, a, 0.0,,, "Target Declination (deg)")
  29 +
  30 +#
  31 +# Standard parameters
  32 +#====================
  33 +chatter, i, h, 2,0,4, "Chattiness of output"
  34 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  35 +debug, b, h, no,,, "Debugging mode activated"
  36 +mode, s, h, ql,,, "Mode of automatic parameters"
  37 +logfile, f, h, csobsinfo.log,,, "Log filename"
... ...
cscripts/csobsinfo.py
... ... @@ -28,40 +28,48 @@ import sys
28 28 # =============== #
29 29 class csobsinfo(ctools.cscript):
30 30 """
31   - This class dumps information about an observation container into a
32   - logfile or on screen. This might be helpful for quick access to an
33   - observation container showing, e.g., total lifetime, energy range etc.
  31 + Shows the content of an observation container.
34 32 """
  33 +
  34 + # Constructors and destructors
35 35 def __init__(self, *argv):
36 36 """
37 37 Constructor.
38 38 """
39 39 # Set name
40   - self.name = "csobsinfo"
41   - self.version = "1.1.0"
  40 + self._name = "csobsinfo"
  41 + self._version = "1.1.0"
42 42  
43   - # Initialise some members
  43 + # Initialise class members
  44 + self._obj_dir = None
  45 + self._compute_offset = False
  46 + self._ds9file = ""
  47 + self._offsets = []
  48 + self._zeniths = []
  49 + self._azimuths = []
  50 + self._pnt_ra = []
  51 + self._pnt_dec = []
  52 + self._ebounds = gammalib.GEbounds()
  53 + self._gti = gammalib.GGti()
  54 + self._read_ahead = False
44 55 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
45   - self.obs = argv[0]
46   - argv = argv[1:]
  56 + self._obs = argv[0]
  57 + argv = argv[1:]
47 58 else:
48   - self.obs = gammalib.GObservations()
49   - self.obs.clear()
50   -
51   - # Make sure that parfile exists
52   - file = self.parfile()
  59 + self._obs = gammalib.GObservations()
  60 + self._obs.clear()
53 61  
54 62 # Initialise application
55 63 if len(argv) == 0:
56   - ctools.cscript.__init__(self, self.name, self.version)
  64 + ctools.cscript.__init__(self, self._name, self._version)
57 65 elif len(argv) == 1:
58   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  66 + ctools.cscript.__init__(self, self._name, self._version, *argv)
59 67 else:
60 68 raise TypeError("Invalid number of arguments given.")
61 69  
62 70 # Set logger properties
63   - self.log_header()
64   - self.log.date(True)
  71 + self._log_header()
  72 + self._log.date(True)
65 73  
66 74 # Return
67 75 return
... ... @@ -73,89 +81,64 @@ class csobsinfo(ctools.cscript):
73 81 # Return
74 82 return
75 83  
76   - def parfile(self):
77   - """
78   - Check if parfile exists. If parfile does not exist then create a
79   - default parfile. This kluge avoids shipping the cscript with a
80   - parfile.
81   - """
82   -
83   - # Set parfile name
84   - parfile = self.name+".par"
85   -
86   - try:
87   - pars = gammalib.GApplicationPars(parfile)
88   - except:
89   - # Signal that parfile was not found
90   - print("Parfile \""+parfile+"\" not found. Create default parfile.")
91   -
92   - # Create default parfile
93   - pars = gammalib.GApplicationPars()
94   - pars.append(gammalib.GApplicationPar("inobs","f","a","obs.xml","","","Event list, counts cube, or observation definition file"))
95   - pars.append(gammalib.GApplicationPar("offset","b","h","no","","","Compute offset from target to pointing positions"))
96   - pars.append(gammalib.GApplicationPar("ra","r","a","0.0","","","Target Right Ascension"))
97   - pars.append(gammalib.GApplicationPar("dec","r","a","0.0","","","Target Declination"))
98   - pars.append(gammalib.GApplicationPar("ds9file","f","h","NONE","","","DS9 region file containing pointings"))
99   - pars.append_standard()
100   - pars.append(gammalib.GApplicationPar("logfile","f","h","csobsinfo.log","","","Log filename"))
101   - pars.save(parfile)
102 84  
103   - # Return
104   - return
105   -
106   - def get_parameters(self):
  85 + # Private methods
  86 + def _get_parameters(self):
107 87 """
108 88 Get parameters from parfile and setup the observation.
109 89 """
110 90 # Get parameters
111   - if self.obs.size() == 0:
  91 + if self._obs.size() == 0:
112 92 self._require_inobs("csobsinfo::get_parameters")
113   - self.obs = self._get_observations(False)
  93 + self._obs = self._get_observations(False)
114 94  
115 95 # Initialise object position
116   - self.obj_dir = gammalib.GSkyDir()
  96 + self._obj_dir = gammalib.GSkyDir()
117 97  
118 98 # Get (optional) offset parameters
119   - self.m_offset = self["offset"].boolean()
120   - if self.m_offset:
  99 + self._compute_offset = self["offset"].boolean()
  100 + if self._compute_offset:
121 101 ra = self["ra"].real()
122 102 dec = self["dec"].real()
123   - self.obj_dir.radec_deg(ra,dec)
  103 + self._obj_dir.radec_deg(ra,dec)
  104 +
  105 + # Read ahead DS9 filename
  106 + if self._read_ahead:
  107 + self._ds9file = self["ds9file"].filename()
124 108  
125   - # Get (optional) DS9 filename
126   - self.ds9file = self["ds9file"].filename()
  109 + # Write input parameters into logger
  110 + if self._logTerse():
  111 + self._log_parameters()
  112 + self._log("\n")
127 113  
128 114 # Return
129 115 return
130 116  
  117 +
  118 + # Public methods
131 119 def run(self):
132 120 """
133 121 Run the script.
134 122 """
135 123 # Switch screen logging on in debug mode
136   - if self.logDebug():
137   - self.log.cout(True)
  124 + if self._logDebug():
  125 + self._log.cout(True)
138 126  
139 127 # Get parameters
140   - self.get_parameters()
141   -
142   - # Write input parameters into logger
143   - if self.logTerse():
144   - self.log_parameters()
145   - self.log("\n")
  128 + self._get_parameters()
146 129  
147 130 # Initialise arrays to store certain values for reuse
148 131 # Todo, think about using a python dictionary
149   - self.m_offsets = []
150   - self.m_zeniths = []
151   - self.m_azimuths = []
152   - self.pnt_ra = []
153   - self.pnt_dec = []
154   - obs_names = []
  132 + self._offsets = []
  133 + self._zeniths = []
  134 + self._azimuths = []
  135 + self._pnt_ra = []
  136 + self._pnt_dec = []
  137 + self._ebounds = gammalib.GEbounds()
  138 + self._gti = gammalib.GGti()
  139 + obs_names = []
155 140  
156 141 # Initialise output to be filled
157   - self.m_ebounds = gammalib.GEbounds()
158   - self.m_gti = gammalib.GGti()
159 142 ontime = 0.0
160 143 livetime = 0.0
161 144 n_events = 0
... ... @@ -163,27 +146,30 @@ class csobsinfo(ctools.cscript):
163 146 n_obs_binned = 0
164 147 n_obs_unbinned = 0
165 148  
166   - # Logging
167   - self.log.header1("Looping over observations")
  149 + # Write header
  150 + if self._logTerse():
  151 + self._log("\n")
  152 + if self._obs.size() > 1:
  153 + self._log.header1("Observations")
  154 + else:
  155 + self._log.header1("Observation")
168 156  
169 157 # Loop over observations
170   - for obs in self.obs:
171   -
  158 + for obs in self._obs:
172 159  
173 160 # Skip non-CTA observations
174 161 if not obs.classname() == "GCTAObservation":
175   - self.log("Skipping "+obs.instrument()+" observation\n")
  162 + self._log("Skipping "+obs.instrument()+" observation\n")
176 163 continue
177 164  
178   - # Use observed object as observation name
179   - # if name is not given
  165 + # Use observed object as observation name if name is not given
180 166 obs_name = obs.name()
181 167 if obs_name == "":
182 168 obs_name = obs.object()
183 169  
184 170 # Logging
185   - if self.logTerse():
186   - self.log.header2(obs_name)
  171 + if self._logTerse():
  172 + self._log.header2(obs_name)
187 173  
188 174 # Retrieve observation name
189 175 obs_names.append(obs_name)
... ... @@ -199,206 +185,231 @@ class csobsinfo(ctools.cscript):
199 185  
200 186 # Retrieve pointing and store Ra,Dec
201 187 pnt_dir = obs.pointing().dir()
202   - self.pnt_ra.append(pnt_dir.ra_deg())
203   - self.pnt_dec.append(pnt_dir.dec_deg())
  188 + self._pnt_ra.append(pnt_dir.ra_deg())
  189 + self._pnt_dec.append(pnt_dir.dec_deg())
204 190  
205 191 # If avaliable append energy boundaries
206 192 if obs_bounds.size() > 0 :
207   - self.m_ebounds.append(obs_bounds.emin(),obs_bounds.emax())
  193 + self._ebounds.append(obs_bounds.emin(),obs_bounds.emax())
208 194  
209 195 # Append time interval
210   - self.m_gti.append(obs_gti.tstart(), obs_gti.tstop())
  196 + self._gti.append(obs_gti.tstart(), obs_gti.tstop())
211 197  
212 198 # Increment global livetime and ontime
213 199 ontime += obs.ontime()
214 200 livetime += obs.livetime()
215 201  
216   - # Log the event type (binned or unbinned)
217   - if self.logTerse():
218   - self.log.parformat("binned")
219   -
  202 + # Bookkeeping
220 203 if obs.eventtype() == "CountsCube":
221 204 n_eventbins += obs.events().size()
222 205 n_obs_binned += 1
223   - if self.logTerse():
224   - self.log("yes")
225   - self.log("\n")
226   - self.log.parformat("Number of bins")
227   - self.log(str(obs.events().size()))
228   - self.log("\n")
  206 + if self._logTerse():
  207 + self._log.parformat("Binned")
  208 + self._log("yes\n")
  209 + self._log.parformat("Number of bins")
  210 + self._log(str(obs.events().size()))
  211 + self._log("\n")
229 212 else:
230 213 n_events += obs.events().size()
231 214 n_obs_unbinned += 1
232   - if self.logTerse():
233   - self.log("no")
234   - self.log("\n")
235   - self.log.parformat("Number of events")
236   - self.log(str(obs.events().size()))
237   - self.log("\n")
  215 + if self._logTerse():
  216 + self._log.parformat("Binned")
  217 + self._log("no\n")
  218 + self._log.parformat("Number of events")
  219 + self._log(str(obs.events().size()))
  220 + self._log("\n")
  221 +
  222 + # Retrieve zenith and azimuth and store for later use
  223 + zenith = obs.pointing().zenith()
  224 + azimuth = obs.pointing().azimuth()
  225 + self._zeniths.append(zenith)
  226 + self._azimuths.append(azimuth)
  227 +
  228 + # Optionally compute offset with respect to target direction
  229 + if self._compute_offset:
  230 + offset = pnt_dir.dist_deg(self._obj_dir)
  231 + self._offsets.append(offset)
  232 + else:
  233 + self._offsets.append(-1.0)
238 234  
239   - # Log option
240   - if self.logTerse():
  235 + # Optionally log details
  236 + if self._logTerse():
241 237  
242 238 # Log the observation energy range (if available)
243   - self.log.parformat("Energy range")
  239 + self._log.parformat("Energy range")
244 240 if obs_bounds.size() == 0:
245   - self.log("undefined")
  241 + self._log("undefined")
246 242 else:
247   - self.log(str(obs_bounds.emin())+" - "+str(obs_bounds.emax()))
248   - self.log("\n")
  243 + self._log(str(obs_bounds.emin()))
  244 + self._log(" - ")
  245 + self._log(str(obs_bounds.emax()))
  246 + self._log("\n")
249 247  
250 248 # Log observation time interval
251   - self.log.parformat("Time range (MJD)")
  249 + self._log.parformat("Time range (MJD)")
252 250 if obs_gti.size() == 0:
253   - self.log("undefined")
  251 + self._log("undefined")
254 252 else:
255   - self.log(str(obs_gti.tstart().mjd())+" - "+str(obs_gti.tstop().mjd()))
256   - self.log("\n")
  253 + self._log(str(obs_gti.tstart().mjd()))
  254 + self._log(" - ")
  255 + self._log(str(obs_gti.tstop().mjd()))
  256 + self._log("\n")
257 257  
258 258 # Log ontime
259   - self.log.parformat("Ontime [s]")
260   - self.log(str(obs.ontime()))
261   - self.log("\n")
  259 + self._log.parformat("Ontime")
  260 + self._log(str(obs.ontime()))
  261 + self._log(" s\n")
262 262  
263 263 # Log livetime
264   - self.log.parformat("Livetime [s]")
265   - self.log(str(obs.livetime()))
266   - self.log("\n")
  264 + self._log.parformat("Livetime")
  265 + self._log(str(obs.livetime()))
  266 + self._log(" s\n")
267 267  
268 268 # Log dead time fraction
269   - self.log.parformat("Dead-time fraction (%)")
270   - self.log("%.3f" % (deadfrac))
271   - self.log("\n")
  269 + self._log.parformat("Deadtime fraction (%)")
  270 + self._log("%.3f" % (deadfrac))
  271 + self._log("\n")
272 272  
273 273 # Log pointing direction
274   - self.log.parformat("Pointing")
275   - self.log(str(pnt_dir))
276   - self.log("\n")
  274 + self._log.parformat("Pointing")
  275 + self._log(str(pnt_dir))
  276 + self._log("\n")
277 277  
278   - # Log offset if possible
279   - if self.m_offset:
280   - offset = pnt_dir.dist_deg(self.obj_dir)
281   - self.m_offsets.append(offset)
282   - self.log.parformat("Offset")
283   - self.log("%.2f" % (offset))
284   - self.log("\n")
285   - else:
286   - self.m_offsets.append(-1.0)
  278 + # Optionally log offset with respect to target direction
  279 + if self._compute_offset:
  280 + self._log.parformat("Offset from target")
  281 + self._log("%.2f" % (offset))
  282 + self._log(" deg\n")
287 283  
288   - # Retrieve zenith and azimuth and store for later use
289   - zenith = obs.pointing().zenith()
290   - azimuth = obs.pointing().azimuth()
291   - self.m_zeniths.append(zenith)
292   - self.m_azimuths.append(azimuth)
293   -
294 284 # Log Zenith and Azimuth if required
295   - if self.logExplicit():
296   - self.log.parformat("Zenith angle")
297   - self.log("%.2f" % (zenith))
298   - self.log("\n")
299   - self.log.parformat("Azimuth angle")
300   - self.log("%.2f" % (azimuth))
301   - self.log("\n\n")
  285 + self._log.parformat("Zenith angle")
  286 + self._log("%.2f" % (zenith))
  287 + self._log(" deg\n")
  288 + self._log.parformat("Azimuth angle")
  289 + self._log("%.2f" % (azimuth))
  290 + self._log(" deg\n")
302 291  
303 292 # Log summary
304   - self.log("\n")
305   - self.log.header1("Summary")
  293 + if self._logTerse():
  294 +
  295 + # Write header
  296 + self._log("\n")
  297 + self._log.header1("Summary")
306 298  
307   - # Log general summary
308   - self.log.parformat("Unbinned observations")
309   - self.log(str(n_obs_unbinned))
310   - self.log("\n")
311   - self.log.parformat("Number of events")
312   - self.log(str(n_events))
313   - self.log("\n")
314   - self.log.parformat("Binned observations")
315   - self.log(str(n_obs_binned))
316   - self.log("\n")
317   - self.log.parformat("Number of bins")
318   - self.log(str(n_eventbins))
319   - self.log("\n\n")
  299 + # Log general summary
  300 + self._log.header3("Observations")
  301 + self._log.parformat("Unbinned observations")
  302 + self._log(str(n_obs_unbinned))
  303 + self._log("\n")
  304 + self._log.parformat("Binned observations")
  305 + self._log(str(n_obs_binned))
  306 + self._log("\n")
  307 + self._log.header3("Events")
  308 + self._log.parformat("Number of events")
  309 + self._log(str(n_events))
  310 + self._log("\n")
  311 + self._log.parformat("Number of bins")
  312 + self._log(str(n_eventbins))
  313 + self._log("\n")
320 314  
321   - # Log pointing summary
322   - self.log.header3("Pointings")
  315 + # Log pointing summary
  316 + self._log.header3("Pointings")
323 317  
324   - # Log mean offset if possible
325   - if self.m_offset:
326   - self.log.parformat("Mean offset angle")
327   - self.log("%.2f" % (sum(self.m_offsets)/len(self.m_offsets)))
328   - self.log("\n")
  318 + # Log mean offset if possible
  319 + if self._compute_offset:
  320 + self._log.parformat("Mean offset angle")
  321 + self._log("%.2f" % (sum(self._offsets)/len(self._offsets)))
  322 + self._log(" deg\n")
329 323  
330   - # Log mean azimuth and zenith angle
331   - self.log.parformat("Mean zenith angle")
332   - self.log("%.2f" % (sum(self.m_zeniths)/len(self.m_zeniths)))
333   - self.log("\n")
334   - self.log.parformat("Mean azimuth angle")
335   - self.log("%.2f" % (sum(self.m_azimuths)/len(self.m_azimuths)))
336   - self.log("\n")
  324 + # Log mean azimuth and zenith angle
  325 + self._log.parformat("Mean zenith angle")
  326 + self._log("%.2f" % (sum(self._zeniths)/len(self._zeniths)))
  327 + self._log(" deg\n")
  328 + self._log.parformat("Mean azimuth angle")
  329 + self._log("%.2f" % (sum(self._azimuths)/len(self._azimuths)))
  330 + self._log(" deg\n")
337 331  
338   - # Log name of observations if requested
339   - if self.logExplicit():
340   - obs_set = set(obs_names)
341   - for name in obs_set:
342   - self.log.parformat("\""+name+"\"")
343   - self.log(str(obs_names.count(name)))
344   - self.log("\n")
345   - self.log("\n")
  332 + # Optionally log name of observations
  333 + if self._logExplicit():
  334 + obs_set = set(obs_names)
  335 + for name in obs_set:
  336 + self._log.parformat("\""+name+"\"")
  337 + self._log(str(obs_names.count(name)))
  338 + self._log("\n")
  339 + self._log("\n")
346 340  
347   - # Log energy range
348   - self.log.header3("Energy range")
349   - self.log.parformat("Emin")
350   - if self.m_ebounds.size() == 0:
351   - self.log("undefined")
352   - else:
353   - self.log(str(self.m_ebounds.emin()))
354   - self.log("\n")
355   - self.log.parformat("Emax")
356   - if self.m_ebounds.size() == 0:
357   - self.log("undefined")
358   - else:
359   - self.log(str(self.m_ebounds.emax()))
360   - self.log("\n\n")
  341 + # Log energy range
  342 + self._log.header3("Energy range")
  343 + self._log.parformat("Minimum energy")
  344 + if self._ebounds.size() == 0:
  345 + self._log("undefined")
  346 + else:
  347 + self._log(str(self._ebounds.emin()))
  348 + self._log("\n")
  349 + self._log.parformat("Maximum energy")
  350 + if self._ebounds.size() == 0:
  351 + self._log("undefined")
  352 + else:
  353 + self._log(str(self._ebounds.emax()))
  354 + self._log("\n")
361 355  
362   - # Log time range
363   - self.log.header3("Time range")
364   - self.log.parformat("Start [MJD]")
365   - self.log(str(self.m_gti.tstart().mjd()))
366   - self.log("\n")
367   - self.log.parformat("Stop [MJD]")
368   - self.log(str(self.m_gti.tstop().mjd()))
369   - self.log("\n")
  356 + # Log time range
  357 + self._log.header3("Time range")
  358 + self._log.parformat("Start (MJD)")
  359 + self._log(str(self._gti.tstart().mjd()))
  360 + self._log("\n")
  361 + self._log.parformat("Stop (MJD)")
  362 + self._log(str(self._gti.tstop().mjd()))
  363 + self._log("\n")
370 364  
371   - # Log ontime and livetime in different units
372   - self.log.parformat("Total ontime")
373   - self.log("%.2f s = %.2f min = %.2f h" % (ontime, ontime/60., ontime/3600.))
374   - self.log("\n")
375   - self.log.parformat("Total livetime")
376   - self.log("%.2f s = %.2f min = %.2f h" % (livetime, livetime/60.0, livetime/3600.))
377   - self.log("\n")
  365 + # Log ontime and livetime in different units
  366 + self._log.parformat("Total ontime")
  367 + self._log("%.2f s = %.2f min = %.2f h" % (ontime, ontime/60., ontime/3600.))
  368 + self._log("\n")
  369 + self._log.parformat("Total livetime")
  370 + self._log("%.2f s = %.2f min = %.2f h" % (livetime, livetime/60.0, livetime/3600.))
  371 + self._log("\n")
378 372  
379 373 # Return
380 374 return
381 375  
382 376 def save(self):
383 377 """
384   - Save pointings to ds9 region file if required
  378 + Save pointings into DS9 region file.
  379 +
  380 + This method saves all pointing directions that are found in the
  381 + observation container into a DS9 region file. If "NONE" is
  382 + specified for the "ds9file" parameter the method does nothing.
385 383 """
386   -
387   - # Check if ds9 file is valid
388   - if not self.ds9file == "NONE":
  384 + # Write header
  385 + if self._logTerse():
  386 + self._log("\n")
  387 + self._log.header1("Save pointings in DS9 file")
  388 +
  389 + # Get output filename in case it was not read ahead
  390 + self._ds9file = self["ds9file"].filename()
  391 +
  392 + # Check if DS9 file is valid
  393 + if self._ds9file != "NONE":
  394 +
  395 + # Log filename
  396 + if self._logTerse():
  397 + self._log(gammalib.parformat("DS9 filename"))
  398 + self._log(self._ds9file.url())
  399 + self._log("\n")
389 400  
390 401 # Open file
391   - f = open(self.ds9file.url(),"w")
  402 + f = open(self._ds9file.url(),"w")
392 403  
393 404 # Write coordinate system
394 405 f.write("fk5\n")
395 406  
396 407 # Loop over pointings
397   - for i in range(len(self.pnt_ra)):
  408 + for i in range(len(self._pnt_ra)):
398 409  
399 410 # Create string
400 411 line = "point("
401   - line+=str(self.pnt_ra[i])+","+str(self.pnt_dec[i])+")"
  412 + line+=str(self._pnt_ra[i])+","+str(self._pnt_dec[i])+")"
402 413 line+=" # point=cross 20 width=3 \n"
403 414  
404 415 # Write to file
... ... @@ -410,41 +421,43 @@ class csobsinfo(ctools.cscript):
410 421 # Return
411 422 return
412 423  
413   -
414 424 def zeniths(self):
415 425 """
416 426 Return zenith angles
417 427 """
418   - return self.m_zeniths
  428 + return self._zeniths
419 429  
420 430 def azimuths(self):
421 431 """
422 432 Return azimuth angles
423 433 """
424   - return self.m_azimuths
  434 + return self._azimuths
425 435  
426 436 def offsets(self):
427 437 """
428 438 Return offset angles
429 439 """
430   - return self.m_offsets
  440 + return self._offsets
431 441  
432 442 def ebounds(self):
433 443 """
434 444 Return energy boundaries
435 445 """
436   - return self.m_ebounds
  446 + return self._ebounds
437 447  
438 448 def gti(self):
439 449 """
440 450 Return good time intervals
441 451 """
442   - return self.m_gti
  452 + return self._gti
443 453  
444 454 def execute(self):
445 455 """
446 456 Execute the script.
447 457 """
  458 + # Set read ahead flag
  459 + self._read_ahead = True
  460 +
448 461 # Run the script
449 462 self.run()
450 463  
... ... @@ -459,14 +472,12 @@ class csobsinfo(ctools.cscript):
459 472 # Main routine entry point #
460 473 # ======================== #
461 474 if __name__ == '__main__':
462   - """
463   - Generates information about an observation container.
464   - """
  475 +
465 476 # Create instance of application
466 477 app = csobsinfo(sys.argv)
467 478  
468 479 # Open logfile
469   - app.logFileOpen()
  480 + app._logFileOpen()
470 481  
471 482 # Execute application
472 483 app.execute()
... ...
cscripts/cspull.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: cspull.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the cspull script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inobs, f, h, NONE,,, "Input event list, counts cube, or observation definition XML file"
  21 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model XML file"
  22 +caldb, s, a, prod2,,, "Calibration database"
  23 +irf, s, a, South_0.5h,,, "Instrument response function"
  24 +edisp, b, h, no,,, "Apply energy dispersion?"
  25 +deadc, r, h, 0.95,0,1, "Deadtime correction factor"
  26 +outfile, f, a, pull.dat,,, "Output pull distribution file"
  27 +
  28 +#
  29 +# Script parameters
  30 +#==================
  31 +ntrials, i, a, 10,,, "Number of trials"
  32 +ra, r, a, 83.6331,0,360, "RA of pointing (deg)"
  33 +dec, r, a, 22.0145,-90,90, "Dec of pointing (deg)"
  34 +coordsys, s, a, CEL,CEL|GAL,, "Coordinate system (CEL - celestial, GAL - galactic)"
  35 +proj, s, a, TAN,AIT|AZP|CAR|MER|MOL|STG|TAN,, "Projection method"
  36 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  37 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  38 +enumbins, i, a, 0,,, "Number of energy bins (0=unbinned)"
  39 +tmin, r, h, 0.0,,, "Start time (MET in s)"
  40 +tmax, r, a, 1800.0,,, "Duration (s)"
  41 +npix, i, a, 200,,, "Number of pixels for binned"
  42 +binsz, r, a, 0.05,,, "Pixel size for binned (deg/pixel)"
  43 +rad, r, h, 5.0,0,180, "Radius of ROI (deg)"
  44 +pattern, s, h, single,single|four,, "Observation pattern"
  45 +offset, r, h, 1.5,,, "Observation pattern offset (deg)"
  46 +profile, b, h, no,,, "Use likelihood profile method for errors?"
  47 +
  48 +#
  49 +# Standard parameters
  50 +#====================
  51 +chatter, i, h, 2,0,4, "Chattiness of output"
  52 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  53 +debug, b, h, no,,, "Debugging mode activated"
  54 +mode, s, h, ql,,, "Mode of automatic parameters"
  55 +logfile, f, h, cspull.log,,, "Log filename"
... ...
cscripts/cspull.py
... ... @@ -30,47 +30,49 @@ import csv
30 30 # ============ #
31 31 class cspull(ctools.cscript):
32 32 """
  33 + Generates pull distributions for a model.
  34 +
33 35 This class implements the pull distribution generation script. It derives
34 36 from the ctools.cscript class which provides support for parameter
35 37 files, command line arguments, and logging. In that way the Python
36 38 script behaves just as a regular ctool.
37 39 """
  40 +
  41 + # Constructors and destructors
38 42 def __init__(self, *argv):
39 43 """
40 44 Constructor.
41 45 """
42 46 # Set name
43   - self.name = "cspull"
44   - self.version = "1.0.0"
  47 + self._name = "cspull"
  48 + self._version = "1.1.0"
45 49  
46 50 # Initialise some members
47   - self.obs = None
48   - self.model = None
49   - self.m_inmodel = None
50   - self.m_edisp = False
51   - self.m_profile = False
52   - self.m_exposure = None
53   - self.m_psfcube = None
54   - self.m_bckcube = None
55   - self.m_edispcube = None
56   - self.m_stackmodels = None
57   - self.m_coordsys = "CEL"
58   - self.m_proj = "TAN"
59   -
60   - # Make sure that parfile exists
61   - file = self.parfile()
  51 + self._obs = None
  52 + self._model = None
  53 + self._inmodel = None
  54 + self._edisp = False
  55 + self._profile = False
  56 + self._exposure = None
  57 + self._psfcube = None
  58 + self._bckcube = None
  59 + self._edispcube = None
  60 + self._stackmodels = None
  61 + self._coordsys = "CEL"
  62 + self._proj = "TAN"
  63 + self._log_clients = False
62 64  
63 65 # Initialise application
64 66 if len(argv) == 0:
65   - ctools.cscript.__init__(self, self.name, self.version)
  67 + ctools.cscript.__init__(self, self._name, self._version)
66 68 elif len(argv) ==1:
67   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  69 + ctools.cscript.__init__(self, self._name, self._version, *argv)
68 70 else:
69 71 raise TypeError("Invalid number of arguments given.")
70 72  
71 73 # Set logger properties
72   - self.log_header()
73   - self.log.date(True)
  74 + self._log_header()
  75 + self._log.date(True)
74 76  
75 77 # Return
76 78 return
... ... @@ -79,348 +81,220 @@ class cspull(ctools.cscript):
79 81 """
80 82 Destructor.
81 83 """
82   - # Write separator into logger
83   - if self.logTerse():
84   - self.log("\n")
85   -
86 84 # Return
87 85 return
88 86  
89   - def parfile(self):
90   - """
91   - Check if parfile exists. If parfile does not exist then create a
92   - default parfile. This kluge avoids shipping the cscript with a parfile.
93   - """
94   - # Set parfile name
95   - parfile = self.name+".par"
96   -
97   - try:
98   - pars = gammalib.GApplicationPars(parfile)
99   - except:
100   - # Signal if parfile was not found
101   - print("Parfile "+parfile+" not found. Create default parfile.")
102   -
103   - # Create default parfile
104   - pars = gammalib.GApplicationPars()
105   - pars.append(gammalib.GApplicationPar("inobs","f","h","NONE","","","Input event list, counts cube, or observation definition XML file"))
106   - pars.append(gammalib.GApplicationPar("inmodel","f","a","$CTOOLS/share/models/crab.xml","","","Input model XML file"))
107   - pars.append(gammalib.GApplicationPar("caldb","s","a","prod2","","","Calibration database"))
108   - pars.append(gammalib.GApplicationPar("irf","s","a","South_0.5h","","","Instrument response function"))
109   - pars.append(gammalib.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?"))
110   - pars.append(gammalib.GApplicationPar("deadc","r","h","0.95","0","1","Deadtime correction factor"))
111   - pars.append(gammalib.GApplicationPar("outfile","f","a","pull.dat","","","Output pull distribution file"))
112   - pars.append(gammalib.GApplicationPar("profile","b","h","no","","","Use likelihood profile method for errors?"))
113   - pars.append(gammalib.GApplicationPar("ntrials","i","a","10","","","Number of trials"))
114   - pars.append(gammalib.GApplicationPar("ra","r","a","83.6331","0","360","RA of pointing (deg)"))
115   - pars.append(gammalib.GApplicationPar("dec","r","a","22.0145","-90","90","Dec of pointing (deg)"))
116   - pars.append(gammalib.GApplicationPar("coordsys","s","a","CEL","CEL|GAL","","Coordinate system (CEL - celestial, GAL - galactic)"))
117   - pars.append(gammalib.GApplicationPar("proj","s","a","TAN","AIT|AZP|CAR|MER|MOL|STG|TAN","","Projection method"))
118   - pars.append(gammalib.GApplicationPar("emin","r","a","0.1","","","Lower energy limit (TeV)"))
119   - pars.append(gammalib.GApplicationPar("emax","r","a","100.0","","","Upper energy limit (TeV)"))
120   - pars.append(gammalib.GApplicationPar("enumbins","i","a","0","","","Number of energy bins (0=unbinned)"))
121   - pars.append(gammalib.GApplicationPar("tmin","r","h","0.0","","","Start time (MET in s)"))
122   - pars.append(gammalib.GApplicationPar("tmax","r","a","1800.0","","","Duration (in s)"))
123   - pars.append(gammalib.GApplicationPar("npix","i","a","200","","","Number of pixels for binned"))
124   - pars.append(gammalib.GApplicationPar("binsz","r","a","0.05","","","Pixel size for binned (deg/pixel)"))
125   - pars.append(gammalib.GApplicationPar("rad","r","h","5.0","0","180","Radius of ROI (deg)"))
126   - pars.append(gammalib.GApplicationPar("pattern","s","h","single","","","Observation pattern (single/four)"))
127   - pars.append(gammalib.GApplicationPar("offset","r","h","1.5","","","Observation pattern offset (deg)"))
128   - pars.append_standard()
129   - pars.append(gammalib.GApplicationPar("logfile","f","h","cspull.log","","","Log filename"))
130   - pars.save(parfile)
131   -
132   - # Return
133   - return
134 87  
135   - def get_parameters(self):
  88 + # Private methods
  89 + def _get_parameters(self):
136 90 """
137   - Get parameters from parfile and setup the observation.
  91 + Get parameters from parfile.
138 92 """
139 93 # Get parameters
140 94  
141 95 # Set observation if not done before
142   - if self.obs == None or self.obs.size() == 0:
143   - self.obs = self._get_observations()
  96 + if self._obs == None or self._obs.size() == 0:
  97 + self._obs = self._get_observations()
144 98  
145 99 # Check for requested pattern and use above
146 100 # observation parameters to set wobble pattern
147   - self.m_pattern = self["pattern"].string()
148   - if self.m_pattern == "four":
149   - self.obs = self.set_obs()
  101 + self._pattern = self["pattern"].string()
  102 + if self._pattern == "four":
  103 + self._obs = self._set_obs()
150 104  
151 105 # Get number of energy bins
152   - self.m_enumbins = self["enumbins"].integer()
  106 + self._enumbins = self["enumbins"].integer()
153 107  
154 108 # Read parameters for binned if requested
155   - if not self.m_enumbins == 0:
156   - self.m_npix = self["npix"].integer()
157   - self.m_binsz = self["binsz"].real()
158   - self.m_coordsys = self["coordsys"].string()
159   - self.m_proj = self["proj"].string()
  109 + if not self._enumbins == 0:
  110 + self._npix = self["npix"].integer()
  111 + self._binsz = self["binsz"].real()
  112 + self._coordsys = self["coordsys"].string()
  113 + self._proj = self["proj"].string()
160 114 else:
161 115 # Set dummy values (required by obsutils)
162   - self.m_npix = 0
163   - self.m_binsz = 0.0
  116 + self._npix = 0
  117 + self._binsz = 0.0
164 118  
165 119 # Set models if we have none
166   - if self.obs.models().size() == 0:
167   - self.obs.models(self["inmodel"].filename())
  120 + if self._obs.models().size() == 0:
  121 + self._obs.models(self["inmodel"].filename())
168 122  
169 123 # Read other parameters
170   - self.m_outfile = self["outfile"].filename()
171   - self.m_ntrials = self["ntrials"].integer()
172   - self.m_edisp = self["edisp"].boolean()
173   - self.m_offset = self["offset"].real()
174   - self.m_profile = self["profile"].boolean()
  124 + self._outfile = self["outfile"].filename()
  125 + self._ntrials = self["ntrials"].integer()
  126 + self._edisp = self["edisp"].boolean()
  127 + self._offset = self["offset"].real()
  128 + self._profile = self["profile"].boolean()
  129 + self._chatter = self["chatter"].integer()
  130 + self._debug = self["debug"].boolean()
175 131  
176   - # Set some fixed parameters
177   - self.m_log = False # Logging in client tools
178   - self.m_chatter = self["chatter"].integer() # Chatter level
179   - self.m_debug = self["debug"].boolean() # Debugging in client tools
  132 + # Write input parameters into logger
  133 + if self._logTerse():
  134 + self._log_parameters()
  135 + self._log("\n")
180 136  
181 137 # Return
182 138 return
183 139  
184   -
185   - def models(self, models):
  140 + def _set_obs(self):
186 141 """
187   - Set model.
188   - """
189   - # Copy models
190   - self.obs.models(models.copy())
191   -
192   - # Return
193   - return
194   -
  142 + Set CTA observation(s).
195 143  
196   - def execute(self):
197   - """
198   - Execute the script.
  144 + Returns:
  145 + An observation container.
199 146 """
200   - # Run the script
201   - self.run()
  147 + # Setup observation definition list
  148 + obsdeflist = obsutils.set_obs_patterns(self._pattern,
  149 + ra=self["ra"].real(),
  150 + dec=self["dec"].real(),
  151 + offset=self["offset"].real())
202 152  
203   - # Return
204   - return
  153 + # Create list of observations
  154 + obs = obsutils.set_obs_list(obsdeflist,
  155 + tstart=self["tmin"].real(),
  156 + duration=self["tmax"].real()-self["tmin"].real(),
  157 + deadc=self["deadc"].real(),
  158 + emin=self["emin"].real(),
  159 + emax=self["emax"].real(),
  160 + rad=self["rad"].real(),
  161 + irf=self["irf"].string(),
  162 + caldb=self["caldb"].string())
205 163  
  164 + # Return observation container
  165 + return obs
206 166  
207   - def set_stacked_irf(self):
  167 + def _set_stacked_irf(self):
208 168 """
209 169 For stacked analysis prepare stacked irfs
210 170 """
211 171 # Write header into logger
212   - if self.logTerse():
213   - self.log("\n")
214   - self.log.header1("Compute stacked response")
  172 + if self._logTerse():
  173 + self._log("\n")
  174 + self._log.header1("Compute stacked response")
215 175  
216 176 # Get stacked exposure
217   - expcube = ctools.ctexpcube(self.obs)
  177 + expcube = ctools.ctexpcube(self._obs)
218 178 expcube["incube"] = "NONE"
219 179 expcube["usepnt"] = True
220 180 expcube["ebinalg"] = "LOG"
221   - expcube["binsz"] = self.m_binsz
222   - expcube["nxpix"] = self.m_npix
223   - expcube["nypix"] = self.m_npix
224   - expcube["enumbins"] = self.m_enumbins
  181 + expcube["binsz"] = self._binsz
  182 + expcube["nxpix"] = self._npix
  183 + expcube["nypix"] = self._npix
  184 + expcube["enumbins"] = self._enumbins
225 185 expcube["emin"] = self["emin"].real()
226 186 expcube["emax"] = self["emax"].real()
227   - expcube["coordsys"] = self.m_coordsys
228   - expcube["proj"] = self.m_proj
  187 + expcube["coordsys"] = self._coordsys
  188 + expcube["proj"] = self._proj
229 189 expcube.run()
230 190  
231 191 # Notify exposure computation
232   - if self.logTerse():
233   - self.log("Computed exposure cube\n")
  192 + if self._logTerse():
  193 + self._log("Computed exposure cube\n")
234 194  
235 195 # Get stacked Psf
236   - psfcube = ctools.ctpsfcube(self.obs)
  196 + psfcube = ctools.ctpsfcube(self._obs)
237 197 psfcube["incube"] = "NONE"
238 198 psfcube["usepnt"] = True
239 199 psfcube["ebinalg"] = "LOG"
240   - psfcube["binsz"] = self.m_binsz*10.0
241   - psfcube["nxpix"] = self.m_npix/10
242   - psfcube["nypix"] = self.m_npix/10
243   - psfcube["enumbins"] = self.m_enumbins
  200 + psfcube["binsz"] = self._binsz*10.0
  201 + psfcube["nxpix"] = self._npix/10
  202 + psfcube["nypix"] = self._npix/10
  203 + psfcube["enumbins"] = self._enumbins
244 204 psfcube["emin"] = self["emin"].real()
245 205 psfcube["emax"] = self["emax"].real()
246   - psfcube["coordsys"] = self.m_coordsys
247   - psfcube["proj"] = self.m_proj
  206 + psfcube["coordsys"] = self._coordsys
  207 + psfcube["proj"] = self._proj
248 208 psfcube.run()
249 209  
250 210 # Notify Psf computation
251   - if self.logTerse():
252   - self.log("Computed Psf cube\n")
  211 + if self._logTerse():
  212 + self._log("Computed point spread function cube\n")
253 213  
254 214 # Optionally get stacked Edisp
255   - if self.m_edisp:
256   - edispcube = ctools.edispcube(self.obs)
  215 + if self._edisp:
  216 + edispcube = ctools.edispcube(self._obs)
257 217 edispcube["incube"] = "NONE"
258 218 edispcube["usepnt"] = True
259 219 edispcube["ebinalg"] = "LOG"
260   - edispcube["binsz"] = self.m_binsz*10.0
261   - edispcube["nxpix"] = self.m_npix/10
262   - edispcube["nypix"] = self.m_npix/10
263   - edispcube["enumbins"] = self.m_enumbins
  220 + edispcube["binsz"] = self._binsz*10.0
  221 + edispcube["nxpix"] = self._npix/10
  222 + edispcube["nypix"] = self._npix/10
  223 + edispcube["enumbins"] = self._enumbins
264 224 edispcube["emin"] = self["emin"].real()
265 225 edispcube["emax"] = self["emax"].real()
266   - edispcube["coordsys"] = self.m_coordsys
267   - edispcube["proj"] = self.m_proj
  226 + edispcube["coordsys"] = self._coordsys
  227 + edispcube["proj"] = self._proj
268 228 edispcube.run()
269 229  
270 230 # Store result
271   - self.m_edispcube = edispcube.edispcube().copy()
  231 + self._edispcube = edispcube.edispcube().copy()
272 232  
273 233 # Logging
274   - if self.logTerse():
275   - self.log("Computed energy dispersion cube\n")
  234 + if self._logTerse():
  235 + self._log("Computed energy dispersion cube\n")
276 236  
277 237 # Get stacked background
278   - bkgcube = ctools.ctbkgcube(self.obs)
  238 + bkgcube = ctools.ctbkgcube(self._obs)
279 239 bkgcube["incube"] = "NONE"
280 240 bkgcube["usepnt"] = True
281 241 bkgcube["ebinalg"] = "LOG"
282   - bkgcube["binsz"] = self.m_binsz
283   - bkgcube["nxpix"] = self.m_npix
284   - bkgcube["nypix"] = self.m_npix
285   - bkgcube["enumbins"] = self.m_enumbins
  242 + bkgcube["binsz"] = self._binsz
  243 + bkgcube["nxpix"] = self._npix
  244 + bkgcube["nypix"] = self._npix
  245 + bkgcube["enumbins"] = self._enumbins
286 246 bkgcube["emin"] = self["emin"].real()
287 247 bkgcube["emax"] = self["emax"].real()
288   - bkgcube["coordsys"] = self.m_coordsys
289   - bkgcube["proj"] = self.m_proj
  248 + bkgcube["coordsys"] = self._coordsys
  249 + bkgcube["proj"] = self._proj
290 250 bkgcube.run()
291 251  
292 252 # Notify background cube computation
293   - if self.logTerse():
294   - self.log("Computed background cube\n")
  253 + if self._logTerse():
  254 + self._log("Computed background cube\n")
295 255  
296 256 # Store results
297   - self.m_exposure = expcube.expcube().copy()
298   - self.m_psfcube = psfcube.psfcube().copy()
299   - self.m_bckcube = bkgcube.bkgcube().copy()
300   - self.m_stackmodels = bkgcube.models().copy()
301   -
302   - # Return
303   - return
304   -
305   -
306   - def run(self):
307   - """
308   - Run the script.
309   - """
310   - # Switch screen logging on in debug mode
311   - if self.logDebug():
312   - self.log.cout(True)
313   -
314   - # Get parameters
315   - self.get_parameters()
316   -
317   - # Write input parameters into logger
318   - if self.logTerse():
319   - self.log_parameters()
320   - self.log("\n")
321   -
322   - # Write observation into logger
323   - if self.logTerse():
324   - self.log("\n")
325   - self.log.header1("Observation(s)")
326   - self.log(str(self.obs))
327   - self.log("\n")
328   -
329   - # If several observations and binned: prepare stacked irfs
330   - if self.obs.size() > 1 and self.m_enumbins > 0:
331   - self.set_stacked_irf()
332   -
333   - # Write header
334   - if self.logTerse():
335   - self.log("\n")
336   - self.log.header1("Generate pull distribution")
337   -
338   - # Loop over trials
339   - for seed in range(self.m_ntrials):
340   -
341   - # Make a trial
342   - result = self.trial(seed)
343   -
344   - # Write out result immediately
345   - if seed == 0:
346   - file = open(self.m_outfile.url(), 'w')
347   - writer = csv.DictWriter(file, result['colnames'])
348   - headers = {}
349   - for n in result['colnames']:
350   - headers[n] = n
351   - writer.writerow(headers)
352   - else:
353   - file = open(self.m_outfile.url(), 'a')
354   - writer = csv.DictWriter(file, result['colnames'])
355   - writer.writerow(result['values'])
356   - file.close()
  257 + self._exposure = expcube.expcube().copy()
  258 + self._psfcube = psfcube.psfcube().copy()
  259 + self._bckcube = bkgcube.bkgcube().copy()
  260 + self._stackmodels = bkgcube.models().copy()
357 261  
358 262 # Return
359 263 return
360 264  
361   -
362   - def set_obs(self):
363   - """
364   - Returns an observation container with a set of CTA observations.
365   -
366   - Keywords:
  265 + def _trial(self, seed):
367 266 """
  267 + Compute the pull for a single trial.
368 268  
369   - # Setup observation definition list
370   - obsdeflist = obsutils.set_obs_patterns(self.m_pattern,
371   - ra=self["ra"].real(),
372   - dec=self["dec"].real(),
373   - offset=self["offset"].real())
374   -
375   - # Create list of observations
376   - obs = obsutils.set_obs_list(obsdeflist,
377   - tstart=self["tmin"].real(),
378   - duration=self["tmax"].real()-self["tmin"].real(),
379   - deadc=self["deadc"].real(),
380   - emin=self["emin"].real(),
381   - emax=self["emax"].real(),
382   - rad=self["rad"].real(),
383   - irf=self["irf"].string(),
384   - caldb=self["caldb"].string())
385   -
386   - # Return observation container
387   - return obs
388   -
389   -
390   - def trial(self, seed):
  269 + Args:
  270 + seed: Random number generator seed.
391 271 """
392   - Create the pull for a single trial.
393   -
394   - Parameters:
395   - seed - Random number generator seed
396   - """
397   -
398 272 # Write header
399   - if self.logNormal():
400   - self.log.header2("Trial "+str(seed+1))
  273 + if self._logNormal():
  274 + self._log.header2("Trial "+str(seed+1))
401 275  
402 276 # Simulate events
403   - obs = obsutils.sim(self.obs,
404   - nbins=self.m_enumbins,
  277 + obs = obsutils.sim(self._obs,
  278 + nbins=self._enumbins,
405 279 seed=seed,
406   - binsz=self.m_binsz,
407   - npix=self.m_npix,
408   - proj=self.m_proj,
409   - coord=self.m_coordsys,
410   - edisp=self.m_edisp,
411   - log=self.m_log,
412   - debug=self.m_debug,
413   - chatter=self.m_chatter)
414   -
415   - # If stacked, add stacked responses and model
416   - if self.obs.size() > 1 and self.m_enumbins > 0:
417   - if self.m_edisp:
418   - obs[0].response(self.m_exposure, self.m_psfcube,
419   - self.m_edispcube, self.m_bckcube)
  280 + binsz=self._binsz,
  281 + npix=self._npix,
  282 + proj=self._proj,
  283 + coord=self._coordsys,
  284 + edisp=self._edisp,
  285 + log=self._log_clients,
  286 + debug=self._debug,
  287 + chatter=self._chatter)
  288 +
  289 + # Set response for a stacked observation
  290 + if self._obs.size() > 1 and self._enumbins > 0:
  291 + if self._edisp:
  292 + obs[0].response(self._exposure, self._psfcube,
  293 + self._edispcube, self._bckcube)
420 294 else:
421   - obs[0].response(self.m_exposure, self.m_psfcube,
422   - self.m_bckcube)
423   - obs.models(self.m_stackmodels)
  295 + obs[0].response(self._exposure, self._psfcube,
  296 + self._bckcube)
  297 + obs.models(self._stackmodels)
424 298  
425 299 # Determine number of events in simulation
426 300 nevents = 0.0
... ... @@ -428,26 +302,26 @@ class cspull(ctools.cscript):
428 302 nevents += run.events().number()
429 303  
430 304 # Write simulation results
431   - if self.logNormal():
432   - self.log.header3("Simulation")
433   - self.log.parformat("Number of simulated events")
434   - self.log(nevents)
435   - self.log("\n")
  305 + if self._logNormal():
  306 + self._log.header3("Simulation")
  307 + self._log.parformat("Number of simulated events")
  308 + self._log(nevents)
  309 + self._log("\n")
436 310  
437 311 # Fit model
438   - if self.m_profile:
439   - models = obs.models()
  312 + if self._profile:
  313 + models = _obs.models()
440 314 for i in range(models.size()):
441 315 model_name = models[i].name()
442   - like = obsutils.cterror(obs, model_name,
443   - log=self.m_log,
444   - debug=self.m_debug,
445   - chatter=self.m_chatter)
  316 + like = obsutils.cterror(_obs, model_name,
  317 + log=self._log_clients,
  318 + debug=self._debug,
  319 + chatter=self._chatter)
446 320 else:
447   - like = obsutils.fit(obs, edisp=self.m_edisp,
448   - log=self.m_log,
449   - debug=self.m_debug,
450   - chatter=self.m_chatter)
  321 + like = obsutils.fit(obs, edisp=self._edisp,
  322 + log=self._log_clients,
  323 + debug=self._debug,
  324 + chatter=self._chatter)
451 325  
452 326 # Store results
453 327 logL = like.opt().value()
... ... @@ -455,8 +329,8 @@ class cspull(ctools.cscript):
455 329 models = like.obs().models()
456 330  
457 331 # Write result header
458   - if self.logNormal():
459   - self.log.header3("Pulls")
  332 + if self._logNormal():
  333 + self._log.header3("Pulls")
460 334  
461 335 # Gather results
462 336 colnames = []
... ... @@ -484,7 +358,7 @@ class cspull(ctools.cscript):
484 358  
485 359 # Compute pull
486 360 fitted_value = par.value()
487   - real_value = self.obs.models()[i][k].value()
  361 + real_value = self._obs.models()[i][k].value()
488 362 error = par.error()
489 363 if error != 0.0:
490 364 pull = (fitted_value - real_value) / error
... ... @@ -497,14 +371,14 @@ class cspull(ctools.cscript):
497 371 values["Unc_"+name] = error
498 372  
499 373 # Write result
500   - if self.logNormal():
501   - self.log.parformat(name)
502   - self.log(pull)
503   - self.log(" (")
504   - self.log(fitted_value)
505   - self.log(" +/- ")
506   - self.log(error)
507   - self.log(")\n")
  374 + if self._logNormal():
  375 + self._log.parformat(name)
  376 + self._log(pull)
  377 + self._log(" (")
  378 + self._log(fitted_value)
  379 + self._log(" +/- ")
  380 + self._log(error)
  381 + self._log(")\n")
508 382  
509 383 # Bundle together results
510 384 result = {'colnames': colnames, 'values': values}
... ... @@ -513,18 +387,88 @@ class cspull(ctools.cscript):
513 387 return result
514 388  
515 389  
  390 + # Public methods
  391 + def run(self):
  392 + """
  393 + Run the script.
  394 + """
  395 + # Switch screen logging on in debug mode
  396 + if self._logDebug():
  397 + self._log.cout(True)
  398 +
  399 + # Get parameters
  400 + self._get_parameters()
  401 +
  402 + # Write observation into logger
  403 + if self._logTerse():
  404 + self._log("\n")
  405 + self._log.header1("Observation(s)")
  406 + self._log(str(self._obs))
  407 + self._log("\n")
  408 +
  409 + # If several observations and binned: prepare stacked irfs
  410 + if self._obs.size() > 1 and self._enumbins > 0:
  411 + self._set_stacked_irf()
  412 +
  413 + # Write header
  414 + if self._logTerse():
  415 + self._log("\n")
  416 + self._log.header1("Generate pull distribution")
  417 +
  418 + # Loop over trials
  419 + for seed in range(self._ntrials):
  420 +
  421 + # Make a trial
  422 + result = self._trial(seed)
  423 +
  424 + # Write out result immediately
  425 + if seed == 0:
  426 + file = open(self._outfile.url(), 'w')
  427 + writer = csv.DictWriter(file, result['colnames'])
  428 + headers = {}
  429 + for n in result['colnames']:
  430 + headers[n] = n
  431 + writer.writerow(headers)
  432 + else:
  433 + file = open(self._outfile.url(), 'a')
  434 + writer = csv.DictWriter(file, result['colnames'])
  435 + writer.writerow(result['values'])
  436 + file.close()
  437 +
  438 + # Return
  439 + return
  440 +
  441 + def execute(self):
  442 + """
  443 + Execute the script.
  444 + """
  445 + # Run the script
  446 + self.run()
  447 +
  448 + # Return
  449 + return
  450 +
  451 + def models(self, models):
  452 + """
  453 + Set model.
  454 + """
  455 + # Copy models
  456 + self.obs.models(models.copy())
  457 +
  458 + # Return
  459 + return
  460 +
  461 +
516 462 # ======================== #
517 463 # Main routine entry point #
518 464 # ======================== #
519 465 if __name__ == '__main__':
520   - """
521   - Generates pull distribution for a source model.
522   - """
  466 +
523 467 # Create instance of application
524 468 app = cspull(sys.argv)
525 469  
526 470 # Open logfile
527   - app.logFileOpen()
  471 + app._logFileOpen()
528 472  
529 473 # Execute application
530 474 app.execute()
... ...
cscripts/csresmap.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csresmap.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csresmap script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inobs, f, h, events.fits,,, "Input event list, counts cube, or observation definition XML file"
  21 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model XML file"
  22 +modcube, f, a, NONE,,, "Input model cube file (generated with ctmodel)"
  23 +expcube, f, a, NONE,,, "Input exposure cube file (only needed for stacked analysis)"
  24 +psfcube, f, a, NONE,,, "Input PSF cube file (only needed for stacked analysis)"
  25 +edispcube, f, a, NONE,,, "Input energy dispersion cube file (only needed for stacked analysis)"
  26 +bkgcube, f, a, NONE,,, "Input background cube file (only needed for stacked analysis)"
  27 +caldb, s, a, prod2,,, "Calibration database"
  28 +irf, s, a, South_0.5h,,, "Instrument response function"
  29 +edisp, b, h, no,,, "Apply energy dispersion?"
  30 +outmap, f, a, resmap.fits,,, "Output residual map file"
  31 +
  32 +#
  33 +# Script parameters
  34 +#==================
  35 +ebinalg, s, h, LOG,LIN|LOG|FILE,, "Binning algorithm"
  36 +emin, r, h, 0.1,,, "Lower energy limit (TeV)"
  37 +emax, r, h, 100.0,,, "Upper energy limit (TeV)"
  38 +enumbins, i, h, 20,,, "Number of energy bins (0=unbinned)"
  39 +ebinfile, f, h, NONE,,, "Name of the file containing the energy bin definition"
  40 +coordsys, s, a, CEL,CEL|GAL,, "Coordinate System"
  41 +proj, s, a, CAR,AIT|AZP|CAR|MER|MOL|STG|TAN,, "Projection method"
  42 +xref, r, a, 83.63,0,360, "First coordinate of image center in degrees (RA or galactic l)"
  43 +yref, r, a, 22.01,-90,90, "Second coordinate of image center in degrees (DEC or galactic b)"
  44 +nxpix, i, a, 200,,, "Size of the X axis in pixels"
  45 +nypix, i, a, 200,,, "Size of the Y axis in pixels"
  46 +binsz, r, a, 0.02,,, "Pixel size (deg/pixel)"
  47 +algorithm, s, a, SUBDIV,SUB|SUBDIV|SUBDIVSQRT,, "Residual map computation algorithm"
  48 +
  49 +#
  50 +# Standard parameters
  51 +#====================
  52 +publish, b, h, no,,, "Publish residual map on VO Hub?"
  53 +chatter, i, h, 2,0,4, "Chattiness of output"
  54 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  55 +debug, b, h, no,,, "Debugging mode activated"
  56 +mode, s, h, ql,,, "Mode of automatic parameters"
  57 +logfile, f, h, csresmap.log,,, "Log filename"
... ...
cscripts/csresmap.py
1 1 #! /usr/bin/env python
2 2 # ==========================================================================
3   -# Residual map generation script.
  3 +# Generates a residual map.
4 4 #
5 5 # Copyright (C) 2014-2016 Michael Mayer
6 6 #
... ... @@ -22,57 +22,76 @@ import gammalib
22 22 import ctools
23 23 import sys
24 24  
  25 +
25 26 # ============== #
26   -# cstsdist class #
  27 +# csresmap class #
27 28 # ============== #
28 29 class csresmap(ctools.cscript):
29 30 """
  31 + Generates a residual map.
  32 +
30 33 This class implements the creation of a residual map. It derives from
31 34 the ctools.cscript class which provides support for parameter files,
32 35 command line arguments, and logging. In that way the Python script
33 36 behaves just as a regular ctool.
34 37 """
  38 +
  39 + # Constructors and destructors
35 40 def __init__(self, *argv):
36 41 """
37 42 Constructor.
38 43 """
39 44 # Set name
40   - self.name = "csresmap"
41   - self.version = "1.1.0"
42   -
43   - # Initialise some members
44   - self.obs = None
45   - self.algorithm = "SUB"
46   - self.resmap = None
47   - self.m_modcube = "None"
48   - self.m_outmap = "None"
49   - self.m_use_maps = False
50   - self.m_skip_binning = False
51   - self.m_publish = False
52   -
53   - # Initialise some members
  45 + self._name = "csresmap"
  46 + self._version = "1.1.0"
  47 +
  48 + # Initialise class members
  49 + self._algorithm = "SUB"
  50 + self._resmap = None
  51 + self._modcube = "NONE"
  52 + self._outmap = "NONE"
  53 + self._use_maps = False
  54 + self._skip_binning = False
  55 + self._publish = False
  56 + self._outfile = ""
  57 + self._use_maps = False
  58 + self._skip_binning = False
  59 + self._edisp = False
  60 + self._xref = 83.63
  61 + self._yref = 22.01
  62 + self._emin = 0.1
  63 + self._emax = 100.0
  64 + self._enumbins = 0
  65 + self._ebinalg = "LOG"
  66 + self._coordsys = "CEL"
  67 + self._proj = "CAR"
  68 + self._nxpix = 200
  69 + self._nypix = 200
  70 + self._binsz = 0.02
  71 + self._publish = False
  72 + self._chatter = 2
  73 + self._clobber = True
  74 + self._debug = False
  75 + self._log_clients = False
  76 +
  77 + # Initialise observation
54 78 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
55   - self.obs = argv[0]
56   - argv = argv[1:]
  79 + self._obs = argv[0]
  80 + argv = argv[1:]
57 81 else:
58   - self.obs = gammalib.GObservations()
59   - self.obs.clear()
60   - self.outfile = ""
61   -
62   - # Make sure that parfile exists
63   - file = self.parfile()
  82 + self._obs = gammalib.GObservations()
64 83  
65 84 # Initialise application
66 85 if len(argv) == 0:
67   - ctools.cscript.__init__(self, self.name, self.version)
  86 + ctools.cscript.__init__(self, self._name, self._version)
68 87 elif len(argv) ==1:
69   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  88 + ctools.cscript.__init__(self, self._name, self._version, *argv)
70 89 else:
71 90 raise TypeError("Invalid number of arguments given.")
72 91  
73 92 # Set logger properties
74   - self.log_header()
75   - self.log.date(True)
  93 + self._log_header()
  94 + self._log.date(True)
76 95  
77 96 # Return
78 97 return
... ... @@ -81,271 +100,148 @@ class csresmap(ctools.cscript):
81 100 """
82 101 Destructor.
83 102 """
84   - # Write separator into logger
85   - if self.logTerse():
86   - self.log("\n")
87   -
88 103 # Return
89 104 return
90 105  
91   - def parfile(self):
92   - """
93   - Check if parfile exists. If parfile does not exist then create a
94   - default parfile. This kluge avoids shipping the cscript with a parfile.
95   - """
96   - # Set parfile name
97   - parfile = self.name+".par"
98   -
99   - try:
100   - pars = gammalib.GApplicationPars(parfile)
101   - except:
102   - # Signal if parfile was not found
103   - sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n")
104   -
105   - # Create default parfile
106   - pars = gammalib.GApplicationPars()
107   - pars.append(gammalib.GApplicationPar("inobs","f","a","events.fits","","","Input event list, counts cube, or observation definition XML file"))
108   - pars.append(gammalib.GApplicationPar("modcube","f","a","NONE","","","Input model cube file (generated with ctmodel)"))
109   - pars.append(gammalib.GApplicationPar("expcube","f","a","NONE","","","Input exposure cube file (only needed for stacked analysis)"))
110   - pars.append(gammalib.GApplicationPar("psfcube","f","a","NONE","","","Input PSF cube file (only needed for stacked analysis)"))
111   - pars.append(gammalib.GApplicationPar("edispcube","f","a","NONE","","","Input energy dispersion cube file (only needed for stacked analysis)"))
112   - pars.append(gammalib.GApplicationPar("bkgcube","f","a","NONE","","","Input background cube file (only needed for stacked analysis)"))
113   - pars.append(gammalib.GApplicationPar("inmodel","f","a","$CTOOLS/share/models/crab.xml","","","Input model XML file"))
114   - pars.append(gammalib.GApplicationPar("caldb","s","a","prod2","","","Calibration database"))
115   - pars.append(gammalib.GApplicationPar("irf","s","a","South_0.5h","","","Instrument response function"))
116   - pars.append(gammalib.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?"))
117   - pars.append(gammalib.GApplicationPar("outmap","f","a","resmap.fits","","","Output residual map file"))
118   - pars.append(gammalib.GApplicationPar("ebinalg","s","h","LOG","LIN|LOG|FILE","","Binning algorithm"))
119   - pars.append(gammalib.GApplicationPar("emin","r","h","0.1","","","Lower energy limit (TeV)"))
120   - pars.append(gammalib.GApplicationPar("emax","r","h","100.0","","","Upper energy limit (TeV)"))
121   - pars.append(gammalib.GApplicationPar("enumbins","i","h","20","","","Number of energy bins"))
122   - pars.append(gammalib.GApplicationPar("ebinfile","f","h","NONE","","","Name of the file containing the energy bin definition"))
123   - pars.append(gammalib.GApplicationPar("coordsys","s","a","CEL","CEL|GAL","","Coordinate System"))
124   - pars.append(gammalib.GApplicationPar("proj","s","a","CAR","AIT|AZP|CAR|MER|MOL|STG|TAN","","Projection method"))
125   - pars.append(gammalib.GApplicationPar("xref","r","a","83.63","0","360","First coordinate of image center in degrees (RA or galactic l)"))
126   - pars.append(gammalib.GApplicationPar("yref","r","a","22.01","-90","90","Second coordinate of image center in degrees (DEC or galactic b)"))
127   - pars.append(gammalib.GApplicationPar("nxpix","i","a","200","","","Size of the X axis in pixels"))
128   - pars.append(gammalib.GApplicationPar("nypix","i","a","200","","","Size of the Y axis in pixels"))
129   - pars.append(gammalib.GApplicationPar("binsz","r","a","0.02","","","Pixel size (deg/pixel)"))
130   - pars.append(gammalib.GApplicationPar("algorithm","s","a","SUBDIV","SUB|SUBDIV|SUBDIVSQRT","","Residual map computation algorithm"))
131   - pars.append(gammalib.GApplicationPar("publish","b","h","no","","","Publish residual map on VO Hub?"))
132   - pars.append_standard()
133   - pars.append(gammalib.GApplicationPar("logfile","f","h","csresmap.log","","","Log filename"))
134   - pars.save(parfile)
135   -
136   - # Return
137   - return
138 106  
139   - def get_parameters(self):
  107 + # Private methods
  108 + def _get_parameters(self):
140 109 """
141 110 Get parameters from parfile and setup the observation.
142 111 """
143 112 # Initialise some flags
144   - self.m_use_maps = False
145   - self.m_skip_binning = False
  113 + self._use_maps = False
  114 + self._skip_binning = False
146 115  
147 116 # First check if the inobs parameter is a counts cube
148   - if self.obs.size() == 0 and self["inobs"].filename() != "NONE":
  117 + if self._obs.size() == 0 and self["inobs"].filename() != "NONE":
149 118 filename = gammalib.GFilename(self["inobs"].filename())
150 119 if filename.is_fits():
151 120 cta = gammalib.GCTAObservation()
152 121 cta.load(filename)
153 122 if cta.eventtype() == "CountsCube":
154   - self.m_skip_binning = True
  123 + self._skip_binning = True
155 124  
156 125 # If we have a counts cube, then ask whether we also have a model
157   - if self.m_skip_binning:
158   - self.m_modcube = self["modcube"].filename()
159   - if self.m_modcube != "NONE":
160   - self.m_use_maps = True
  126 + if self._skip_binning:
  127 + self._modcube = self["modcube"].filename()
  128 + if self._modcube != "NONE":
  129 + self._use_maps = True
161 130  
162 131 # If not two maps are given, proceed to set up observation
163   - if not self.m_use_maps:
  132 + if not self._use_maps:
164 133  
165 134 # Set observation if not done before
166   - if self.obs.size() == 0:
  135 + if self._obs.size() == 0:
167 136 self._require_inobs("csresmap.get_parameters()")
168   - self.obs = self._get_observations()
  137 + self._obs = self._get_observations()
169 138  
170 139 # Check if we have exactly one binned CTA observation
171   - if self.obs.size() == 1:
  140 + if self._obs.size() == 1:
172 141  
173   - if self.obs[0].classname() == "GCTAObservation":
174   - if self.obs[0].eventtype() == "CountsCube":
  142 + if self._obs[0].classname() == "GCTAObservation":
  143 + if self._obs[0].eventtype() == "CountsCube":
175 144 # Skip ctbin step later on
176   - self.m_skip_binning = True
  145 + self._skip_binning = True
177 146  
178 147 # Set models if we have none
179   - if self.obs.models().size() == 0:
180   - self.obs.models(self["inmodel"].filename())
  148 + if self._obs.models().size() == 0:
  149 + self._obs.models(self["inmodel"].filename())
181 150  
182 151 # Skip query for spatial parameters if a binning is provided in the observation
183   - if not self.m_skip_binning:
  152 + if not self._skip_binning:
184 153 # Read other parameters
185   - self.m_xref = self["xref"].real()
186   - self.m_yref = self["yref"].real()
187   - self.m_emin = self["emin"].real()
188   - self.m_emax = self["emax"].real()
189   - self.m_enumbins = self["enumbins"].integer()
190   - self.m_ebinalg = self["ebinalg"].string()
191   - self.m_coordsys = self["coordsys"].string()
192   - self.m_proj = self["proj"].string()
193   - self.m_nxpix = self["nxpix"].integer()
194   - self.m_nypix = self["nypix"].integer()
195   - self.m_binsz = self["binsz"].real()
  154 + self._xref = self["xref"].real()
  155 + self._yref = self["yref"].real()
  156 + self._emin = self["emin"].real()
  157 + self._emax = self["emax"].real()
  158 + self._enumbins = self["enumbins"].integer()
  159 + self._ebinalg = self["ebinalg"].string()
  160 + self._coordsys = self["coordsys"].string()
  161 + self._proj = self["proj"].string()
  162 + self._nxpix = self["nxpix"].integer()
  163 + self._nypix = self["nypix"].integer()
  164 + self._binsz = self["binsz"].real()
196 165  
197 166 # Read energy dispersion flag
198   - self.m_edisp = self["edisp"].boolean()
  167 + self._edisp = self["edisp"].boolean()
199 168  
200 169 # Read algorithm
201   - self.m_algorithm = self["algorithm"].string()
  170 + self._algorithm = self["algorithm"].string()
202 171  
203 172 # Read standard parameters
204   - self.m_publish = self["publish"].boolean()
205   - self.m_log = False # Logging in client tools
206   - self.m_chatter = self["chatter"].integer()
207   - self.m_clobber = self["clobber"].boolean()
208   - self.m_debug = self["debug"].boolean()
  173 + self._publish = self["publish"].boolean()
  174 + self._chatter = self["chatter"].integer()
  175 + self._clobber = self["clobber"].boolean()
  176 + self._debug = self["debug"].boolean()
209 177  
210 178 # Read ahead output parameters
211 179 if (self._read_ahead()):
212   - self.m_outmap = self["outmap"].filename()
213   -
214   - # Return
215   - return
216   -
217   - def models(self, models):
218   - """
219   - Set model.
220   - """
221   - # Copy models
222   - self.obs.models(models.clone())
223   -
224   - # Return
225   - return
226   -
227   - def execute(self):
228   - """
229   - Execute the script.
230   - """
231   - # Read ahead output parameters
232   - self._read_ahead(True)
233   -
234   - # Run the script
235   - self.run()
236   -
237   - # Save residual map
238   - self.save()
  180 + self._outmap = self["outmap"].filename()
239 181  
240   - # Return
241   - return
242   -
243   - def save(self):
244   - """
245   - Save residual map.
246   - """
247   - # Write header
248   - if self.logTerse():
249   - self.log("\n")
250   - self.log.header1("Save residual map")
251   -
252   - # Continue only filename and residual map are valid
253   - if self.m_outmap != "None" and self.resmap != None:
254   -
255   - # Log file name
256   - if self.logTerse():
257   - self.log("Save residual map into \""+self.m_outmap+"\".\n")
258   -
259   - # Save residual map
260   - self.resmap.save(self.m_outmap, self.m_clobber)
  182 + # Write input parameters into logger
  183 + if self._logTerse():
  184 + self._log_parameters()
  185 + self._log("\n")
261 186  
262 187 # Return
263 188 return
264 189  
265   - def publish(self, name=""):
266   - """
267   - Publish residual map.
268   - """
269   - # Write header
270   - if self.logTerse():
271   - self.log("\n")
272   - self.log.header1("Publish sky map")
273   -
274   - # Continue only if residual map is valid
275   - if self.resmap != None:
276   -
277   - # Set default name is user name is empty
278   - if not name:
279   - user_name = self.name
280   - else:
281   - user_name = name
282   -
283   - # Log map name
284   - if self.logTerse():
285   - self.log("Publish residual map \""+user_name+"\".\n")
286   -
287   - # Publish map
288   - self.resmap.publish(user_name)
289   -
290   - # Return
291   - return
292 190  
  191 + # Public methods
293 192 def run(self):
294 193 """
295 194 Run the script.
296 195 """
297 196 # Switch screen logging on in debug mode
298   - if self.logDebug():
299   - self.log.cout(True)
  197 + if self._logDebug():
  198 + self._log.cout(True)
300 199  
301 200 # Get parameters
302   - self.get_parameters()
303   -
304   - # Write input parameters into logger
305   - if self.logTerse():
306   - self.log_parameters()
307   - self.log("\n")
  201 + self._get_parameters()
308 202  
309 203 # Write observation into logger
310   - if self.logTerse():
311   - self.log("\n")
312   - self.log.header1("Observation")
313   - self.log(str(self.obs))
314   - self.log("\n")
  204 + if self._logTerse():
  205 + self._log("\n")
  206 + self._log.header1("Observation")
  207 + self._log(str(self._obs))
  208 + self._log("\n")
315 209  
316 210 # Use input file directly if given
317   - if self.m_use_maps:
  211 + if self._use_maps:
318 212 countmap = gammalib.GSkyMap(self["inobs"].filename())
319   - modelmap = gammalib.GSkyMap(self.m_modcube)
  213 + modelmap = gammalib.GSkyMap(self._modcube)
320 214  
321 215 else:
322 216  
323   - if self.m_skip_binning:
324   - cta_counts_cube = gammalib.GCTAEventCube(self.obs[0].events().clone())
  217 + # ...
  218 + if self._skip_binning:
  219 + cta_counts_cube = gammalib.GCTAEventCube(self._obs[0].events().clone())
325 220  
  221 + # ...
326 222 else:
327 223  
328 224 # Write header
329   - if self.logTerse():
330   - self.log("\n")
331   - self.log.header1("Generate binned map (ctbin)")
  225 + if self._logTerse():
  226 + self._log("\n")
  227 + self._log.header1("Generate binned map (ctbin)")
332 228  
333 229 # Create countsmap
334   - bin = ctools.ctbin(self.obs)
335   - bin["nxpix"].integer(self.m_nxpix)
336   - bin["nypix"].integer(self.m_nypix)
337   - bin["proj"].string(self.m_proj)
338   - bin["coordsys"].string(self.m_coordsys)
339   - bin["xref"].real(self.m_xref)
340   - bin["yref"].real(self.m_yref)
341   - bin["enumbins"].integer(self.m_enumbins)
342   - bin["ebinalg"].string(self.m_ebinalg)
343   - bin["emin"].real(self.m_emin)
344   - bin["emax"].real(self.m_emax)
345   - bin["binsz"].real(self.m_binsz)
346   - bin["chatter"].integer(self.m_chatter)
347   - bin["clobber"].boolean(self.m_clobber)
348   - bin["debug"].boolean(self.m_debug)
  230 + bin = ctools.ctbin(self._obs)
  231 + bin["nxpix"].integer(self._nxpix)
  232 + bin["nypix"].integer(self._nypix)
  233 + bin["proj"].string(self._proj)
  234 + bin["coordsys"].string(self._coordsys)
  235 + bin["xref"].real(self._xref)
  236 + bin["yref"].real(self._yref)
  237 + bin["enumbins"].integer(self._enumbins)
  238 + bin["ebinalg"].string(self._ebinalg)
  239 + bin["emin"].real(self._emin)
  240 + bin["emax"].real(self._emax)
  241 + bin["binsz"].real(self._binsz)
  242 + bin["chatter"].integer(self._chatter)
  243 + bin["clobber"].boolean(self._clobber)
  244 + bin["debug"].boolean(self._debug)
349 245 bin.run()
350 246  
351 247 # Retrieve counts cube
... ... @@ -355,17 +251,17 @@ class csresmap(ctools.cscript):
355 251 countmap = cta_counts_cube.map()
356 252  
357 253 # Write header
358   - if self.logTerse():
359   - self.log("\n")
360   - self.log.header1("Generate model map (ctmodel)")
  254 + if self._logTerse():
  255 + self._log("\n")
  256 + self._log.header1("Generate model map (ctmodel)")
361 257  
362 258 # Create model map
363   - model = ctools.ctmodel(self.obs)
  259 + model = ctools.ctmodel(self._obs)
364 260 model.cube(cta_counts_cube)
365   - model["chatter"].integer(self.m_chatter)
366   - model["clobber"].boolean(self.m_clobber)
367   - model["debug"].boolean(self.m_debug)
368   - model["edisp"].boolean(self.m_edisp)
  261 + model["chatter"].integer(self._chatter)
  262 + model["clobber"].boolean(self._clobber)
  263 + model["debug"].boolean(self._debug)
  264 + model["edisp"].boolean(self._edisp)
369 265 model.run()
370 266  
371 267 # Get model map into GSkyMap object
... ... @@ -376,61 +272,141 @@ class csresmap(ctools.cscript):
376 272 # to be a SWIG feature as SWIG creates a new object when
377 273 # calling bin.cube()
378 274 #residualmap = bin.cube().map()
379   - self.resmap = countmap.copy()
380   - self.resmap.stack_maps()
  275 + self._resmap = countmap.copy()
  276 + self._resmap.stack_maps()
381 277 modelmap.stack_maps()
382 278  
383 279 # Continue calculations depending on given algorithm
384   - if self.m_algorithm == "SUB":
  280 + if self._algorithm == "SUB":
385 281  
386 282 # Subtract maps
387   - self.resmap -= modelmap
  283 + self._resmap -= modelmap
388 284  
389   - elif self.m_algorithm == "SUBDIV":
  285 + elif self._algorithm == "SUBDIV":
390 286  
391 287 # Subtract and divide by model map
392   - self.resmap -= modelmap
393   - self.resmap /= modelmap
  288 + self._resmap -= modelmap
  289 + self._resmap /= modelmap
394 290 #for pixel in modelmap:
395 291 # if pixel != 0.0:
396 292 # pixel = 1.0/pixel
397   - #self.resmap *= modelmap
  293 + #self._resmap *= modelmap
398 294  
399   - elif self.m_algorithm == "SUBDIVSQRT":
  295 + elif self._algorithm == "SUBDIVSQRT":
400 296  
401 297 # subtract and divide by sqrt of model map
402   - self.resmap -= modelmap
403   - self.resmap /= modelmap.sqrt()
  298 + self._resmap -= modelmap
  299 + self._resmap /= modelmap.sqrt()
404 300 #for pixel in modelmap:
405 301 # if pixel != 0.0:
406 302 # pixel = 1.0/math.sqrt(pixel)
407   - #self.resmap *= modelmap
  303 + #self._resmap *= modelmap
408 304  
409 305 else:
410 306  
411 307 # Raise error if algorithm is unkown
412   - raise TypeError("Algorithm \""+self.m_algorithm+"\" not known")
  308 + raise TypeError("Algorithm \""+self._algorithm+"\" not known")
413 309  
414 310 # Optionally publish map
415   - if self.m_publish:
  311 + if self._publish:
416 312 self.publish()
417 313  
418 314 # Return
419 315 return
420 316  
  317 + def execute(self):
  318 + """
  319 + Execute the script.
  320 + """
  321 + # Read ahead output parameters
  322 + self._read_ahead(True)
  323 +
  324 + # Run the script
  325 + self.run()
  326 +
  327 + # Save residual map
  328 + self.save()
  329 +
  330 + # Return
  331 + return
  332 +
  333 + def save(self):
  334 + """
  335 + Save residual map.
  336 + """
  337 + # Write header
  338 + if self._logTerse():
  339 + self._log("\n")
  340 + self._log.header1("Save residual map")
  341 +
  342 + # Get outmap parameter
  343 + self._outmap = self["outmap"].filename()
  344 +
  345 + # Continue only filename and residual map are valid
  346 + if self._outmap != "NONE" and self._resmap != None:
  347 +
  348 + # Log file name
  349 + if self._logTerse():
  350 + self._log("Save residual map into \""+self._outmap+"\".\n")
  351 +
  352 + # Save residual map
  353 + self._resmap.save(self._outmap, self._clobber)
  354 +
  355 + # Return
  356 + return
  357 +
  358 + def publish(self, name=""):
  359 + """
  360 + Publish residual map.
  361 +
  362 + Kwargs:
  363 + name: Name of residual map.
  364 + """
  365 + # Write header
  366 + if self._logTerse():
  367 + self._log("\n")
  368 + self._log.header1("Publish residual map")
  369 +
  370 + # Continue only if residual map is valid
  371 + if self._resmap != None:
  372 +
  373 + # Set default name is user name is empty
  374 + if not name:
  375 + user_name = self.name
  376 + else:
  377 + user_name = name
  378 +
  379 + # Log map name
  380 + if self._logTerse():
  381 + self._log("Publish residual map \""+user_name+"\".\n")
  382 +
  383 + # Publish map
  384 + self._resmap.publish(user_name)
  385 +
  386 + # Return
  387 + return
  388 +
  389 + def models(self, models):
  390 + """
  391 + Set model.
  392 + """
  393 + # Copy models
  394 + self._obs.models(models.clone())
  395 +
  396 + # Return
  397 + return
  398 +
421 399  
422 400 # ======================== #
423 401 # Main routine entry point #
424 402 # ======================== #
425 403 if __name__ == '__main__':
426   - """
427   - Generates residual count map.
428   - """
  404 +
429 405 # Create instance of application
430 406 app = csresmap(sys.argv)
431 407  
432 408 # Open logfile
433   - app.logFileOpen()
  409 + app._logFileOpen()
434 410  
435 411 # Execute application
436 412 app.execute()
... ...
cscripts/cssens.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: cssens.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the cssens script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inobs, f, h, NONE,,, "Input event list, counts cube, or observation definition XML file"
  21 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model XML file"
  22 +srcname, s, a, Crab,,, "Source name"
  23 +caldb, s, a, prod2,,, "Calibration database"
  24 +irf, s, a, South_0.5h,,, "Instrument response function"
  25 +edisp, b, h, no,,, "Apply energy dispersion?"
  26 +deadc, r, h, 0.95,0,1, "Deadtime correction factor"
  27 +outfile, f, h, sensitivity.dat,,, "Output sensitivity ASCII file"
  28 +
  29 +#
  30 +# Script parameters
  31 +#==================
  32 +offset, r, h, 0.0,,, "Source offset angle (deg)"
  33 +duration, r, a, 180000.0,,, "Effective exposure time (s)"
  34 +rad, r, a, 5.0,,, "Radius of ROI (deg)"
  35 +emin, r, a, 0.020,,, "Lower energy limit (TeV)"
  36 +emax, r, a, 200.0,,, "Upper energy limit (TeV)"
  37 +bins, i, a, 21,,, "Number of energy bins for differential sensitivity computation"
  38 +enumbins, i, h, 0,,, "Number of energy bins for binned analysis (0=unbinned)"
  39 +npix, i, h, 200,,, "Number of pixels for binned analysis"
  40 +binsz, r, h, 0.05,,, "Pixel size for binned analysis (deg/pixel)"
  41 +type, s, h, Differential,Differential|Integral,, "Sensitivity type"
  42 +sigma, r, h, 5.0,,, "Significance threshold (Gaussian sigma)"
  43 +max_iter, i, h, 50,,, "Maximum number of iterations"
  44 +num_avg, i, h, 3,,, "Number of iterations for sliding average"
  45 +
  46 +#
  47 +# Standard parameters
  48 +#====================
  49 +chatter, i, h, 2,0,4, "Chattiness of output"
  50 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  51 +debug, b, h, no,,, "Debugging mode activated"
  52 +mode, s, h, ql,,, "Mode of automatic parameters"
  53 +logfile, f, h, cspull.log,,, "Log filename"
  54 +
  55 +
... ...
cscripts/cssens.py
... ... @@ -31,6 +31,8 @@ import math
31 31 # ============ #
32 32 class cssens(ctools.cscript):
33 33 """
  34 + Computes the CTA sensitivity.
  35 +
34 36 This class computes the CTA sensitivity for a number of energy bins using
35 37 ctlike. Spectra are fitted in narrow energy bins to simulated data,
36 38 and the flux level is determined that leads to a particular significance.
... ... @@ -38,34 +40,48 @@ class cssens(ctools.cscript):
38 40 The significance is determined using the Test Statistic, defined as twice
39 41 the likelihood difference between fitting with and without the test source.
40 42 """
  43 +
  44 + # Constructors and destructors
41 45 def __init__(self, *argv):
42 46 """
43 47 Constructor.
44 48 """
45 49 # Set name
46   - self.name = "cssens"
47   - self.version = "1.0.0"
48   -
49   - # Initialise some parameters
50   - self.obs = None
51   - self.m_ra = None
52   - self.m_dec = None
53   -
54   - # Make sure that parfile exists
55   - file = self.parfile()
56   -
  50 + self._name = "cssens"
  51 + self._version = "1.1.0"
  52 +
  53 + # Initialise class members
  54 + self._obs = gammalib.GObservations()
  55 + self._ebounds = gammalib.GEbounds()
  56 + self._srcname = ""
  57 + self._outfile = ""
  58 + self._ra = None
  59 + self._dec = None
  60 + self._edisp = False
  61 + self._emin = 0.020
  62 + self._emax = 200.0
  63 + self._bins = 21
  64 + self._enumbins = 0
  65 + self._npix = 200
  66 + self._binsz = 0.05
  67 + self._type = "Differential"
  68 + self._ts_thres = 25.0
  69 + self._max_iter = 50
  70 + self._num_avg = 3
  71 + self._log_clients = False
  72 + self._debug = False
  73 +
57 74 # Initialise application
58 75 if len(argv) == 0:
59   - ctools.cscript.__init__(self, self.name, self.version)
  76 + ctools.cscript.__init__(self, self._name, self._version)
60 77 elif len(argv) ==1:
61   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  78 + ctools.cscript.__init__(self, self._name, self._version, *argv)
62 79 else:
63 80 raise TypeError("Invalid number of arguments given.")
64 81  
65 82 # Set logger properties
66   - self.logFileOpen()
67   - self.log_header()
68   - self.log.date(True)
  83 + self._log_header()
  84 + self._log.date(True)
69 85  
70 86 # Return
71 87 return
... ... @@ -74,216 +90,79 @@ class cssens(ctools.cscript):
74 90 """
75 91 Destructor.
76 92 """
77   - # Write separator into logger
78   - if self.logTerse():
79   - self.log("\n")
80   -
81 93 # Return
82 94 return
83 95  
84   - def parfile(self):
85   - """
86   - Check if parfile exists. If parfile does not exist then create a
87   - default parfile. This kluge avoids shipping the cscript with a parfile.
88   - """
89   - # Set parfile name
90   - parfile = self.name+".par"
91   -
92   - try:
93   - pars = gammalib.GApplicationPars(parfile)
94   - except:
95   - # Signal if parfile was not found
96   - print("Parfile "+parfile+" not found. Create default parfile.")
97   -
98   - # Create default parfile
99   - pars = gammalib.GApplicationPars()
100   - pars.append(gammalib.GApplicationPar("inobs","f","h","NONE","","","Input event list, counts cube, or observation definition XML file"))
101   - pars.append(gammalib.GApplicationPar("inmodel","f","a","$CTOOLS/share/models/crab.xml","","","Input model XML file"))
102   - pars.append(gammalib.GApplicationPar("srcname","s","a","Crab","","","Source name"))
103   - pars.append(gammalib.GApplicationPar("caldb","s","a","prod2","","","Calibration database"))
104   - pars.append(gammalib.GApplicationPar("irf","s","a","South_0.5h","","","Instrument response function"))
105   - pars.append(gammalib.GApplicationPar("deadc","r","h","0.95","0","1","Deadtime correction factor"))
106   - pars.append(gammalib.GApplicationPar("outfile","f","h","sensitivity.dat","","","Output sensitivity ASCII file"))
107   - pars.append(gammalib.GApplicationPar("offset","r","h","0.0","","","Source offset angle (deg)"))
108   - pars.append(gammalib.GApplicationPar("duration","r","a","180000.0","","","Effective exposure time (s)"))
109   - pars.append(gammalib.GApplicationPar("rad","r","a","5.0","","","Radius of ROI (deg)"))
110   - pars.append(gammalib.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?"))
111   - pars.append(gammalib.GApplicationPar("emin","r","a","0.020","","","Lower energy limit (TeV)"))
112   - pars.append(gammalib.GApplicationPar("emax","r","a","200.0","","","Upper energy limit (TeV)"))
113   - pars.append(gammalib.GApplicationPar("bins","i","a","21","","","Number of energy bins for differential sensitivity computation"))
114   - pars.append(gammalib.GApplicationPar("enumbins","i","h","0","","","Number of energy bins (0=unbinned)"))
115   - pars.append(gammalib.GApplicationPar("npix","i","h","200","","","Number of pixels for binned"))
116   - pars.append(gammalib.GApplicationPar("binsz","r","h","0.05","","","Pixel size for binned (deg/pixel)"))
117   - pars.append(gammalib.GApplicationPar("type","s","h","Differential","Differential|Integral","","Sensitivity type"))
118   - pars.append(gammalib.GApplicationPar("sigma","r","h","5.0","","","Significance threshold"))
119   - pars.append(gammalib.GApplicationPar("max_iter","i","h","50","","","Maximum number of iterations"))
120   - pars.append(gammalib.GApplicationPar("num_avg","i","h","3","","","Number of iterations for sliding average"))
121   - pars.append_standard()
122   - pars.append(gammalib.GApplicationPar("logfile","f","h","cssens.log","","","Log filename"))
123   - pars.save(parfile)
124   -
125   - # Return
126   - return
127 96  
128   - def get_parameters(self):
  97 + # Private methods
  98 + def _get_parameters(self):
129 99 """
130 100 Get user parameters from parfile.
131 101 """
132 102 # Set observation if not done before
133   - if self.obs == None or self.obs.size() == 0:
134   - self.obs = self.set_obs()
  103 + if self._obs == None or self._obs.size() == 0:
  104 + self._obs = self._set_obs()
135 105  
136 106 # Set models if we have none
137   - if self.obs.models().size() == 0:
138   - self.obs.models(self["inmodel"].filename())
  107 + if self._obs.models().size() == 0:
  108 + self._obs.models(self["inmodel"].filename())
139 109  
140 110 # Get source name
141   - self.m_srcname = self["srcname"].string()
  111 + self._srcname = self["srcname"].string()
142 112  
143 113 # Read further parameters
144   - self.m_outfile = self["outfile"].filename()
145   - self.m_emin = self["emin"].real()
146   - self.m_emax = self["emax"].real()
147   - self.m_bins = self["bins"].integer()
  114 + self._outfile = self["outfile"].filename()
  115 + self._emin = self["emin"].real()
  116 + self._emax = self["emax"].real()
  117 + self._bins = self["bins"].integer()
148 118  
149 119 # Read parameters for binned if requested
150   - self.m_enumbins = self["enumbins"].integer()
151   - if not self.m_enumbins == 0:
152   - self.m_npix = self["npix"].integer()
153   - self.m_binsz = self["binsz"].real()
154   - else:
155   - # Set dummy values (required by obsutils)
156   - self.m_npix = 0
157   - self.m_binsz = 0.0
  120 + self._enumbins = self["enumbins"].integer()
  121 + if not self._enumbins == 0:
  122 + self._npix = self["npix"].integer()
  123 + self._binsz = self["binsz"].real()
158 124  
159 125 # Read remaining parameters
160   - self.m_edisp = self["edisp"].boolean()
161   - self.m_ts_thres = self["sigma"].real()*self["sigma"].real()
162   - self.m_max_iter = self["max_iter"].integer()
163   - self.m_num_avg = self["num_avg"].integer()
164   - self.m_type = self["type"].string()
  126 + self._edisp = self["edisp"].boolean()
  127 + self._ts_thres = self["sigma"].real()*self["sigma"].real()
  128 + self._max_iter = self["max_iter"].integer()
  129 + self._num_avg = self["num_avg"].integer()
  130 + self._type = self["type"].string()
165 131  
166 132 # Set some fixed parameters
167   - self.m_log = False # Logging in client tools
168   - self.m_debug = self["debug"].boolean() # Debugging in client tools
  133 + self._debug = self["debug"].boolean() # Debugging in client tools
169 134  
170 135 # Derive some parameters
171   - self.m_ebounds = gammalib.GEbounds(self.m_bins,
172   - gammalib.GEnergy(self.m_emin, "TeV"),
173   - gammalib.GEnergy(self.m_emax, "TeV"))
  136 + self._ebounds = gammalib.GEbounds(self._bins,
  137 + gammalib.GEnergy(self._emin, "TeV"),
  138 + gammalib.GEnergy(self._emax, "TeV"))
174 139  
175   - # Return
176   - return
177   -
178   - def execute(self):
179   - """
180   - Execute the script.
181   - """
182   - # Run the script
183   - self.run()
  140 + # Write input parameters into logger
  141 + if self._logTerse():
  142 + self._log_parameters()
  143 + self._log("\n")
184 144  
185 145 # Return
186 146 return
187 147  
188   - def run(self):
189   - """
190   - Run the script.
  148 + def _set_obs(self, lpnt=0.0, bpnt=0.0, emin=0.1, emax=100.0):
191 149 """
192   - # Switch screen logging on in debug mode
193   - if self.logDebug():
194   - self.log.cout(True)
195   -
196   - # Get parameters
197   - self.get_parameters()
198   -
199   - # Write input parameters into logger
200   - if self.logTerse():
201   - self.log_parameters()
202   - self.log("\n")
203   -
204   - # Initialise script
205   - colnames = ['loge', 'emin', 'emax', 'crab_flux', 'photon_flux',
206   - 'energy_flux', 'sensitivity']
207   - results = []
208   -
209   - # Initialise models
210   - full_model, bkg_model = self.set_models()
211   -
212   - # Write models into logger
213   - if self.logTerse():
214   - self.log("\n")
215   - self.log.header1("Models")
216   - self.log.header2("Background model")
217   - self.log(str(bkg_model))
218   - self.log("\n\n")
219   - self.log.header2("Full model")
220   - self.log(str(full_model))
221   - self.log("\n")
222   -
223   - # Write heder
224   - if self.logTerse():
225   - self.log("\n")
226   - self.log.header1("Sensitivity determination")
227   - self.log.parformat("Type")
228   - self.log(self.m_type)
229   - self.log("\n")
230   -
231   - # Loop over energy bins
232   - for ieng in range(self.m_ebounds.size()):
233   -
234   - # Set energies
235   - if self.m_type == "Differential":
236   - emin = self.m_ebounds.emin(ieng)
237   - emax = self.m_ebounds.emax(ieng)
238   - elif self.m_type == "Integral":
239   - emin = self.m_ebounds.emin(ieng)
240   - emax = self.m_ebounds.emax()
241   - else:
242   - msg = "Invalid sensitivity type \""+self.m_type+"\" encountered."+ \
243   - " Either use \"Differential\" or \"Integral\"."
244   - raise RuntimeError(msg)
  150 + Set an observation container.
245 151  
246   - # Determine sensitivity
247   - result = self.get_sensitivity(self.obs, emin, emax,
248   - bkg_model, full_model)
  152 + Kwargs:
  153 + lpnt: Galactic longitude of pointing [deg] (default: 0.0)
  154 + bpnt: Galactic latitude of pointing [deg] (default: 0.0)
  155 + emin: Minimum energy [TeV] (default: 0.1)
  156 + emax: Maximum energy [TeV] (default: 100.0)
249 157  
250   - # Write results
251   - if ieng == 0:
252   - f = open(self.m_outfile.url(), 'w')
253   - writer = csv.DictWriter(f, colnames)
254   - headers = {}
255   - for n in colnames:
256   - headers[n] = n
257   - writer.writerow(headers)
258   - #writer.writerow(dict((_,_) for _ in colnames))
259   - writer.writerow(result)
260   - f.close()
261   - else:
262   - f = open(self.m_outfile.url(), 'a')
263   - writer = csv.DictWriter(f, colnames)
264   - writer.writerow(result)
265   - f.close()
266   -
267   - # Append results
268   - results.append(result)
269   -
270   - # Return
271   - return
272   -
273   - def set_obs(self, lpnt=0.0, bpnt=0.0, emin=0.1, emax=100.0):
  158 + Returns:
  159 + Observation container.
274 160 """
275   - Returns an observation container.
276   -
277   - Keywords:
278   - lpnt - Galactic longitude of pointing [deg] (default: 0.0)
279   - bpnt - Galactic latitude of pointing [deg] (default: 0.0)
280   - emin - Minimum energy [TeV] (default: 0.1)
281   - emax - Maximum energy [TeV] (default: 100.0)
282   - """
283   - # If an observation was provided on input then load it from XML file
  161 + # If an observation was provided on input then load it from XML
  162 + # file
284 163 filename = self["inobs"].filename()
285 164 if filename != "NONE" and filename != "":
286   - obs = self.get_observations()
  165 + obs = self._get_observations()
287 166  
288 167 # ... otherwise allocate a single observation
289 168 else:
... ... @@ -311,56 +190,46 @@ class cssens(ctools.cscript):
311 190 obs.append(run)
312 191  
313 192 # Set source position
314   - offset = self["offset"].real()
  193 + offset = self["offset"].real()
315 194 pntdir.lb_deg(lpnt, bpnt+offset)
316   - self.m_ra = pntdir.ra_deg()
317   - self.m_dec = pntdir.dec_deg()
  195 + self._ra = pntdir.ra_deg()
  196 + self._dec = pntdir.dec_deg()
318 197  
319 198 # Return observation container
320 199 return obs
321 200  
322   - def set_obs_ebounds(self, emin, emax):
323   - """
324   - Set energy boundaries for observation in container.
325   -
326   - Parameters:
327   - emin - Minimum energy
328   - emax - Maximum energy
  201 + def _set_models(self, fitspat=False, fitspec=False):
329 202 """
330   - # Loop over all observations in container
331   - for obs in self.obs:
332   -
333   - # Set energy boundaries
334   - ebounds = gammalib.GEbounds(emin, emax)
335   - obs.events().ebounds(ebounds)
  203 + Set full model and background model.
336 204  
337   - # Return
338   - return
  205 + Kwargs:
  206 + fitspat: Fit spatial parameter (default: False).
  207 + fitspec: Fit spectral parameters (default: False).
339 208  
340   - def set_models(self, fitpos=False, fitspec=False):
341   - """
342   - Set full and background model.
  209 + Returns:
  210 + Tuple containing full model and background model.
343 211 """
344 212 # Retrieve full model from observation container
345   - full_model = self.obs.models().copy()
  213 + full_model = self._obs.models().copy()
346 214  
347 215 # Get source model
348   - model = full_model[self.m_srcname]
  216 + model = full_model[self._srcname]
349 217  
350 218 # Check that model has a Prefactor
351 219 if not model.has_par("Prefactor"):
352   - msg = "Model \""+self.m_srcname+"\" has no parameter \"Prefactor\"."+ \
353   - " Only spectral models with a \"Prefactor\" parameter are supported."
  220 + msg = "Model \""+self._srcname+"\" has no parameter "+\
  221 + "\"Prefactor\". Only spectral models with a "+\
  222 + "\"Prefactor\" parameter are supported."
354 223 raise RuntimeError(msg)
355 224  
356 225 # Set source position
357   - if self.m_ra != None and self.m_dec != None:
  226 + if self._ra != None and self._dec != None:
358 227 if model.has_par("RA") and model.has_par("DEC"):
359   - model["RA"].value(self.m_ra)
360   - model["DEC"].value(self.m_dec)
  228 + model["RA"].value(self._ra)
  229 + model["DEC"].value(self._dec)
361 230  
362 231 # Fit or fix spatial parameters
363   - if fitpos:
  232 + if fitspat:
364 233 if model.has_par("RA"):
365 234 model["RA"].free()
366 235 if model.has_par("DEC"):
... ... @@ -397,27 +266,71 @@ class cssens(ctools.cscript):
397 266  
398 267 # Create background model
399 268 bkg_model = full_model.copy()
400   - bkg_model.remove(self.m_srcname)
  269 + bkg_model.remove(self._srcname)
401 270  
402 271 # Return models
403 272 return full_model, bkg_model
404 273  
405   - def get_sensitivity(self, obs, emin, emax, bkg_model, full_model):
  274 + def _set_obs_ebounds(self, emin, emax):
  275 + """
  276 + Set energy boundaries for observation container.
  277 +
  278 + Sets the energy boundaries for all observations in the observation
  279 + container.
  280 +
  281 + Args:
  282 + emin: Minimum energy
  283 + emax: Maximum energy
  284 + """
  285 + # Loop over all observations in container
  286 + for obs in self._obs:
  287 +
  288 + # Set energy boundaries
  289 + ebounds = gammalib.GEbounds(emin, emax)
  290 + obs.events().ebounds(ebounds)
  291 +
  292 + # Return
  293 + return
  294 +
  295 + def _get_crab_flux(self, emin, emax):
  296 + """
  297 + Return Crab photon flux in a given energy interval.
  298 +
  299 + Args:
  300 + emin: Minimum energy
  301 + emax: Maximum energy
  302 +
  303 + Returns:
  304 + Crab photon flux in specified energy interval.
  305 + """
  306 + # Set Crab TeV spectral model based on a power law
  307 + crab = gammalib.GModelSpectralPlaw(5.7e-16, -2.48,
  308 + gammalib.GEnergy(0.3, "TeV"))
  309 +
  310 + # Determine flux
  311 + flux = crab.flux(emin, emax)
  312 +
  313 + # Return flux
  314 + return flux
  315 +
  316 + def _get_sensitivity(self, emin, emax, bkg_model, full_model):
406 317 """
407 318 Determine sensitivity for given observations.
408 319  
409   - Parameters:
410   - obs - Observation container
411   - emin - Minimum energy for fitting and flux computation
412   - emax - Maximum energy for fitting and flux computation
413   - bkg_model - Background model
414   - full_model - Source model
  320 + Args:
  321 + emin: Minimum energy for fitting and flux computation
  322 + emax: Maximum energy for fitting and flux computation
  323 + bkg_model: Background model
  324 + full_model: Source model
  325 +
  326 + Returns:
  327 + Result dictionary
415 328 """
416 329 # Set TeV->erg conversion factor
417 330 tev2erg = 1.6021764
418 331  
419 332 # Set energy boundaries
420   - self.set_obs_ebounds(emin, emax)
  333 + self._set_obs_ebounds(emin, emax)
421 334  
422 335 # Determine energy boundaries from first observation in the container
423 336 loge = math.log10(math.sqrt(emin.TeV()*emax.TeV()))
... ... @@ -426,23 +339,23 @@ class cssens(ctools.cscript):
426 339  
427 340 # Compute Crab unit (this is the factor with which the Prefactor needs
428 341 # to be multiplied to get 1 Crab
429   - crab_flux = self.get_crab_flux(emin, emax)
430   - src_flux = full_model[self.m_srcname].spectral().flux(emin, emax)
  342 + crab_flux = self._get_crab_flux(emin, emax)
  343 + src_flux = full_model[self._srcname].spectral().flux(emin, emax)
431 344 crab_unit = crab_flux/src_flux
432 345  
433 346 # Write header
434   - if self.logTerse():
435   - self.log("\n")
436   - self.log.header2("Energies: "+str(emin)+" - "+str(emax))
437   - self.log.parformat("Crab flux")
438   - self.log(crab_flux)
439   - self.log(" ph/cm2/s\n")
440   - self.log.parformat("Source model flux")
441   - self.log(src_flux)
442   - self.log(" ph/cm2/s\n")
443   - self.log.parformat("Crab unit factor")
444   - self.log(crab_unit)
445   - self.log("\n")
  347 + if self._logTerse():
  348 + self._log("\n")
  349 + self._log.header2("Energies: "+str(emin)+" - "+str(emax))
  350 + self._log.parformat("Crab flux")
  351 + self._log(crab_flux)
  352 + self._log(" ph/cm2/s\n")
  353 + self._log.parformat("Source model flux")
  354 + self._log(src_flux)
  355 + self._log(" ph/cm2/s\n")
  356 + self._log.parformat("Crab unit factor")
  357 + self._log(crab_unit)
  358 + self._log("\n")
446 359  
447 360 # Initialise loop
448 361 crab_flux_value = []
... ... @@ -459,20 +372,23 @@ class cssens(ctools.cscript):
459 372 iter += 1
460 373  
461 374 # Write header
462   - if self.logExplicit():
463   - self.log.header2("Iteration "+str(iter))
  375 + if self._logExplicit():
  376 + self._log.header2("Iteration "+str(iter))
464 377  
465 378 # Set source model. crab_prefactor is the Prefactor that
466 379 # corresponds to 1 Crab
467 380 src_model = full_model.copy()
468   - crab_prefactor = src_model[self.m_srcname]['Prefactor'].value() * crab_unit
469   - src_model[self.m_srcname]['Prefactor'].value(crab_prefactor * test_crab_flux)
470   - obs.models(src_model)
  381 + crab_prefactor = src_model[self._srcname]['Prefactor'].value() * \
  382 + crab_unit
  383 + src_model[self._srcname]['Prefactor'].value(crab_prefactor * \
  384 + test_crab_flux)
  385 + self._obs.models(src_model)
471 386  
472 387 # Simulate events
473   - sim = obsutils.sim(obs, nbins=self.m_enumbins, seed=iter,
474   - binsz=self.m_binsz, npix=self.m_npix,
475   - log=self.m_log, debug=self.m_debug, edisp=self.m_edisp)
  388 + sim = obsutils.sim(self._obs, nbins=self._enumbins, seed=iter,
  389 + binsz=self._binsz, npix=self._npix,
  390 + log=self._log_clients, debug=self._debug,
  391 + edisp=self._edisp)
476 392  
477 393 # Determine number of events in simulation
478 394 nevents = 0.0
... ... @@ -480,15 +396,16 @@ class cssens(ctools.cscript):
480 396 nevents += run.events().number()
481 397  
482 398 # Write simulation results
483   - if self.logExplicit():
484   - self.log.header3("Simulation")
485   - self.log.parformat("Number of simulated events")
486   - self.log(nevents)
487   - self.log("\n")
  399 + if self._logExplicit():
  400 + self._log.header3("Simulation")
  401 + self._log.parformat("Number of simulated events")
  402 + self._log(nevents)
  403 + self._log("\n")
488 404  
489 405 # Fit background only
490 406 sim.models(bkg_model)
491   - like = obsutils.fit(sim, log=self.m_log, debug=self.m_debug, edisp=self.m_edisp)
  407 + like = obsutils.fit(sim, log=self._log_clients,
  408 + debug=self._debug, edisp=self._edisp)
492 409 result_bgm = like.obs().models().copy()
493 410 LogL_bgm = like.opt().value()
494 411 npred_bgm = like.obs().npred()
... ... @@ -497,36 +414,31 @@ class cssens(ctools.cscript):
497 414 quality_bgm = npred_bgm-nevents
498 415  
499 416 # Write background fit results
500   - if self.logExplicit():
501   - self.log.header3("Background model fit")
502   - self.log.parformat("log likelihood")
503   - self.log(LogL_bgm)
504   - self.log("\n")
505   - self.log.parformat("Number of predicted events")
506   - self.log(npred_bgm)
507   - self.log("\n")
508   - self.log.parformat("Fit quality")
509   - self.log(quality_bgm)
510   - self.log("\n")
511   -
512   - # Start over if the fit quality was bad
513   - #if abs(quality_bgm) > 3.0:
514   - # if self.logExplicit():
515   - # self.log("Fit quality outside required range. Start over.\n")
516   - # continue
  417 + if self._logExplicit():
  418 + self._log.header3("Background model fit")
  419 + self._log.parformat("log likelihood")
  420 + self._log(LogL_bgm)
  421 + self._log("\n")
  422 + self._log.parformat("Number of predicted events")
  423 + self._log(npred_bgm)
  424 + self._log("\n")
  425 + self._log.parformat("Fit quality")
  426 + self._log(quality_bgm)
  427 + self._log("\n")
517 428  
518 429 # Write model fit results
519   - if self.logExplicit():
  430 + if self._logExplicit():
520 431 for model in result_bgm:
521   - self.log.parformat("Model")
522   - self.log(model.name())
523   - self.log("\n")
  432 + self._log.parformat("Model")
  433 + self._log(model.name())
  434 + self._log("\n")
524 435 for par in model:
525   - self.log(str(par)+"\n")
  436 + self._log(str(par)+"\n")
526 437  
527 438 # Fit background and test source
528 439 sim.models(src_model)
529   - like = obsutils.fit(sim, log=self.m_log, debug=self.m_debug, edisp=self.m_edisp)
  440 + like = obsutils.fit(sim, log=self._log_clients,
  441 + debug=self._debug, edisp=self._edisp)
530 442 result_all = like.obs().models().copy()
531 443 LogL_all = like.opt().value()
532 444 npred_all = like.obs().npred()
... ... @@ -536,56 +448,52 @@ class cssens(ctools.cscript):
536 448 quality_all = npred_all-nevents
537 449  
538 450 # Write background and test source fit results
539   - if self.logExplicit():
540   - self.log.header3("Background and test source model fit")
541   - self.log.parformat("Test statistics")
542   - self.log(ts)
543   - self.log("\n")
544   - self.log.parformat("log likelihood")
545   - self.log(LogL_all)
546   - self.log("\n")
547   - self.log.parformat("Number of predicted events")
548   - self.log(npred_all)
549   - self.log("\n")
550   - self.log.parformat("Fit quality")
551   - self.log(quality_all)
552   - self.log("\n")
  451 + if self._logExplicit():
  452 + self._log.header3("Background and test source model fit")
  453 + self._log.parformat("Test statistics")
  454 + self._log(ts)
  455 + self._log("\n")
  456 + self._log.parformat("log likelihood")
  457 + self._log(LogL_all)
  458 + self._log("\n")
  459 + self._log.parformat("Number of predicted events")
  460 + self._log(npred_all)
  461 + self._log("\n")
  462 + self._log.parformat("Fit quality")
  463 + self._log(quality_all)
  464 + self._log("\n")
553 465 #
554 466 for model in result_all:
555   - self.log.parformat("Model")
556   - self.log(model.name())
557   - self.log("\n")
  467 + self._log.parformat("Model")
  468 + self._log(model.name())
  469 + self._log("\n")
558 470 for par in model:
559   - self.log(str(par)+"\n")
560   -
561   - # Start over if the fit quality was bad
562   - #if abs(quality_all) > 3.0:
563   - # if self.logExplicit():
564   - # self.log("Fit quality outside required range. Start over.\n")
565   - # continue
  471 + self._log(str(par)+"\n")
566 472  
567 473 # Start over if TS was non-positive
568 474 if ts <= 0.0:
569   - if self.logExplicit():
570   - self.log("Non positive TS. Start over.\n")
  475 + if self._logExplicit():
  476 + self._log("Non positive TS. Start over.\n")
571 477 continue
572 478  
573 479 # Get fitted Crab, photon and energy fluxes
574   - crab_flux = result_all[self.m_srcname]['Prefactor'].value() / crab_prefactor
575   - crab_flux_err = result_all[self.m_srcname]['Prefactor'].error() / crab_prefactor
576   - photon_flux = result_all[self.m_srcname].spectral().flux(emin, emax)
577   - energy_flux = result_all[self.m_srcname].spectral().eflux(emin, emax)
  480 + crab_flux = result_all[self._srcname]['Prefactor'].value() / \
  481 + crab_prefactor
  482 + crab_flux_err = result_all[self._srcname]['Prefactor'].error() / \
  483 + crab_prefactor
  484 + photon_flux = result_all[self._srcname].spectral().flux(emin, emax)
  485 + energy_flux = result_all[self._srcname].spectral().eflux(emin, emax)
578 486  
579 487 # Compute differential sensitivity in unit erg/cm2/s
580 488 energy = gammalib.GEnergy(e_mean, "TeV")
581 489 time = gammalib.GTime()
582   - sensitivity = result_all[self.m_srcname].spectral().eval(energy, time) * \
  490 + sensitivity = result_all[self._srcname].spectral().eval(energy, time) * \
583 491 e_mean*erg_mean * 1.0e6
584 492  
585 493 # Compute flux correction factor based on average TS
586 494 correct = 1.0
587 495 if ts > 0:
588   - correct = math.sqrt(self.m_ts_thres/ts)
  496 + correct = math.sqrt(self._ts_thres/ts)
589 497  
590 498 # Compute extrapolated fluxes
591 499 crab_flux = correct * crab_flux
... ... @@ -598,41 +506,41 @@ class cssens(ctools.cscript):
598 506 sensitivity_value.append(sensitivity)
599 507  
600 508 # Write background and test source fit results
601   - if self.logExplicit():
602   - self.log.parformat("Photon flux")
603   - self.log(photon_flux)
604   - self.log(" ph/cm2/s\n")
605   - self.log.parformat("Energy flux")
606   - self.log(energy_flux)
607   - self.log(" erg/cm2/s\n")
608   - self.log.parformat("Crab flux")
609   - self.log(crab_flux*1000.0)
610   - self.log(" mCrab\n")
611   - self.log.parformat("Differential sensitivity")
612   - self.log(sensitivity)
613   - self.log(" erg/cm2/s\n")
  509 + if self._logExplicit():
  510 + self._log.parformat("Photon flux")
  511 + self._log(photon_flux)
  512 + self._log(" ph/cm2/s\n")
  513 + self._log.parformat("Energy flux")
  514 + self._log(energy_flux)
  515 + self._log(" erg/cm2/s\n")
  516 + self._log.parformat("Crab flux")
  517 + self._log(crab_flux*1000.0)
  518 + self._log(" mCrab\n")
  519 + self._log.parformat("Differential sensitivity")
  520 + self._log(sensitivity)
  521 + self._log(" erg/cm2/s\n")
614 522 for model in result_all:
615   - self.log.parformat("Model")
616   - self.log(model.name())
617   - self.log("\n")
  523 + self._log.parformat("Model")
  524 + self._log(model.name())
  525 + self._log("\n")
618 526 for par in model:
619   - self.log(str(par)+"\n")
620   - elif self.logTerse():
621   - self.log.parformat("Iteration "+str(iter))
622   - self.log("TS=")
623   - self.log(ts)
624   - self.log(" ")
625   - self.log("corr=")
626   - self.log(correct)
627   - self.log(" ")
628   - self.log(photon_flux)
629   - self.log(" ph/cm2/s = ")
630   - self.log(energy_flux)
631   - self.log(" erg/cm2/s = ")
632   - self.log(crab_flux*1000.0)
633   - self.log(" mCrab = ")
634   - self.log(sensitivity)
635   - self.log(" erg/cm2/s\n")
  527 + self._log(str(par)+"\n")
  528 + elif self._logTerse():
  529 + self._log.parformat("Iteration "+str(iter))
  530 + self._log("TS=")
  531 + self._log(ts)
  532 + self._log(" ")
  533 + self._log("corr=")
  534 + self._log(correct)
  535 + self._log(" ")
  536 + self._log(photon_flux)
  537 + self._log(" ph/cm2/s = ")
  538 + self._log(energy_flux)
  539 + self._log(" erg/cm2/s = ")
  540 + self._log(crab_flux*1000.0)
  541 + self._log(" mCrab = ")
  542 + self._log(sensitivity)
  543 + self._log(" erg/cm2/s\n")
636 544  
637 545 # Compute sliding average of extrapolated fitted prefactor,
638 546 # photon and energy flux. This damps out fluctuations and
... ... @@ -642,7 +550,7 @@ class cssens(ctools.cscript):
642 550 energy_flux = 0.0
643 551 sensitivity = 0.0
644 552 num = 0.0
645   - for k in range(self.m_num_avg):
  553 + for k in range(self._num_avg):
646 554 inx = len(crab_flux_value) - k - 1
647 555 if inx >= 0:
648 556 crab_flux += crab_flux_value[inx]
... ... @@ -656,7 +564,7 @@ class cssens(ctools.cscript):
656 564 sensitivity /= num
657 565  
658 566 # Compare average flux to last average
659   - if iter > self.m_num_avg:
  567 + if iter > self._num_avg:
660 568 if test_crab_flux > 0:
661 569 ratio = crab_flux/test_crab_flux
662 570  
... ... @@ -665,70 +573,71 @@ class cssens(ctools.cscript):
665 573 # 2. The flux correction factor is small
666 574 if ratio >= 0.99 and ratio <= 1.01 and \
667 575 correct >= 0.9 and correct <= 1.1:
668   - if self.logTerse():
669   - self.log(" Converged ("+str(ratio)+")\n")
  576 + if self._logTerse():
  577 + self._log(" Converged ("+str(ratio)+")\n")
670 578 break
671 579 else:
672   - if self.logTerse():
673   - self.log(" Flux is zero.\n")
  580 + if self._logTerse():
  581 + self._log(" Flux is zero.\n")
674 582 break
675 583  
676 584 # Use average for next iteration
677 585 test_crab_flux = crab_flux
678 586  
679 587 # Exit loop if number of trials exhausted
680   - if (iter >= self.m_max_iter):
681   - if self.logTerse():
682   - self.log(" Test ended after "+str(self.m_max_iter)+" iterations.\n")
  588 + if (iter >= self._max_iter):
  589 + if self._logTerse():
  590 + self._log(" Test ended after "+str(self._max_iter)+
  591 + " iterations.\n")
683 592 break
684 593  
685 594 # Write fit results
686   - if self.logTerse():
687   - self.log.header3("Fit results")
688   - self.log.parformat("Test statistics")
689   - self.log(ts)
690   - self.log("\n")
691   - self.log.parformat("Photon flux")
692   - self.log(photon_flux)
693   - self.log(" ph/cm2/s\n")
694   - self.log.parformat("Energy flux")
695   - self.log(energy_flux)
696   - self.log(" erg/cm2/s\n")
697   - self.log.parformat("Crab flux")
698   - self.log(crab_flux*1000.0)
699   - self.log(" mCrab\n")
700   - self.log.parformat("Differential sensitivity")
701   - self.log(sensitivity)
702   - self.log(" erg/cm2/s\n")
703   - self.log.parformat("Number of simulated events")
704   - self.log(nevents)
705   - self.log("\n")
706   - self.log.header3("Background and test source model fitting")
707   - self.log.parformat("log likelihood")
708   - self.log(LogL_all)
709   - self.log("\n")
710   - self.log.parformat("Number of predicted events")
711   - self.log(npred_all)
712   - self.log("\n")
  595 + if self._logTerse():
  596 + self._log.header3("Fit results")
  597 + self._log.parformat("Test statistics")
  598 + self._log(ts)
  599 + self._log("\n")
  600 + self._log.parformat("Photon flux")
  601 + self._log(photon_flux)
  602 + self._log(" ph/cm2/s\n")
  603 + self._log.parformat("Energy flux")
  604 + self._log(energy_flux)
  605 + self._log(" erg/cm2/s\n")
  606 + self._log.parformat("Crab flux")
  607 + self._log(crab_flux*1000.0)
  608 + self._log(" mCrab\n")
  609 + self._log.parformat("Differential sensitivity")
  610 + self._log(sensitivity)
  611 + self._log(" erg/cm2/s\n")
  612 + self._log.parformat("Number of simulated events")
  613 + self._log(nevents)
  614 + self._log("\n")
  615 + self._log.header3("Background and test source model fitting")
  616 + self._log.parformat("log likelihood")
  617 + self._log(LogL_all)
  618 + self._log("\n")
  619 + self._log.parformat("Number of predicted events")
  620 + self._log(npred_all)
  621 + self._log("\n")
713 622 for model in result_all:
714   - self.log.parformat("Model")
715   - self.log(model.name())
716   - self.log("\n")
  623 + self._log.parformat("Model")
  624 + self._log(model.name())
  625 + self._log("\n")
717 626 for par in model:
718   - self.log(str(par)+"\n")
719   - self.log.header3("Background model fit")
720   - self.log.parformat("log likelihood")
721   - self.log(LogL_bgm)
722   - self.log("\n")
723   - self.log.parformat("Number of predicted events")
724   - self.log(npred_bgm)
725   - self.log("\n")
  627 + self._log(str(par)+"\n")
  628 + self._log.header3("Background model fit")
  629 + self._log.parformat("log likelihood")
  630 + self._log(LogL_bgm)
  631 + self._log("\n")
  632 + self._log.parformat("Number of predicted events")
  633 + self._log(npred_bgm)
  634 + self._log("\n")
726 635 for model in result_bgm:
727   - self.log.parformat("Model")
728   - self.log(model.name())
729   - self.log("\n")
  636 + self._log.parformat("Model")
  637 + self._log(model.name())
  638 + self._log("\n")
730 639 for par in model:
731   - self.log(str(par)+"\n")
  640 + self._log(str(par)+"\n")
732 641  
733 642 # Store result
734 643 result = {'loge': loge, 'emin': emin.TeV(), 'emax': emax.TeV(), \
... ... @@ -739,29 +648,110 @@ class cssens(ctools.cscript):
739 648 # Return result
740 649 return result
741 650  
742   - def get_crab_flux(self, emin, emax):
  651 +
  652 + # Public methods
  653 + def run(self):
743 654 """
744   - Determine the Crab photon flux in a given energy interval.
  655 + Run the script.
745 656 """
746   - # Set Crab spectral model
747   - crab = gammalib.GModelSpectralPlaw(5.7e-16, -2.48, gammalib.GEnergy(0.3, "TeV"))
  657 + # Switch screen logging on in debug mode
  658 + if self._logDebug():
  659 + self._log.cout(True)
748 660  
749   - # Determine flux
750   - flux = crab.flux(emin, emax)
  661 + # Get parameters
  662 + self._get_parameters()
  663 +
  664 + # Initialise script
  665 + colnames = ['loge', 'emin', 'emax', 'crab_flux', 'photon_flux',
  666 + 'energy_flux', 'sensitivity']
  667 + results = []
  668 +
  669 + # Initialise models
  670 + full_model, bkg_model = self._set_models()
  671 +
  672 + # Write models into logger
  673 + if self._logTerse():
  674 + self._log("\n")
  675 + self._log.header1("Models")
  676 + self._log.header2("Background model")
  677 + self._log(str(bkg_model))
  678 + self._log("\n\n")
  679 + self._log.header2("Full model")
  680 + self._log(str(full_model))
  681 + self._log("\n")
  682 +
  683 + # Write header
  684 + if self._logTerse():
  685 + self._log("\n")
  686 + self._log.header1("Sensitivity determination")
  687 + self._log.parformat("Type")
  688 + self._log(self._type)
  689 + self._log("\n")
  690 +
  691 + # Loop over energy bins
  692 + for ieng in range(self._ebounds.size()):
  693 +
  694 + # Set energies
  695 + if self._type == "Differential":
  696 + emin = self._ebounds.emin(ieng)
  697 + emax = self._ebounds.emax(ieng)
  698 + elif self._type == "Integral":
  699 + emin = self._ebounds.emin(ieng)
  700 + emax = self._ebounds.emax()
  701 + else:
  702 + msg = "Invalid sensitivity type \""+self._type+"\" "+\
  703 + "encountered. Either specify \"Differential\" or "+\
  704 + "\"Integral\"."
  705 + raise RuntimeError(msg)
  706 +
  707 + # Determine sensitivity
  708 + result = self._get_sensitivity(emin, emax,
  709 + bkg_model, full_model)
  710 +
  711 + # Write results
  712 + if ieng == 0:
  713 + f = open(self._outfile.url(), 'w')
  714 + writer = csv.DictWriter(f, colnames)
  715 + headers = {}
  716 + for n in colnames:
  717 + headers[n] = n
  718 + writer.writerow(headers)
  719 + writer.writerow(result)
  720 + f.close()
  721 + else:
  722 + f = open(self._outfile.url(), 'a')
  723 + writer = csv.DictWriter(f, colnames)
  724 + writer.writerow(result)
  725 + f.close()
  726 +
  727 + # Append results
  728 + results.append(result)
  729 +
  730 + # Return
  731 + return
  732 +
  733 + def execute(self):
  734 + """
  735 + Execute the script.
  736 + """
  737 + # Run the script
  738 + self.run()
  739 +
  740 + # Return
  741 + return
751 742  
752   - # Return flux
753   - return flux
754 743  
755 744  
756 745 # ======================== #
757 746 # Main routine entry point #
758 747 # ======================== #
759 748 if __name__ == '__main__':
760   - """
761   - CTA ctlike-based sensitivity estimator.
762   - """
  749 +
763 750 # Create instance of application
764 751 app = cssens(sys.argv)
765 752  
  753 + # Open logfile
  754 + app._logFileOpen()
  755 +
766 756 # Run application
767 757 app.execute()
... ...
cscripts/csspec.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csspec.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the csspec script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inobs, f, h, events.fits,,, "Input event list, counts cube, or observation definition XML file"
  21 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model XML file"
  22 +srcname, s, a, Crab,,, "Source name"
  23 +expcube, f, a, NONE,,, "Input exposure cube file (only needed for stacked analysis)"
  24 +psfcube, f, a, NONE,,, "Input PSF cube file (only needed for stacked analysis)"
  25 +edispcube, f, a, NONE,,, "Input energy dispersion cube file (only needed for stacked analysis)"
  26 +bkgcube, f, a, NONE,,, "Input background cube file (only needed for stacked analysis)"
  27 +caldb, s, a, prod2,,, "Calibration database"
  28 +irf, s, a, South_0.5h,,, "Instrument response function"
  29 +edisp, b, h, no,,, "Apply energy dispersion?"
  30 +outfile, f, a, spectrum.fits,,, "Output spectrum file"
  31 +
  32 +#
  33 +# Script parameters
  34 +#==================
  35 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  36 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  37 +enumbins, i, a, 20,,, "Number of energy bins (0=unbinned)"
  38 +ebinalg, s, a, LOG,LIN|LOG|FILE,, "Binning algorithm"
  39 +ebinfile, f, a, NONE,,, "Name of the file containing the energy bin definition"
  40 +calc_ts, b, h, yes,,, "Compute TS value in each bin"
  41 +calc_ulim, b, h, yes,,, "Compute upper limit in each bin"
  42 +fix_srcs, b, h, yes,,, "Fix other skymodel parameters"
  43 +fix_bkg, b, h, no,,, "Fix background parameters"
  44 +
  45 +#
  46 +# Standard parameters
  47 +#====================
  48 +chatter, i, h, 2,0,4, "Chattiness of output"
  49 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  50 +debug, b, h, no,,, "Debugging mode activated"
  51 +mode, s, h, ql,,, "Mode of automatic parameters"
  52 +logfile, f, h, csspec.log,,, "Log filename"
... ...
cscripts/csspec.py
1 1 #! /usr/bin/env python
2 2 # ==========================================================================
3   -# Spectral points generation script.
  3 +# Generates a spectrum.
4 4 #
5 5 # Copyright (C) 2014-2016 Michael Mayer
6 6 #
... ... @@ -28,47 +28,57 @@ import sys
28 28 # ============ #
29 29 class csspec(ctools.cscript):
30 30 """
  31 + Generates a spectrum.
  32 +
31 33 This class implements the creation of spectral points. It derives from
32 34 the ctools.cscript class which provides support for parameter files,
33 35 command line arguments, and logging. In that way the Python script
34 36 behaves just as a regular ctool.
35 37 """
  38 +
  39 + # Constructors and destructors
36 40 def __init__(self, *argv):
37 41 """
38 42 Constructor.
39 43 """
40   -
41 44 # Set name
42   - self.name = "csspec"
43   - self.version = "1.1.0"
44   -
45   - # Initialise some members
46   - self.obs = gammalib.GObservations()
47   - self.fits = None
  45 + self._name = "csspec"
  46 + self._version = "1.1.0"
48 47  
49 48 # Initialise some members
  49 + self._ebounds = gammalib.GEbounds()
  50 + self._fits = None
  51 + self._binned_mode = False
  52 + self._srcname = ""
  53 + self._edisp = False
  54 + self._outfile = "NONE"
  55 + self._calc_ulimit = True
  56 + self._calc_ts = True
  57 + self._fix_bkg = False
  58 + self._fix_srcs = True
  59 + self._chatter = 2
  60 + self._clobber = True
  61 + self._debug = False
  62 + self._log_clients = False
  63 +
  64 + # Initialise observation
50 65 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
51   - self.obs = argv[0]
52   - argv = argv[1:]
  66 + self._obs = argv[0]
  67 + argv = argv[1:]
53 68 else:
54   - self.obs = gammalib.GObservations()
55   - self.obs.clear()
56   - self.m_outfile = ""
57   -
58   - # Make sure that parfile exists
59   - file = self.parfile()
  69 + self._obs = gammalib.GObservations()
60 70  
61 71 # Initialise application
62 72 if len(argv) == 0:
63   - ctools.cscript.__init__(self, self.name, self.version)
  73 + ctools.cscript.__init__(self, self._name, self._version)
64 74 elif len(argv) ==1:
65   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  75 + ctools.cscript.__init__(self, self._name, self._version, *argv)
66 76 else:
67 77 raise TypeError("Invalid number of arguments given.")
68 78  
69 79 # Set logger properties
70   - self.log_header()
71   - self.log.date(True)
  80 + self._log_header()
  81 + self._log.date(True)
72 82  
73 83 # Return
74 84 return
... ... @@ -77,121 +87,81 @@ class csspec(ctools.cscript):
77 87 """
78 88 Destructor.
79 89 """
80   - # Write separator into logger
81   - if self.logTerse():
82   - self.log("\n")
83   -
84 90 # Return
85 91 return
86 92  
87   - def parfile(self):
88   - """
89   - Check if parfile exists. If parfile does not exist then create a
90   - default parfile. This kluge avoids shipping the cscript with a parfile.
91   - """
92   - # Set parfile name
93   - parfile = self.name+".par"
94   -
95   - try:
96   - pars = gammalib.GApplicationPars(parfile)
97   - except:
98   - # Signal if parfile was not found
99   - sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n")
100   -
101   - # Create default parfile
102   - pars = gammalib.GApplicationPars()
103   - pars.append(gammalib.GApplicationPar("inobs","f","a","events.fits","","","Input event list, counts cube, or observation definition XML file"))
104   - pars.append(gammalib.GApplicationPar("inmodel","f","a","$CTOOLS/share/models/crab.xml","","","Input model XML file"))
105   - pars.append(gammalib.GApplicationPar("srcname","s","a","Crab","","","Source name"))
106   - pars.append(gammalib.GApplicationPar("expcube","f","a","NONE","","","Input exposure cube file (only needed for stacked analysis)"))
107   - pars.append(gammalib.GApplicationPar("psfcube","f","a","NONE","","","Input PSF cube file (only needed for stacked analysis)"))
108   - pars.append(gammalib.GApplicationPar("edispcube","f","a","NONE","","","Input energy dispersion cube file (only needed for stacked analysis)"))
109   - pars.append(gammalib.GApplicationPar("bkgcube","f","a","NONE","","","Input background cube file (only needed for stacked analysis)"))
110   - pars.append(gammalib.GApplicationPar("caldb","s","a","prod2","","","Calibration database"))
111   - pars.append(gammalib.GApplicationPar("irf","s","a","South_0.5h","","","Instrument response function"))
112   - pars.append(gammalib.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?"))
113   - pars.append(gammalib.GApplicationPar("outfile","f","a","spectrum.fits","","","Output spectrum file"))
114   - pars.append(gammalib.GApplicationPar("emin","r","a","0.1","","","Lower energy limit for spectral points (TeV)"))
115   - pars.append(gammalib.GApplicationPar("emax","r","a","100.0","","","Upper energy limit for spectral points (TeV)"))
116   - pars.append(gammalib.GApplicationPar("enumbins","i","a","20","1","10000","Number of spectral points"))
117   - pars.append(gammalib.GApplicationPar("ebinalg","s","a","LOG","FILE|LIN|LOG","", "Algorithm for defining energy bins"))
118   - pars.append(gammalib.GApplicationPar("ebinfile","f","a","NONE","","", "Name of the file containing the energy bin definition"))
119   - pars.append(gammalib.GApplicationPar("calc_ts","b","h","yes","yes|no","","Compute TS value in each bin"))
120   - pars.append(gammalib.GApplicationPar("calc_ulim","b","h","yes","yes|no","","Compute upper limit in each bin"))
121   - pars.append(gammalib.GApplicationPar("fix_srcs","b","h","yes","yes|no","","Fix other skymodel parameters"))
122   - pars.append(gammalib.GApplicationPar("fix_bkg","b","h","no","yes|no","","Fix background parameters"))
123   - pars.append_standard()
124   - pars.append(gammalib.GApplicationPar("logfile","f","h","csspec.log","","","Log filename"))
125   - pars.save(parfile)
126 93  
127   - # Return
128   - return
129   -
130   - def get_parameters(self):
  94 + # Private methods
  95 + def _get_parameters(self):
131 96 """
132 97 Get parameters from parfile and setup the observation.
133 98 """
134 99 # Set observation if not done before
135   - if self.obs == None or self.obs.size() == 0:
  100 + if self._obs == None or self._obs.size() == 0:
136 101 self._require_inobs("csspec::get_parameters()")
137   - self.obs = self._get_observations()
  102 + self._obs = self._get_observations()
138 103  
139 104 # Check if we have one binned cta observation, i.e. if we are in binned mode
140   - self.m_binned_mode = False
141   - if self.obs.size() == 1:
142   - if self.obs[0].classname() == "GCTAObservation":
143   - if self.obs[0].eventtype() == "CountsCube":
144   - self.m_binned_mode = True
  105 + self._binned_mode = False
  106 + if self._obs.size() == 1:
  107 + if self._obs[0].classname() == "GCTAObservation":
  108 + if self._obs[0].eventtype() == "CountsCube":
  109 + self._binned_mode = True
145 110  
146 111 # Set models if we have none
147   - if self.obs.models().size() == 0:
148   - self.obs.models(self["inmodel"].filename())
  112 + if self._obs.models().size() == 0:
  113 + self._obs.models(self["inmodel"].filename())
149 114  
150 115 # Get source name
151   - self.m_srcname = self["srcname"].string()
  116 + self._srcname = self["srcname"].string()
152 117  
153 118 # Set ebounds
154   - self.set_ebounds()
  119 + self._set_ebounds()
155 120  
156 121 # Get edisp flag
157   - self.m_edisp = self["edisp"].boolean()
158   -
159   - # Read other parameters
160   - self.m_outfile = self["outfile"].filename()
  122 + self._edisp = self["edisp"].boolean()
161 123  
162 124 # Get other parameeters
163   - self.m_calc_ulimit = self["calc_ulim"].boolean()
164   - self.m_calc_ts = self["calc_ts"].boolean()
165   - self.m_fix_bkg = self["fix_bkg"].boolean()
166   - self.m_fix_srcs = self["fix_srcs"].boolean()
  125 + self._calc_ulimit = self["calc_ulim"].boolean()
  126 + self._calc_ts = self["calc_ts"].boolean()
  127 + self._fix_bkg = self["fix_bkg"].boolean()
  128 + self._fix_srcs = self["fix_srcs"].boolean()
167 129  
168 130 # Set some fixed parameters
169   - self.m_log = False # Logging in client tools
170   - self.m_chatter = self["chatter"].integer()
171   - self.m_clobber = self["clobber"].boolean()
172   - self.m_debug = self["debug"].boolean()
  131 + self._chatter = self["chatter"].integer()
  132 + self._clobber = self["clobber"].boolean()
  133 + self._debug = self["debug"].boolean()
  134 +
  135 + # Read ahead output parameters
  136 + if (self._read_ahead()):
  137 + self._outfile = self["outfile"].filename()
  138 +
  139 + # Write input parameters into logger
  140 + if self._logTerse():
  141 + self._log_parameters()
  142 + self._log("\n")
173 143  
174 144 # Return
175 145 return
176 146  
177   - def set_ebounds(self):
  147 + def _set_ebounds(self):
178 148 """
179 149 Set energy boundaries.
180 150 """
181 151 # Get ebounds
182   - if self.m_binned_mode:
  152 + if self._binned_mode:
183 153  
184 154 # Extract cube ebounds
185   - cube_ebounds = self.obs[0].events().ebounds()
  155 + cube_ebounds = self._obs[0].events().ebounds()
186 156  
187 157 # Read user parameters
188   - self.m_enumbins = self["enumbins"].integer()
189   - self.m_emin = self["emin"].real() - 1e-6 # Rounding tolerance
190   - self.m_emax = self["emax"].real() + 1e-6 # Rounding tolerance
  158 + enumbins = self["enumbins"].integer()
  159 + emin_val = self["emin"].real() - 1e-6 # Rounding tolerance
  160 + emax_val = self["emax"].real() + 1e-6 # Rounding tolerance
191 161  
192 162 # Set energy range
193   - emin = gammalib.GEnergy(self.m_emin, "TeV")
194   - emax = gammalib.GEnergy(self.m_emax, "TeV")
  163 + emin = gammalib.GEnergy(emin_val, "TeV")
  164 + emax = gammalib.GEnergy(emax_val, "TeV")
195 165  
196 166 # Determine all cube layers that overlap with the [emin,emax]
197 167 # energy range.
... ... @@ -210,14 +180,14 @@ class csspec(ctools.cscript):
210 180 raise RuntimeError(msg)
211 181  
212 182 # Determine number of layers to use for each spectral bin
213   - n_layers = int(len(use_layers) / self.m_enumbins)
  183 + n_layers = int(len(use_layers) / enumbins)
214 184  
215 185 # Don't allow zero layers
216 186 if n_layers == 0:
217 187 n_layers = 1
218 188  
219 189 # Create energy boundaries
220   - self.m_ebounds = gammalib.GEbounds()
  190 + self._ebounds = gammalib.GEbounds()
221 191 i_start = 0
222 192 i_stop = n_layers - 1
223 193 while i_start < len(use_layers):
... ... @@ -225,51 +195,13 @@ class csspec(ctools.cscript):
225 195 i_stop = len(use_layers) - 1
226 196 ebinmin = cube_ebounds.emin(use_layers[i_start])
227 197 ebinmax = cube_ebounds.emax(use_layers[i_stop])
228   - self.m_ebounds.append(ebinmin, ebinmax)
  198 + self._ebounds.append(ebinmin, ebinmax)
229 199 i_start += n_layers
230 200 i_stop += n_layers
231   - #for i in range(len(use_layers))[::n_layers]:
232   - # i_start = i
233   - # i_stop = i + n_layers - 1
234   - # if i_stop >= len(use_layers):
235   - # i_stop = len(use_layers) - 1
236   - # ebinmin = cube_ebounds.emin(use_layers[i_start])
237   - # ebinmax = cube_ebounds.emax(use_layers[i_stop])
238   - # self.m_ebounds.append(ebinmin, ebinmax)
239 201  
240 202 # Unbinned mode
241 203 else:
242   - self.m_ebounds = self._create_ebounds()
243   -
244   - # Return
245   - return
246   -
247   - def models(self, models):
248   - """
249   - Set model.
250   - """
251   - # Copy models
252   - self.obs.models(models.clone())
253   -
254   - # Return
255   - return
256   -
257   - def spectrum(self):
258   - """
259   - Return spectrum as FITS object.
260   - """
261   - # Return
262   - return self.fits
263   -
264   - def execute(self):
265   - """
266   - Execute the script.
267   - """
268   - # Run the script
269   - self.run()
270   -
271   - # Save residual map
272   - self.fits.saveto(self.m_outfile, self.m_clobber)
  204 + self._ebounds = self._create_ebounds()
273 205  
274 206 # Return
275 207 return
... ... @@ -279,91 +211,86 @@ class csspec(ctools.cscript):
279 211 Run the script.
280 212 """
281 213 # Switch screen logging on in debug mode
282   - if self.logDebug():
283   - self.log.cout(True)
  214 + if self._logDebug():
  215 + self._log.cout(True)
284 216  
285 217 # Get parameters
286   - self.get_parameters()
287   -
288   - # Write input parameters into logger
289   - if self.logTerse():
290   - self.log_parameters()
291   - self.log("\n")
  218 + self._get_parameters()
292 219  
293 220 # Write spectral binning into header
294   - if self.logTerse():
295   - self.log("\n")
296   - self.log.header1("Spectral binning")
297   - if self.m_binned_mode:
298   - cube_ebounds = self.obs[0].events().ebounds()
299   - self.log.parformat("Counts cube energy range")
300   - self.log(str(cube_ebounds.emin()))
301   - self.log(" - ")
302   - self.log(str(cube_ebounds.emax()))
303   - self.log("\n")
304   - for i in range(self.m_ebounds.size()):
305   - self.log.parformat("Bin "+str(i+1))
306   - self.log(str(self.m_ebounds.emin(i)))
307   - self.log(" - ")
308   - self.log(str(self.m_ebounds.emax(i)))
309   - self.log("\n")
  221 + if self._logTerse():
  222 + self._log("\n")
  223 + self._log.header1("Spectral binning")
  224 + if self._binned_mode:
  225 + cube_ebounds = self._obs[0].events().ebounds()
  226 + self._log.parformat("Counts cube energy range")
  227 + self._log(str(cube_ebounds.emin()))
  228 + self._log(" - ")
  229 + self._log(str(cube_ebounds.emax()))
  230 + self._log("\n")
  231 + for i in range(self._ebounds.size()):
  232 + self._log.parformat("Bin "+str(i+1))
  233 + self._log(str(self._ebounds.emin(i)))
  234 + self._log(" - ")
  235 + self._log(str(self._ebounds.emax(i)))
  236 + self._log("\n")
310 237  
311 238 # Write observation into logger
312   - if self.logTerse():
313   - self.log("\n")
314   - self.log.header1("Observation")
315   - self.log(str(self.obs))
316   - self.log("\n")
  239 + if self._logTerse():
  240 + self._log("\n")
  241 + self._log.header1("Observation")
  242 + self._log(str(self._obs))
  243 + self._log("\n")
317 244  
318 245 # Write header
319   - if self.logTerse():
320   - self.log("\n")
321   - self.log.header1("Adjust model parameters")
  246 + if self._logTerse():
  247 + self._log("\n")
  248 + self._log.header1("Adjust model parameters")
322 249  
323 250 # Adjust model parameters dependent on input user parameters
324   - for model in self.obs.models():
  251 + for model in self._obs.models():
325 252  
326 253 # Set TS flag for all models to false.
327 254 # Source of interest will be set to true later
328 255 model.tscalc(False)
329 256  
330 257 # Log model name
331   - if self.logExplicit():
332   - self.log.header3(model.name())
  258 + if self._logExplicit():
  259 + self._log.header3(model.name())
333 260  
334 261 # Deal with the source of interest
335   - if model.name() == self.m_srcname:
  262 + if model.name() == self._srcname:
336 263 for par in model:
337   - if par.is_free() and self.logExplicit():
338   - self.log(" Fixing \""+par.name()+"\"\n")
  264 + if par.is_free() and self._logExplicit():
  265 + self._log(" Fixing \""+par.name()+"\"\n")
339 266 par.fix()
340 267 normpar = model.spectral()[0]
341   - if normpar.is_fixed() and self.logExplicit():
342   - self.log(" Freeing \""+normpar.name()+"\"\n")
  268 + if normpar.is_fixed() and self._logExplicit():
  269 + self._log(" Freeing \""+normpar.name()+"\"\n")
343 270 normpar.free()
344   - if self.m_calc_ts:
  271 + if self._calc_ts:
345 272 model.tscalc(True)
346 273  
347   - elif self.m_fix_bkg and not model.classname() == "GModelSky":
  274 + elif self._fix_bkg and not model.classname() == "GModelSky":
348 275 for par in model:
349   - if par.is_free() and self.logExplicit():
350   - self.log(" Fixing \""+par.name()+"\"\n")
  276 + if par.is_free() and self._logExplicit():
  277 + self._log(" Fixing \""+par.name()+"\"\n")
351 278 par.fix()
352 279  
353   - elif self.m_fix_srcs and model.classname() == "GModelSky":
  280 + elif self._fix_srcs and model.classname() == "GModelSky":
354 281 for par in model:
355   - if par.is_free() and self.logExplicit():
356   - self.log(" Fixing \""+par.name()+"\"\n")
  282 + if par.is_free() and self._logExplicit():
  283 + self._log(" Fixing \""+par.name()+"\"\n")
357 284 par.fix()
358 285  
359 286 # Write header
360   - if self.logTerse():
361   - self.log("\n")
362   - self.log.header1("Generate spectrum")
363   - self.log(str(self.m_ebounds))
  287 + if self._logTerse():
  288 + self._log("\n")
  289 + self._log.header1("Generate spectrum")
  290 + self._log(str(self._ebounds))
364 291  
365 292 # Initialise FITS Table with extension "SPECTRUM"
366   - table = gammalib.GFitsBinTable(self.m_ebounds.size())
  293 + table = gammalib.GFitsBinTable(self._ebounds.size())
367 294 table.extname("SPECTRUM")
368 295  
369 296 # Add Header for compatibility with gammalib.GMWLSpectrum
... ... @@ -371,14 +298,15 @@ class csspec(ctools.cscript):
371 298 table.card("TELESCOP", "CTA", "Name of Telescope")
372 299  
373 300 # Create FITS table columns
374   - energy = gammalib.GFitsTableDoubleCol("Energy", self.m_ebounds.size())
375   - energy_low = gammalib.GFitsTableDoubleCol("ed_Energy", self.m_ebounds.size())
376   - energy_high = gammalib.GFitsTableDoubleCol("eu_Energy", self.m_ebounds.size())
377   - flux = gammalib.GFitsTableDoubleCol("Flux", self.m_ebounds.size())
378   - flux_err = gammalib.GFitsTableDoubleCol("e_Flux", self.m_ebounds.size())
379   - TSvalues = gammalib.GFitsTableDoubleCol("TS", self.m_ebounds.size())
380   - ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", self.m_ebounds.size())
381   - Npred_values = gammalib.GFitsTableDoubleCol("Npred", self.m_ebounds.size())
  301 + nrows = self._ebounds.size()
  302 + energy = gammalib.GFitsTableDoubleCol("Energy", nrows)
  303 + energy_low = gammalib.GFitsTableDoubleCol("ed_Energy", nrows)
  304 + energy_high = gammalib.GFitsTableDoubleCol("eu_Energy", nrows)
  305 + flux = gammalib.GFitsTableDoubleCol("Flux", nrows)
  306 + flux_err = gammalib.GFitsTableDoubleCol("e_Flux", nrows)
  307 + TSvalues = gammalib.GFitsTableDoubleCol("TS", nrows)
  308 + ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", nrows)
  309 + Npred_values = gammalib.GFitsTableDoubleCol("Npred", nrows)
382 310 energy.unit("TeV")
383 311 energy_low.unit("TeV")
384 312 energy_high.unit("TeV")
... ... @@ -387,17 +315,17 @@ class csspec(ctools.cscript):
387 315 ulim_values.unit("erg/cm2/s")
388 316  
389 317 # Loop over energy bins
390   - for i in range(self.m_ebounds.size()):
  318 + for i in range(nrows):
391 319  
392 320 # Log information
393   - if self.logExplicit():
394   - self.log("\n")
395   - self.log.header2("Energy bin "+str(i+1))
  321 + if self._logExplicit():
  322 + self._log("\n")
  323 + self._log.header2("Energy bin "+str(i+1))
396 324  
397 325 # Get energy boundaries
398   - emin = self.m_ebounds.emin(i)
399   - emax = self.m_ebounds.emax(i)
400   - elogmean = self.m_ebounds.elogmean(i)
  326 + emin = self._ebounds.emin(i)
  327 + emax = self._ebounds.emax(i)
  328 + elogmean = self._ebounds.elogmean(i)
401 329 elogmean2 = elogmean.MeV() * elogmean.MeV()
402 330  
403 331 # Store energy as TeV
... ... @@ -407,15 +335,15 @@ class csspec(ctools.cscript):
407 335 energy_low[i] = (elogmean - emin).TeV()
408 336 energy_high[i] = (emax - elogmean).TeV()
409 337  
410   - # use ctselect for unbinned analysis
411   - if not self.m_binned_mode:
  338 + # Use ctselect for unbinned analysis
  339 + if not self._binned_mode:
412 340  
413 341 # Log information
414   - if self.logExplicit():
415   - self.log.header3("Selecting events")
  342 + if self._logExplicit():
  343 + self._log.header3("Selecting events")
416 344  
417 345 # Select events
418   - select = ctools.ctselect(self.obs)
  346 + select = ctools.ctselect(self._obs)
419 347 select["emin"] = emin.TeV()
420 348 select["emax"] = emax.TeV()
421 349 select["tmin"] = "UNDEFINED"
... ... @@ -428,15 +356,15 @@ class csspec(ctools.cscript):
428 356 # Retrieve observation
429 357 obs = select.obs()
430 358  
431   - # use ctcubemask for binned analysis
  359 + # Use ctcubemask for binned analysis
432 360 else:
433 361  
434 362 # Header
435   - if self.logExplicit():
436   - self.log.header3("Filtering cube")
  363 + if self._logExplicit():
  364 + self._log.header3("Filtering cube")
437 365  
438 366 # Select layers
439   - cubemask = ctools.ctcubemask(self.obs)
  367 + cubemask = ctools.ctcubemask(self._obs)
440 368 cubemask["regfile"] = "NONE"
441 369 cubemask["ra"] = "UNDEFINED"
442 370 cubemask["dec"] = "UNDEFINED"
... ... @@ -449,22 +377,22 @@ class csspec(ctools.cscript):
449 377 obs = cubemask.obs()
450 378  
451 379 # Header
452   - if self.logExplicit():
453   - self.log.header3("Performing fit")
  380 + if self._logExplicit():
  381 + self._log.header3("Performing fit")
454 382  
455 383 # Likelihood
456 384 like = ctools.ctlike(obs)
457   - like["edisp"] = self.m_edisp
  385 + like["edisp"] = self._edisp
458 386 like.run()
459 387  
460 388 # Skip bin if no event was present
461 389 if like.obs().logL() == 0.0:
462 390  
463 391 # Log information
464   - if self.logExplicit():
465   - self.log("No event in this bin. ")
466   - self.log("Likelihood is zero. ")
467   - self.log("Bin is skipped.")
  392 + if self._logExplicit():
  393 + self._log("No event in this bin. ")
  394 + self._log("Likelihood is zero. ")
  395 + self._log("Bin is skipped.")
468 396  
469 397 # Set all values to 0
470 398 flux[i] = 0.0
... ... @@ -476,19 +404,19 @@ class csspec(ctools.cscript):
476 404  
477 405 # Get results
478 406 fitted_models = like.obs().models()
479   - source = fitted_models[self.m_srcname]
  407 + source = fitted_models[self._srcname]
480 408  
481 409 # Calculate Upper Limit
482 410 ulimit_value = -1.0
483   - if self.m_calc_ulimit:
  411 + if self._calc_ulimit:
484 412  
485 413 # Logging information
486   - if self.logExplicit():
487   - self.log.header3("Computing upper limit")
  414 + if self._logExplicit():
  415 + self._log.header3("Computing upper limit")
488 416  
489 417 # Create upper limit object
490 418 ulimit = ctools.ctulimit(like.obs())
491   - ulimit["srcname"] = self.m_srcname
  419 + ulimit["srcname"] = self._srcname
492 420 ulimit["eref"] = elogmean.TeV()
493 421  
494 422 # Try to run upper limit and catch exceptions
... ... @@ -496,23 +424,23 @@ class csspec(ctools.cscript):
496 424 ulimit.run()
497 425 ulimit_value = ulimit.diff_ulimit()
498 426 except:
499   - if self.logExplicit():
500   - self.log("Upper limit calculation failed.")
  427 + if self._logExplicit():
  428 + self._log("Upper limit calculation failed.")
501 429 ulimit_value = -1.0
502 430  
503 431 # Get TS value
504 432 TS = -1.0
505   - if self.m_calc_ts:
  433 + if self._calc_ts:
506 434 TS = source.ts()
507 435  
508 436 # Compute Npred value (only works for unbinned analysis)
509 437 Npred = 0.0
510   - if not self.m_binned_mode:
  438 + if not self._binned_mode:
511 439 for observation in like.obs():
512 440 Npred += observation.npred(source)
513 441  
514 442 # Get differential flux
515   - fitted_flux = source.spectral().eval(elogmean,gammalib.GTime())
  443 + fitted_flux = source.spectral().eval(elogmean)
516 444  
517 445 # Compute flux error
518 446 parvalue = source.spectral()[0].value()
... ... @@ -532,17 +460,17 @@ class csspec(ctools.cscript):
532 460 ulim_values[i] = ulimit_value * elogmean2 * gammalib.MeV2erg
533 461  
534 462 # Log information
535   - if self.logTerse():
536   - self.log("\n")
537   - self.log.parformat("Bin "+str(i+1))
538   - self.log(str(flux[i]))
539   - self.log(" +/- ")
540   - self.log(str(flux_err[i]))
541   - if self.m_calc_ulimit and ulim_values[i] > 0.0:
542   - self.log(" [< "+str(ulim_values[i])+"]")
543   - self.log(" erg/cm2/s")
544   - if self.m_calc_ts and TSvalues[i] > 0.0:
545   - self.log(" (TS = "+str(TS)+")")
  463 + if self._logTerse():
  464 + self._log("\n")
  465 + self._log.parformat("Bin "+str(i+1))
  466 + self._log(str(flux[i]))
  467 + self._log(" +/- ")
  468 + self._log(str(flux_err[i]))
  469 + if self._calc_ulimit and ulim_values[i] > 0.0:
  470 + self._log(" [< "+str(ulim_values[i])+"]")
  471 + self._log(" erg/cm2/s")
  472 + if self._calc_ts and TSvalues[i] > 0.0:
  473 + self._log(" (TS = "+str(TS)+")")
546 474  
547 475 # Append filled columns to fits table
548 476 table.append(energy)
... ... @@ -555,8 +483,69 @@ class csspec(ctools.cscript):
555 483 table.append(Npred_values)
556 484  
557 485 # Create the FITS file now
558   - self.fits = gammalib.GFits()
559   - self.fits.append(table)
  486 + self._fits = gammalib.GFits()
  487 + self._fits.append(table)
  488 +
  489 + # Return
  490 + return
  491 +
  492 + def execute(self):
  493 + """
  494 + Execute the script.
  495 + """
  496 + # Read ahead output parameters
  497 + self._read_ahead(True)
  498 +
  499 + # Run the script
  500 + self.run()
  501 +
  502 + # Save spectrum
  503 + self.save()
  504 +
  505 + # Return
  506 + return
  507 +
  508 + def save(self):
  509 + """
  510 + Save spectrum.
  511 + """
  512 + # Write header
  513 + if self._logTerse():
  514 + self._log("\n")
  515 + self._log.header1("Save spectrum")
  516 +
  517 + # Get outmap parameter
  518 + self._outfile = self["outfile"].filename()
  519 +
  520 + # Continue only filename and residual map are valid
  521 + if self._outfile != "NONE" and self._fits != None:
  522 +
  523 + # Log file name
  524 + if self._logTerse():
  525 + self._log("Save spectrum into \""+self._outfile+"\".\n")
  526 +
  527 + # Save spectrum
  528 + self._fits.saveto(self._outfile, self._clobber)
  529 +
  530 + # Return
  531 + return
  532 +
  533 + def spectrum(self):
  534 + """
  535 + Return spectrum FITS file.
  536 +
  537 + Returns:
  538 + FITS file containing spectrum.
  539 + """
  540 + # Return
  541 + return self._fits
  542 +
  543 + def models(self, models):
  544 + """
  545 + Set model.
  546 + """
  547 + # Copy models
  548 + self._obs.models(models.clone())
560 549  
561 550 # Return
562 551 return
... ... @@ -566,14 +555,12 @@ class csspec(ctools.cscript):
566 555 # Main routine entry point #
567 556 # ======================== #
568 557 if __name__ == '__main__':
569   - """
570   - Generates spectral points.
571   - """
  558 +
572 559 # Create instance of application
573 560 app = csspec(sys.argv)
574 561  
575 562 # Open logfile
576   - app.logFileOpen()
  563 + app._logFileOpen()
577 564  
578 565 # Execute application
579 566 app.execute()
... ...
cscripts/cstsdist.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: cstsdist.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the cstsdist script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inobs, f, h, NONE,,, "Input event list, counts cube, or observation definition XML file"
  21 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model XML file"
  22 +srcname, s, a, Crab,,, "Source name"
  23 +expcube, f, a, NONE,,, "Input exposure cube file (only needed for stacked analysis)"
  24 +psfcube, f, a, NONE,,, "Input PSF cube file (only needed for stacked analysis)"
  25 +edispcube, f, a, NONE,,, "Input energy dispersion cube file (only needed for stacked analysis)"
  26 +bkgcube, f, a, NONE,,, "Input background cube file (only needed for stacked analysis)"
  27 +caldb, s, a, prod2,,, "Calibration database"
  28 +irf, s, a, South_0.5h,,, "Instrument response function"
  29 +edisp, b, h, no,,, "Apply energy dispersion?"
  30 +deadc, r, h, 0.95,0,1, "Deadtime correction factor"
  31 +outfile, f, a, ts.dat,,, "Output Test Statistic distribution file"
  32 +
  33 +#
  34 +# Script parameters
  35 +#==================
  36 +ntrials, i, a, 10,,, "Number of trials"
  37 +ra, r, a, 83.63,0,360, "RA of pointing (deg)"
  38 +dec, r, a, 22.01,-90,90, "Dec of pointing (deg)"
  39 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  40 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  41 +enumbins, i, a, 0,,, "Number of energy bins (0=unbinned)"
  42 +tmin, r, h, 0.0,,, "Start time (MET in s)"
  43 +tmax, r, a, 1800.0,,, "Duration (s)"
  44 +npix, i, a, 200,,, "Number of pixels for binned"
  45 +binsz, r, a, 0.05,,, "Pixel size for binned (deg/pixel)"
  46 +rad, r, h, 5.0,0,180, "Radius of ROI (deg)"
  47 +pattern, s, h, single,single|four,, "Observation pattern"
  48 +offset, r, h, 1.5,,, "Observation pattern offset (deg)"
  49 +
  50 +#
  51 +# Standard parameters
  52 +#====================
  53 +chatter, i, h, 2,0,4, "Chattiness of output"
  54 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  55 +debug, b, h, no,,, "Debugging mode activated"
  56 +mode, s, h, ql,,, "Mode of automatic parameters"
  57 +logfile, f, h, cstsdist.log,,, "Log filename"
... ...
cscripts/cstsdist.py
1 1 #! /usr/bin/env python
2 2 # ==========================================================================
3   -# This script generates the TS distribution for a particular model based
4   -# on Monte-Carlo simulations.
  3 +# Generates the TS distribution for a particular model
5 4 #
6 5 # Copyright (C) 2011-2016 Juergen Knoedlseder
7 6 #
... ... @@ -31,38 +30,48 @@ import csv
31 30 # ============== #
32 31 class cstsdist(ctools.cscript):
33 32 """
  33 + Generates Test Statistic distribution for a model.
  34 +
  35 +
34 36 This class implements the TS distribution generation script. It derives
35 37 from the ctools.cscript class which provides support for parameter
36 38 files, command line arguments, and logging. In that way the Python
37 39 script behaves just as a regular ctool.
38 40 """
  41 +
  42 + # Constructors and destructors
39 43 def __init__(self, *argv):
40 44 """
41 45 Constructor.
42 46 """
43 47 # Set name
44   - self.name = "cstsdist"
45   - self.version = "1.1.0"
  48 + self._name = "cstsdist"
  49 + self._version = "1.1.0"
46 50  
47 51 # Initialise some members
48   - self.obs = None
49   - self.bkg_model = None
50   - self.full_model = None
51   -
52   - # Make sure that parfile exists
53   - file = self.parfile()
  52 + self._obs = gammalib.GObservations()
  53 + self._pattern = "single"
  54 + self._srcname = ""
  55 + self._enumbins = 0
  56 + self._npix = 0
  57 + self._binsz = 0.0
  58 + self._outfile = "ts.dat"
  59 + self._ntrials = 10
  60 + self._edisp = False
  61 + self._debug = False
  62 + self._log_clients = False
54 63  
55 64 # Initialise application
56 65 if len(argv) == 0:
57   - ctools.cscript.__init__(self, self.name, self.version)
  66 + ctools.cscript.__init__(self, self._name, self._version)
58 67 elif len(argv) ==1:
59   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  68 + ctools.cscript.__init__(self, self._name, self._version, *argv)
60 69 else:
61 70 raise TypeError("Invalid number of arguments given.")
62 71  
63 72 # Set logger properties
64   - self.log_header()
65   - self.log.date(True)
  73 + self._log_header()
  74 + self._log.date(True)
66 75  
67 76 # Return
68 77 return
... ... @@ -71,219 +80,109 @@ class cstsdist(ctools.cscript):
71 80 """
72 81 Destructor.
73 82 """
74   - # Write separator into logger
75   - if self.logTerse():
76   - self.log("\n")
77   -
78 83 # Return
79 84 return
80 85  
81   - def parfile(self):
82   - """
83   - Check if parfile exists. If parfile does not exist then create a
84   - default parfile. This kluge avoids shipping the cscript with a parfile.
85   - """
86   - # Set parfile name
87   - parfile = self.name+".par"
88   -
89   - try:
90   - pars = gammalib.GApplicationPars(parfile)
91   - except:
92   - # Signal if parfile was not found
93   - sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n")
94   -
95   - # Create default parfile
96   - pars = gammalib.GApplicationPars()
97   - pars.append(gammalib.GApplicationPar("inobs","f","h","NONE","","","Input event list, counts cube, or observation definition XML file"))
98   - pars.append(gammalib.GApplicationPar("inmodel","f","a","$CTOOLS/share/models/crab.xml","","","Input model XML file"))
99   - pars.append(gammalib.GApplicationPar("srcname","s","a","Crab","","","Source name"))
100   - pars.append(gammalib.GApplicationPar("expcube","s","a","NONE","","","Input exposure cube file (only needed for stacked analysis)"))
101   - pars.append(gammalib.GApplicationPar("psfcube","s","a","NONE","","","Input PSF cube file (only needed for stacked analysis)"))
102   - pars.append(gammalib.GApplicationPar("edispcube","f","a","NONE","","","Input energy dispersion cube file (only needed for stacked analysis)"))
103   - pars.append(gammalib.GApplicationPar("bkgcube","s","a","NONE","","","Input background cube file (only needed for stacked analysis)"))
104   - pars.append(gammalib.GApplicationPar("caldb","s","a","prod2","","","Calibration database"))
105   - pars.append(gammalib.GApplicationPar("irf","s","a","South_0.5h","","","Instrument response function"))
106   - pars.append(gammalib.GApplicationPar("deadc","r","h","0.95","","","Deadtime correction factor"))
107   - pars.append(gammalib.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?"))
108   - pars.append(gammalib.GApplicationPar("outfile","f","a","ts.dat","","","Output Test Statistics distribution file"))
109   - pars.append(gammalib.GApplicationPar("ntrials","i","a","10","","","Number of trials"))
110   - pars.append(gammalib.GApplicationPar("ra","r","a","83.63","0","360","RA of pointing (deg)"))
111   - pars.append(gammalib.GApplicationPar("dec","r","a","22.01","-90","90","Dec of pointing (deg)"))
112   - pars.append(gammalib.GApplicationPar("emin","r","a","0.1","","","Lower energy limit (TeV)"))
113   - pars.append(gammalib.GApplicationPar("emax","r","a","100.0","","","Upper energy limit (TeV)"))
114   - pars.append(gammalib.GApplicationPar("enumbins","i","a","0","","","Number of energy bins (0=unbinned)"))
115   - pars.append(gammalib.GApplicationPar("tmin","r","h","0.0","","","Start time (MET in s)"))
116   - pars.append(gammalib.GApplicationPar("tmax","r","a","1800.0","","","Observation duration (in s)"))
117   - pars.append(gammalib.GApplicationPar("npix","i","a","200","","","Number of pixels for binned"))
118   - pars.append(gammalib.GApplicationPar("binsz","r","a","0.05","","","Pixel size for binned (deg/pixel)"))
119   - pars.append(gammalib.GApplicationPar("rad","r","h","5.0","","","Radius of ROI (deg)"))
120   - pars.append(gammalib.GApplicationPar("pattern","s","h","single","","","Observation pattern (single/four)"))
121   - pars.append(gammalib.GApplicationPar("offset","r","h","1.5","","","Observation pattern offset (deg)"))
122   - pars.append_standard()
123   - pars.append(gammalib.GApplicationPar("logfile","f","h","cstsdist.log","","","Log filename"))
124   - pars.save(parfile)
125   -
126   - # Return
127   - return
128 86  
129   - def get_parameters(self):
  87 + # Private methods
  88 + def _get_parameters(self):
130 89 """
131 90 Get parameters from parfile and setup the observation.
132 91 """
133 92  
134 93 # Set observation if not done before
135   - if self.obs == None or self.obs.size() == 0:
136   - self.obs = self._get_observations()
  94 + if self._obs == None or self._obs.size() == 0:
  95 + self._obs = self._get_observations()
137 96  
138 97 # Check for requested pattern and use above
139 98 # observation parameters to set wobble pattern
140   - self.m_pattern = self["pattern"].string()
141   - if self.m_pattern == "four":
142   - self.obs = self.set_obs()
  99 + self._pattern = self["pattern"].string()
  100 + if self._pattern == "four":
  101 + self._obs = self._set_obs()
143 102  
144 103 # Get source name
145   - self.m_srcname = self["srcname"].string()
  104 + self._srcname = self["srcname"].string()
146 105  
147 106 # Get number of energy bins
148   - self.m_enumbins = self["enumbins"].integer()
  107 + self._enumbins = self["enumbins"].integer()
149 108  
150 109 # Read parameters for binned if requested
151   - if not self.m_enumbins == 0:
152   - self.m_npix = self["npix"].integer()
153   - self.m_binsz = self["binsz"].real()
154   - else:
155   - # Set dummy values (required by obsutils)
156   - self.m_npix = 0
157   - self.m_binsz = 0.0
  110 + if not self._enumbins == 0:
  111 + self._npix = self["npix"].integer()
  112 + self._binsz = self["binsz"].real()
158 113  
159 114 # Set models if we have none
160   - if self.obs.models().size() == 0:
161   - self.obs.models(self["inmodel"].filename())
  115 + if self._obs.models().size() == 0:
  116 + self._obs.models(self["inmodel"].filename())
162 117  
163 118 # Get other parameters
164   - self.m_outfile = self["outfile"].filename()
165   - self.m_ntrials = self["ntrials"].integer()
166   - self.m_edisp = self["edisp"].boolean()
167   - self.m_offset = self["offset"].real()
  119 + self._outfile = self["outfile"].filename()
  120 + self._ntrials = self["ntrials"].integer()
  121 + self._edisp = self["edisp"].boolean()
  122 + self._debug = self["debug"].boolean()
168 123  
169   - # Set some fixed parameters
170   - self.m_log = False # Logging in client tools
171   - self.m_debug = self["debug"].boolean() # Debugging in client tools
  124 + # Write input parameters into logger
  125 + if self._logTerse():
  126 + self._log_parameters()
  127 + self._log("\n")
172 128  
173 129 # Return
174 130 return
175 131  
176   - def models(self, models):
  132 + def _set_obs(self):
177 133 """
178   - Set model.
179   - """
180   - # Copy models
181   - self.obs.models(models.copy())
182   -
183   - # Return
184   - return
  134 + Set an observation container.
185 135  
186   - def execute(self):
  136 + Returns:
  137 + Observation container.
187 138 """
188   - Execute the script.
189   - """
190   - # Run the script
191   - self.run()
192   -
193   - # Return
194   - return
195   -
196   - def run(self):
197   - """
198   - Run the script.
199   - """
200   - # Switch screen logging on in debug mode
201   - if self.logDebug():
202   - self.log.cout(True)
203   -
204   - # Get parameters
205   - self.get_parameters()
206   -
207   - # Write input parameters into logger
208   - if self.logTerse():
209   - self.log_parameters()
210   - self.log("\n")
211   -
212   - # Initialise models
213   - full_model, bkg_model = self.set_models()
214   -
215   - # Write models into logger
216   - if self.logTerse():
217   - self.log("\n")
218   - self.log.header1("Models")
219   - self.log.header2("Background model")
220   - self.log(str(bkg_model))
221   - self.log("\n\n")
222   - self.log.header2("Full model")
223   - self.log(str(full_model))
224   - self.log("\n")
225   -
226   - # Write observation into logger
227   - if self.logTerse():
228   - self.log("\n")
229   - self.log.header1("Observation")
230   - self.log(str(self.obs))
231   - self.log("\n")
232 139  
233   - # Write models into logger
234   - if self.logTerse():
235   - self.log("\n")
236   - self.log.header1("Test model")
237   - self.log(str(self.full_model))
238   - self.log("\n")
239   -
240   - # Write header
241   - if self.logTerse():
242   - self.log("\n")
243   - self.log.header1("Generate TS distribution")
  140 + # Setup observation definition list
  141 + obsdeflist = obsutils.set_obs_patterns(self._pattern,
  142 + ra=self["ra"].real(),
  143 + dec=self["dec"].real(),
  144 + offset=self["offset"].real())
244 145  
245   - # Loop over trials
246   - for seed in range(self.m_ntrials):
  146 + # Create list of observations
  147 + obs = obsutils.set_obs_list(obsdeflist,
  148 + tstart=self["tmin"].real(),
  149 + duration=self["tmax"].real()-self["tmin"].real(),
  150 + deadc=self["deadc"].real(),
  151 + emin=self["emin"].real(),
  152 + emax=self["emax"].real(),
  153 + rad=self["rad"].real(),
  154 + irf=self["irf"].string(),
  155 + caldb=self["caldb"].string())
247 156  
248   - # Make a trial
249   - result = self.trial(seed, full_model, bkg_model)
  157 + # Return observation container
  158 + return obs
250 159  
251   - # Write out result immediately
252   - if seed == 0:
253   - file = open(self.m_outfile.url(), 'w')
254   - writer = csv.DictWriter(file, result['colnames'])
255   - headers = {}
256   - for n in result['colnames']:
257   - headers[n] = n
258   - writer.writerow(headers)
259   - #writer.writerow(dict((_,_) for _ in result['colnames']))
260   - else:
261   - file = open(self.m_outfile.url(), 'a')
262   - writer = csv.DictWriter(file, result['colnames'])
263   - writer.writerow(result['values'])
264   - file.close()
  160 + def _set_models(self, fitspat=False, fitspec=False):
  161 + """
  162 + Set full model and background model.
265 163  
266   - # Return
267   - return
  164 + Kwargs:
  165 + fitspat: Fit spatial parameter (default: False).
  166 + fitspec: Fit spectral parameters (default: False).
268 167  
269   - def set_models(self, fitpos=False, fitspec=False):
270   - """
271   - Set full and background model.
  168 + Returns:
  169 + Tuple containing full model and background model.
272 170 """
273 171 # Retrieve full model from observation container
274   - full_model = self.obs.models().copy()
  172 + full_model = self._obs.models().copy()
275 173  
276 174 # Get source model
277   - model = full_model[self.m_srcname]
  175 + model = full_model[self._srcname]
278 176  
279 177 # Check that model has a Prefactor
280 178 if not model.has_par("Prefactor"):
281   - msg = "Model \""+self.m_srcname+"\" has no parameter \"Prefactor\"."+ \
282   - " Only spectral models with a \"Prefactor\" parameter are supported."
  179 + msg = "Model \""+self._srcname+"\" has no parameter "+\
  180 + "\"Prefactor\". Only spectral models with a "+\
  181 + "\"Prefactor\" parameter are supported."
283 182 raise RuntimeError(msg)
284 183  
285 184 # Fit or fix spatial parameters
286   - if fitpos:
  185 + if fitspat:
287 186 if model.has_par("RA"):
288 187 model["RA"].free()
289 188 if model.has_par("DEC"):
... ... @@ -320,55 +219,35 @@ class cstsdist(ctools.cscript):
320 219  
321 220 # Create background model
322 221 bkg_model = full_model.copy()
323   - bkg_model.remove(self.m_srcname)
  222 + bkg_model.remove(self._srcname)
324 223  
325 224 # Return models
326 225 return full_model, bkg_model
327 226  
328   - def set_obs(self):
329   - """
330   - Returns an observation container with a set of CTA observations.
331   -
332   - Keywords:
333   - """
334   -
335   - # Setup observation definition list
336   - obsdeflist = obsutils.set_obs_patterns(self.m_pattern,
337   - ra=self["ra"].real(),
338   - dec=self["dec"].real(),
339   - offset=self["offset"].real())
340   -
341   - # Create list of observations
342   - obs = obsutils.set_obs_list(obsdeflist,
343   - tstart=self["tmin"].real(),
344   - duration=self["tmax"].real()-self["tmin"].real(),
345   - deadc=self["deadc"].real(),
346   - emin=self["emin"].real(),
347   - emax=self["emax"].real(),
348   - rad=self["rad"].real(),
349   - irf=self["irf"].string(), caldb=self["caldb"].string())
350   -
351   - # Return observation container
352   - return obs
353   -
354   - def trial(self, seed, full_model, bkg_model):
  227 + def _trial(self, seed, full_model, bkg_model):
355 228 """
356 229 Create the TS for a single trial.
357 230  
358   - Parameters:
359   - seed - Random number generator seed
  231 + Args:
  232 + seed: Random number generator seed
  233 + full_model: Full model
  234 + bkg_model: Background model
  235 +
  236 + Returns:
  237 + Result dictionary
360 238 """
361 239 # Write header
362   - if self.logExplicit():
363   - self.log.header2("Trial "+str(seed+1))
  240 + if self._logExplicit():
  241 + self._log.header2("Trial "+str(seed+1))
364 242  
365 243 # Simulate events
366   - sim = obsutils.sim(self.obs,
367   - nbins=self.m_enumbins,
  244 + sim = obsutils.sim(self._obs,
  245 + nbins=self._enumbins,
368 246 seed=seed,
369   - binsz=self.m_binsz,
370   - npix=self.m_npix,
371   - log=self.m_log, debug=self.m_debug)
  247 + binsz=self._binsz,
  248 + npix=self._npix,
  249 + log=self._log_clients,
  250 + debug=self._debug)
372 251  
373 252 # Determine number of events in simulation
374 253 nevents = 0.0
... ... @@ -376,72 +255,76 @@ class cstsdist(ctools.cscript):
376 255 nevents += run.events().number()
377 256  
378 257 # Write simulation results
379   - if self.logExplicit():
380   - self.log.header3("Simulation")
381   - self.log.parformat("Number of simulated events")
382   - self.log(nevents)
383   - self.log("\n")
  258 + if self._logExplicit():
  259 + self._log.header3("Simulation")
  260 + self._log.parformat("Number of simulated events")
  261 + self._log(nevents)
  262 + self._log("\n")
384 263  
385 264 # Fit background only
386 265 sim.models(bkg_model)
387   - like_bgm = obsutils.fit(sim, log=self.m_log, debug=self.m_debug)
  266 + like_bgm = obsutils.fit(sim,
  267 + log=self._log_clients,
  268 + debug=self._debug)
388 269 result_bgm = like_bgm.obs().models().copy()
389 270 LogL_bgm = like_bgm.opt().value()
390 271 npred_bgm = like_bgm.obs().npred()
391 272  
392 273 # Write background fit results
393   - if self.logExplicit():
394   - self.log.header3("Background model fit")
395   - self.log.parformat("log likelihood")
396   - self.log(LogL_bgm)
397   - self.log("\n")
398   - self.log.parformat("Number of predicted events")
399   - self.log(npred_bgm)
400   - self.log("\n")
  274 + if self._logExplicit():
  275 + self._log.header3("Background model fit")
  276 + self._log.parformat("log likelihood")
  277 + self._log(LogL_bgm)
  278 + self._log("\n")
  279 + self._log.parformat("Number of predicted events")
  280 + self._log(npred_bgm)
  281 + self._log("\n")
401 282 for model in result_bgm:
402   - self.log.parformat("Model")
403   - self.log(model.name())
404   - self.log("\n")
  283 + self._log.parformat("Model")
  284 + self._log(model.name())
  285 + self._log("\n")
405 286 for par in model:
406   - self.log(str(par)+"\n")
  287 + self._log(str(par)+"\n")
407 288  
408 289 # Fit background and test source
409 290 sim.models(full_model)
410   - like_all = obsutils.fit(sim, log=self.m_log, debug=self.m_debug)
  291 + like_all = obsutils.fit(sim,
  292 + log=self._log_clients,
  293 + debug=self._debug)
411 294 result_all = like_all.obs().models().copy()
412 295 LogL_all = like_all.opt().value()
413 296 npred_all = like_all.obs().npred()
414 297 ts = 2.0*(LogL_bgm-LogL_all)
415 298  
416 299 # Write background and test source fit results
417   - if self.logExplicit():
418   - self.log.header3("Background and test source model fit")
419   - self.log.parformat("Test statistics")
420   - self.log(ts)
421   - self.log("\n")
422   - self.log.parformat("log likelihood")
423   - self.log(LogL_all)
424   - self.log("\n")
425   - self.log.parformat("Number of predicted events")
426   - self.log(npred_all)
427   - self.log("\n")
  300 + if self._logExplicit():
  301 + self._log.header3("Background and test source model fit")
  302 + self._log.parformat("Test statistics")
  303 + self._log(ts)
  304 + self._log("\n")
  305 + self._log.parformat("log likelihood")
  306 + self._log(LogL_all)
  307 + self._log("\n")
  308 + self._log.parformat("Number of predicted events")
  309 + self._log(npred_all)
  310 + self._log("\n")
428 311 for model in result_all:
429   - self.log.parformat("Model")
430   - self.log(model.name())
431   - self.log("\n")
  312 + self._log.parformat("Model")
  313 + self._log(model.name())
  314 + self._log("\n")
432 315 for par in model:
433   - self.log(str(par)+"\n")
  316 + self._log(str(par)+"\n")
434 317  
435 318 # Write result
436   - elif self.logTerse():
437   - self.log.parformat("Trial "+str(seed))
438   - self.log("TS=")
439   - self.log(ts)
440   - self.log(" Prefactor=")
441   - self.log(result_all[self.m_srcname]["Prefactor"].value())
442   - self.log("+/-")
443   - self.log(result_all[self.m_srcname]["Prefactor"].error())
444   - self.log("\n")
  319 + elif self._logTerse():
  320 + self._log.parformat("Trial "+str(seed))
  321 + self._log("TS=")
  322 + self._log(ts)
  323 + self._log(" Prefactor=")
  324 + self._log(result_all[self._srcname]["Prefactor"].value())
  325 + self._log("+/-")
  326 + self._log(result_all[self._srcname]["Prefactor"].error())
  327 + self._log("\n")
445 328  
446 329 # Initialise results
447 330 colnames = []
... ... @@ -498,18 +381,105 @@ class cstsdist(ctools.cscript):
498 381 return result
499 382  
500 383  
  384 + # Public methods
  385 + def run(self):
  386 + """
  387 + Run the script.
  388 + """
  389 + # Switch screen logging on in debug mode
  390 + if self._logDebug():
  391 + self._log.cout(True)
  392 +
  393 + # Get parameters
  394 + self._get_parameters()
  395 +
  396 + # Initialise models
  397 + full_model, bkg_model = self._set_models()
  398 +
  399 + # Write models into logger
  400 + if self._logTerse():
  401 + self._log("\n")
  402 + self._log.header1("Models")
  403 + self._log.header2("Background model")
  404 + self._log(str(bkg_model))
  405 + self._log("\n\n")
  406 + self._log.header2("Full model")
  407 + self._log(str(full_model))
  408 + self._log("\n")
  409 +
  410 + # Write observation into logger
  411 + if self._logTerse():
  412 + self._log("\n")
  413 + self._log.header1("Observation")
  414 + self._log(str(self._obs))
  415 + self._log("\n")
  416 +
  417 + # Write models into logger
  418 + if self._logTerse():
  419 + self._log("\n")
  420 + self._log.header1("Test model")
  421 + self._log(str(full_model))
  422 + self._log("\n")
  423 +
  424 + # Write header
  425 + if self._logTerse():
  426 + self._log("\n")
  427 + self._log.header1("Generate TS distribution")
  428 +
  429 + # Loop over trials
  430 + for seed in range(self._ntrials):
  431 +
  432 + # Make a trial
  433 + result = self._trial(seed, full_model, bkg_model)
  434 +
  435 + # Write out result immediately
  436 + if seed == 0:
  437 + file = open(self._outfile.url(), 'w')
  438 + writer = csv.DictWriter(file, result['colnames'])
  439 + headers = {}
  440 + for n in result['colnames']:
  441 + headers[n] = n
  442 + writer.writerow(headers)
  443 + else:
  444 + file = open(self._outfile.url(), 'a')
  445 + writer = csv.DictWriter(file, result['colnames'])
  446 + writer.writerow(result['values'])
  447 + file.close()
  448 +
  449 + # Return
  450 + return
  451 +
  452 + def models(self, models):
  453 + """
  454 + Set model.
  455 + """
  456 + # Copy models
  457 + self._obs.models(models.copy())
  458 +
  459 + # Return
  460 + return
  461 +
  462 + def execute(self):
  463 + """
  464 + Execute the script.
  465 + """
  466 + # Run the script
  467 + self.run()
  468 +
  469 + # Return
  470 + return
  471 +
  472 +
501 473 # ======================== #
502 474 # Main routine entry point #
503 475 # ======================== #
504 476 if __name__ == '__main__':
505   - """
506   - Generates TS distribution.
507   - """
  477 +
508 478 # Create instance of application
509 479 app = cstsdist(sys.argv)
510 480  
511 481 # Open logfile
512   - app.logFileOpen()
  482 + app._logFileOpen()
513 483  
514 484 # Execute application
515 485 app.execute()
... ...
cscripts/cstsmapmerge.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: cstsmapmerge.par #
  8 +# Version: 1.1.0 #
  9 +# #
  10 +# Author: Juergen Knoedlseder #
  11 +# Jurgen.Knodlseder@irap.omp.eu #
  12 +# IRAP #
  13 +# #
  14 +# Purpose: Parameter file for the cstsmapmerge script #
  15 +# #
  16 +#############################################################################
  17 +#
  18 +# Input and output parameters
  19 +#============================
  20 +inmaps, s, a, tsmap.fits,,, "Input TS map FITS files"
  21 +outmap, f, a, NONE,,, "Output TS map FITS file"
  22 +
  23 +#
  24 +# Script parameters
  25 +#==================
  26 +overwrite, b, h, yes,,, "Overwrite previously filled bins?"
  27 +delete, b, h, no,,, "Delete merged files?"
  28 +
  29 +#
  30 +# Standard parameters
  31 +#====================
  32 +chatter, i, h, 2,0,4, "Chattiness of output"
  33 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  34 +debug, b, h, no,,, "Debugging mode activated"
  35 +mode, s, h, ql,,, "Mode of automatic parameters"
  36 +logfile, f, h, cstsmapmerge.log,,, "Log filename"
  37 +
  38 +
... ...
cscripts/cstsmapmerge.py
1 1 #!/usr/bin/env python
2 2 # ==========================================================================
3   -# Merges sliced TS map files into one map
  3 +# Merge Test Statistic maps
4 4 #
5 5 # Copyright (C) 2015-2016 Michael Mayer
6 6 #
... ... @@ -30,30 +30,42 @@ import os
30 30 # ================== #
31 31 class cstsmapmerge(ctools.cscript):
32 32 """
33   - This class merges TS map files into one TS map
  33 + Merge Test Statistic maps.
34 34 """
  35 +
  36 + # Constructors and destructors
35 37 def __init__(self, *argv):
36 38 """
37 39 Constructor.
38 40 """
39 41 # Set name
40   - self.name = "cstsmapmerge"
41   - self.version = "1.1.0"
  42 + self._name = "cstsmapmerge"
  43 + self._version = "1.1.0"
42 44  
43   - # Make sure that parfile exists
44   - file = self.parfile()
  45 + # Initialise class members
  46 + self._files = None
  47 + self._in_filename = ""
  48 + self._tsmap = gammalib.GSkyMap() # Empty sky map
  49 + self._statusmap = gammalib.GSkyMap() # Empty sky map
  50 + self._maps = []
  51 + self._mapnames = []
  52 + self._merged_files = []
  53 + self._overwrite = True
  54 + self._delete = False
  55 + self._outmap = "NONE"
  56 + self._read_ahead = False
45 57  
46 58 # Initialise application
47 59 if len(argv) == 0:
48   - ctools.cscript.__init__(self, self.name, self.version)
  60 + ctools.cscript.__init__(self, self._name, self._version)
49 61 elif len(argv) == 1:
50   - ctools.cscript.__init__(self, self.name, self.version, *argv)
  62 + ctools.cscript.__init__(self, self._name, self._version, *argv)
51 63 else:
52 64 raise TypeError("Invalid number of arguments given.")
53 65  
54 66 # Set logger properties
55   - self.log_header()
56   - self.log.date(True)
  67 + self._log_header()
  68 + self._log.date(True)
57 69  
58 70 # Return
59 71 return
... ... @@ -65,98 +77,79 @@ class cstsmapmerge(ctools.cscript):
65 77 # Return
66 78 return
67 79  
68   - def parfile(self):
69   - """
70   - Check if parfile exists. If parfile does not exist then create a
71   - default parfile. This kluge avoids shipping the cscript with a
72   - parfile.
73   - """
74   -
75   - # Set parfile name
76   - parfile = self.name+".par"
77   -
78   - try:
79   - pars = gammalib.GApplicationPars(parfile)
80   - except:
81   - # Signal that parfile was not found
82   - print("Parfile \""+parfile+"\" not found. Create default parfile.")
83   -
84   - # Create default parfile
85   - pars = gammalib.GApplicationPars()
86   - pars.append(gammalib.GApplicationPar("inmaps","s","a","tsmap.fits","","","Input TS map FITS files"))
87   - pars.append(gammalib.GApplicationPar("outmap","f","a","NONE","","","Output TS map FITS file"))
88   - pars.append(gammalib.GApplicationPar("overwrite","b","h","yes","","","Overwrite previously filled bins?"))
89   - pars.append(gammalib.GApplicationPar("delete","b","h","no","","","Delete merged files?"))
90   - pars.append_standard()
91   - pars.append(gammalib.GApplicationPar("logfile","f","h","cstsmapmerge.log","","","Log filename"))
92   - pars.save(parfile)
93   -
94   - # Return
95   - return
96 80  
97   - def get_parameters(self):
  81 + # Private methods
  82 + def _get_parameters(self):
98 83 """
99   - Get parameters from parfile and setup the observation.
  84 + Get parameters from parfile.
100 85 """
101   - # Get input models
102   - self.inmodels = self["inmaps"].string()
  86 + # Get input maps string
  87 + inmaps = self["inmaps"].string()
103 88  
104   - # Interpet inmodels string
105 89 # Handle ascii files
106   - if "@" == self.inmodels[0]:
107   - self.files = open(self.inmodels.replace("@","")).read().splitlines()
  90 + if "@" == inmaps[0]:
  91 + self._files = open(inmaps.replace("@","")).read().splitlines()
108 92  
109 93 # Handle wild card strings
110   - elif "*" in self.inmodels:
111   - self.files = glob.glob(self.inmodels)
  94 + elif "*" in inmaps:
  95 + self._files = glob.glob(inmaps)
112 96  
113 97 # Handle space-separated list
114   - elif " " in self.inmodels:
115   - self.files = self.inmodels.split(" ")
  98 + elif " " in inmaps:
  99 + self._files = inmaps.split(" ")
116 100  
117   - # Handle comma-separated list
118   - elif "," in self.inmodels:
119   - self.files = self.inmodels.split(",")
  101 + # Handle semi-colon separated list
  102 + elif ";" in inmaps:
  103 + self._files = inmaps.split(";")
120 104  
121 105 # Throw exception if input models cannot be decoded
122 106 else:
123   - msg = "Parameter \"inmmaps\" must contain either an @ASCII file,"+\
124   - " a comma-separated or whitespace-separated list of files"+\
125   - " or a wildcard string"
  107 + msg = "Parameter \"inmaps\" must contain either an @ASCII "\
  108 + "file, a semi-colon-separated or whitespace-separated "\
  109 + "list of files or a wildcard string."
126 110 raise RuntimeError(msg)
127 111  
128 112 # Check number of files. We need at least two files.
129   - if len(self.files) <= 1:
  113 + if len(self._files) <= 1:
130 114 msg = "Need at least two files to start merging, "+\
131   - str(len(self.files))+" files given."
  115 + str(len(self._files))+" file(s) given."
132 116 raise RuntimeError(msg)
133   -
134   - # Get output file
135   - self.outmap = self["outmap"].filename()
136   -
  117 +
137 118 # Get other parameters
138   - self.overwrite = self["overwrite"].boolean()
139   - self.delete = self["delete"].boolean()
  119 + self._overwrite = self["overwrite"].boolean()
  120 + self._delete = self["delete"].boolean()
140 121  
  122 + # Read ahead output filename
  123 + if self._read_ahead:
  124 + self._outmap = self["outmap"].filename()
  125 +
  126 + # Write input parameters into logger
  127 + if self._logTerse():
  128 + self._log_parameters()
  129 + self._log("\n")
  130 +
141 131 # Return
142 132 return
143 133  
144   - def init_map(self, fitsfile):
  134 + def _init_ts_map(self, fitsfile):
145 135 """
  136 + Initialise Test Statistic map.
146 137 """
147 138 # Set filename
148   - self.in_filename = fitsfile
  139 + self._in_filename = fitsfile
149 140  
150 141 # Open FITS file
151   - fits = gammalib.GFits(fitsfile)
152   - self.tsmap = gammalib.GSkyMap()
153   - self.tsmap.read(fits[0])
154   - self.statusmap = gammalib.GSkyMap()
155   - self.statusmap.read(fits["STATUS MAP"])
  142 + fits = gammalib.GFits(fitsfile)
  143 +
  144 + # Read TS and status maps
  145 + self._tsmap = gammalib.GSkyMap()
  146 + self._tsmap.read(fits[0])
  147 + self._statusmap = gammalib.GSkyMap()
  148 + self._statusmap.read(fits["STATUS MAP"])
156 149  
157 150 # Get other maps
158   - self.maps = []
159   - self.mapnames = []
  151 + self._maps = []
  152 + self._mapnames = []
160 153  
161 154 # Loop over extensions
162 155 for hdu in fits:
... ... @@ -167,8 +160,8 @@ class cstsmapmerge(ctools.cscript):
167 160 # Add present maps
168 161 skymap = gammalib.GSkyMap()
169 162 skymap.read(hdu)
170   - self.maps.append(skymap)
171   - self.mapnames.append(hdu.extname())
  163 + self._maps.append(skymap)
  164 + self._mapnames.append(hdu.extname())
172 165  
173 166 # Close FITS file
174 167 fits.close()
... ... @@ -176,11 +169,17 @@ class cstsmapmerge(ctools.cscript):
176 169 # Return
177 170 return
178 171  
179   - # Add map instances to each other. In this way the TS maps get merged
180   - def add(self, fitsfile):
181   -
182   - # open FITS file
183   - fits = gammalib.GFits(fitsfile)
  172 + def _merge_ts_map(self, fitsfile):
  173 + """
  174 + Merge TS map from FITS file into output TS map.
  175 +
  176 + Args:
  177 + fitsfile: FITS file to be merged.
  178 + """
  179 + # Open FITS file
  180 + fits = gammalib.GFits(fitsfile)
  181 +
  182 + # Read TS and status maps
184 183 add_tsmap = gammalib.GSkyMap()
185 184 add_tsmap.read(fits[0])
186 185 add_statusmap = gammalib.GSkyMap()
... ... @@ -203,175 +202,240 @@ class cstsmapmerge(ctools.cscript):
203 202 # Close FITS file
204 203 fits.close()
205 204  
206   - # Compare size of other maps
207   - if not len(add_maps) == len(self.maps):
208   - self.error("Maps cannot be added: Maps from \""+self.in_filename+"\" do not match from \""+fitsfile+"\"")
  205 + # Compare size of maps
  206 + if not len(add_maps) == len(self._maps):
  207 + msg = "Cannot merge map \""+fitsfile+"\" into map \""+\
  208 + self._in_filename+"\" since the number of parameters "\
  209 + "between both maps is different."
  210 + raise RuntimeError(msg)
209 211  
210 212 # Loop over bins
211   - for i in range(self.tsmap.npix()):
  213 + for i in range(self._tsmap.npix()):
  214 +
  215 + # Consider only bins that have been computed
212 216 if add_statusmap[i] > 0.5:
213 217  
214   - # Throw exception if this bin has already been computed
215   - if self.statusmap[i] > 0.5 and not self.overwrite:
216   - msg = "Attempt to merge bin which apparently has already"+\
217   - " been merged. File \""+fitsfile+"\" contains"+\
218   - " already merged bins. Set hidden parameter"+\
219   - " overwrite=yes to avoid this error."
  218 + # Raise exception if this bin has already been computed
  219 + if self._statusmap[i] > 0.5 and not self._overwrite:
  220 + msg = "Attempt to merge bin which apparently has "+\
  221 + "already been merged. File \""+fitsfile+"\" "+\
  222 + "contains already merged bins. Set hidden "+\
  223 + "parameter \"overwrite=yes\" to avoid this error."
220 224 raise RuntimeError(msg)
221 225  
222 226 # Copy TS values
223   - self.tsmap[i] = add_tsmap[i]
  227 + self._tsmap[i] = add_tsmap[i]
224 228  
225 229 # Copy status
226   - self.statusmap[i] = add_statusmap[i]
  230 + self._statusmap[i] = add_statusmap[i]
227 231  
228 232 # Loop over maps and copy entries
229   - for j in range(len(self.maps)):
230   - self.maps[j][i] = add_maps[j][i]
  233 + for j in range(len(self._maps)):
  234 + self._maps[j][i] = add_maps[j][i]
231 235  
232 236 # Return
233 237 return
234 238  
  239 + def _get_number_of_ts_pixels(self, fitsfile):
  240 + """
  241 + Return number of pixels with TS values.
  242 +
  243 + Args:
  244 + fitsfile: FITS file to be merged.
  245 +
  246 + Returns:
  247 + Number of pixels for which TS has been computed.
  248 + """
  249 + # Get status map for this file
  250 + status = gammalib.GSkyMap(fitsfile+"[STATUS MAP]")
  251 +
  252 + # Count number of pixels with TS status set
  253 + count = 0
  254 + for pix in status:
  255 + if pix > 0.5:
  256 + count += 1
  257 +
  258 + # Return number of pixels
  259 + return count
  260 +
  261 +
  262 + # Public methods
235 263 def run(self):
236 264 """
237 265 Run the script.
238 266 """
239 267 # Switch screen logging on in debug mode
240   - if self.logDebug():
241   - self.log.cout(True)
  268 + if self._logDebug():
  269 + self._log.cout(True)
242 270  
243 271 # Get parameters
244   - self.get_parameters()
  272 + self._get_parameters()
245 273  
246   - # Write input parameters into logger
247   - if self.logTerse():
248   - self.log_parameters()
249   - self.log("\n")
250   -
251   - if self.logTerse():
252   - self.log("\n")
253   - self.log.header1("Merging sliced TS map files")
254   - self.log("\n")
  274 + # Write header into logger
  275 + if self._logTerse():
  276 + self._log("\n")
  277 + self._log.header1("Merge TS maps")
255 278  
256 279 # Initialise file to start with
257   - file0 = ""
258   -
259   - self.added_files = []
  280 + file0 = ""
  281 + self._merged_files = []
260 282  
261   - # Test files for the entry status map
262   - # use the first one to appear useful
263   - for fitsfile in self.files:
  283 + # Test files for the entry status map. Use the first one to appear
  284 + # useful
  285 + for fitsfile in self._files:
  286 +
  287 + # Skip file if it's not a FITS file
264 288 if not gammalib.GFilename(fitsfile).is_fits():
265   - self.log("Skipping file \""+fitsfile+" (not a FITS file)\n")
  289 + if self._logExplicit():
  290 + self._log(gammalib.parformat("Skip file"))
  291 + self._log(fitsfile)
  292 + self._log(" (not a FITS file)\n")
  293 + continue
  294 +
  295 + # Open FITS file
266 296 fits = gammalib.GFits(fitsfile)
267   -
  297 +
  298 + # If file contains a status map then use it
268 299 if fits.contains("STATUS MAP"):
269   - self.log("Using file \""+fitsfile+"\" as initialising map\n")
  300 + fits.close()
270 301 file0 = fitsfile
  302 + if self._logTerse():
  303 + self._log(gammalib.parformat("Initial TS map file"))
  304 + self._log(fitsfile)
  305 + count = self._get_number_of_ts_pixels(fitsfile)
  306 + self._log(" (%d TS pixels computed)" % count)
  307 + self._log("\n")
271 308 break
  309 +
  310 + # ... otherwise signal that file is useless
272 311 else:
273   - if self.logExplicit():
274   - self.log("File \""+fitsfile+"\" has not \"STATUS MAP\" extension\n")
275   -
276   - # Print an error message if no suitable file was provided
  312 + fits.close()
  313 + if self._logExplicit():
  314 + self._log(gammalib.parformat("Skip file"))
  315 + self._log(fitsfile)
  316 + self._log(" (no \"STATUS MAP\" extension)\n")
  317 + continue
  318 +
  319 + # Signal if no suitable file was found
277 320 if file0 == "":
278   - if self.logTerse():
279   - self.log("None of the provided file seems to be a sliced ts map file (no \"STATUS MAP\" extension)\n")
  321 + if self._logTerse():
  322 + self._log("None of the provided files seems to be a sliced "
  323 + "TS map file (none has a \"STATUS MAP\" "
  324 + "extension).\n")
280 325  
281   - # Otherwise merge files
  326 + # ... otherwise merge files
282 327 else:
283 328  
284   - # copy file list
285   - workfiles = self.files
  329 + # Copy file list
  330 + workfiles = self._files
286 331  
287 332 # Remove entry which will be used to initalise the map
288 333 workfiles.remove(file0)
289 334  
290 335 # Initialise map from first file
291   - self.init_map(file0)
  336 + self._init_ts_map(file0)
292 337  
293 338 # Append to added files
294   - self.added_files.append(file0)
  339 + self._merged_files.append(file0)
295 340  
296 341 # Loop over files
297 342 for fitsfile in workfiles:
298 343  
299 344 # Skip if file is not FITS
300 345 if not gammalib.GFilename(fitsfile).is_fits():
301   - if self.logTerse():
302   - self.log("Skipping file \""+fitsfile+" (not a FITS file)\n")
  346 + if self._logExplicit():
  347 + self._log(gammalib.parformat("Skip file"))
  348 + self._log(fitsfile)
  349 + self._log(" (not a FITS file)\n")
303 350 continue
  351 +
  352 + # Open FITS file
  353 + fits = gammalib.GFits(fitsfile)
304 354  
305 355 # Skip if file does not contain status map
306   - fits = gammalib.GFits(fitsfile)
307 356 if not fits.contains("STATUS MAP"):
308   - if self.logTerse():
309   - self.log("Skipping file \""+fitsfile+" (not a FITS file)\n")
  357 + fits.close()
  358 + if self._logExplicit():
  359 + self._log(gammalib.parformat("Skip file"))
  360 + self._log(fitsfile)
  361 + self._log(" (no \"STATUS MAP\" extension)\n")
310 362 continue
  363 +
  364 + # Close FITS file
  365 + fits.close()
311 366  
312 367 # Logging
313   - if self.logTerse():
314   - self.log("Adding TS map slice \""+fitsfile+"\"\n")
  368 + if self._logTerse():
  369 + self._log(gammalib.parformat("Merge TS map file"))
  370 + self._log(fitsfile)
  371 + count = self._get_number_of_ts_pixels(fitsfile)
  372 + self._log(" (%d TS pixels computed)" % count)
  373 + self._log("\n")
315 374  
316   - # Add file
317   - self.add(fitsfile)
  375 + # Merge TS map
  376 + self._merge_ts_map(fitsfile)
318 377  
319   - # Append to added files
320   - self.added_files.append(fitsfile)
  378 + # Append FITS file to merged files
  379 + self._merged_files.append(fitsfile)
321 380  
322 381 # Return
323 382 return
324 383  
325 384 def save(self):
326 385 """
327   - Save merged tsmap file and remove slices if requested
  386 + Save TS map and remove slices if requested.
328 387 """
329   -
330   - # Logging
331   - if self.logExplicit():
332   - self.log("\n")
333   - self.log.header1("Saving merged TS map")
334   - self.log("\n")
  388 + # Write header
  389 + if self._logTerse():
  390 + self._log("\n")
  391 + self._log.header1("Save TS map")
  392 +
  393 + # Get output filename in case it was not read ahead
  394 + self._outmap = self["outmap"].filename()
  395 +
  396 + # Log filename
  397 + if self._logTerse():
  398 + self._log(gammalib.parformat("TS map file"))
  399 + self._log(self._outmap.url())
  400 + self._log("\n")
335 401  
336 402 # Create FITS file
337 403 fits = gammalib.GFits()
338 404  
339 405 # Write TS map into primary
340   - self.tsmap.write(fits)
  406 + self._tsmap.write(fits)
341 407  
342 408 # Loop over maps and write them to fits
343   - for i in range(len(self.maps)):
344   - self.maps[i].write(fits)
  409 + for i in range(len(self._maps)):
  410 + self._maps[i].write(fits)
345 411  
346 412 # Set map names as extensions
347   - for i in range(len(self.mapnames)):
348   - fits[i+1].extname(self.mapnames[i])
  413 + for i in range(len(self._mapnames)):
  414 + fits[i+1].extname(self._mapnames[i])
349 415  
350 416 # Check if map is fully done
351 417 done = True
352   - for pix in self.statusmap:
  418 + for pix in self._statusmap:
353 419 if pix < 0.5:
354 420 done = False
355 421 break
356 422  
357   - # Write Status map if we are not done yet
  423 + # Write status map if we are not done yet
358 424 if not done:
359   - self.statusmap.write(fits)
  425 + self._statusmap.write(fits)
360 426 fits[fits.size()-1].extname("STATUS MAP")
361 427  
362 428 # Save FITS file
363   - fits.saveto(self.outmap, self.clobber())
364   - if self.logExplicit():
365   - self.log("Saved output map to \""+self.outmap+"\"\n")
366   - if self.delete:
367   - if self.logTerse():
368   - self.log("\n")
369   - self.log.header1("Deleting slices")
370   - self.log("\n")
371   - for filename in self.added_files:
372   - if self.logExplicit():
373   - self.log("Deleting file \""+filename+"\"\n")
374   - os.remove(filename)
  429 + fits.saveto(self._outmap, self._clobber())
  430 +
  431 + # Delete TS input maps if requested
  432 + if self._delete:
  433 + for filename in self._merged_files:
  434 + os.remove(filename)
  435 + if self._logTerse():
  436 + self._log(gammalib.parformat("Deleted input file"))
  437 + self._log(filename)
  438 + self._log("\n")
375 439  
376 440 # Return
377 441 return
... ... @@ -380,10 +444,13 @@ class cstsmapmerge(ctools.cscript):
380 444 """
381 445 Execute the script.
382 446 """
  447 + # Set read ahead flag
  448 + self._read_ahead = True
  449 +
383 450 # Run the script
384 451 self.run()
385 452  
386   - # Save ds9 file if required
  453 + # Save TS map if required
387 454 self.save()
388 455  
389 456 # Return
... ... @@ -394,14 +461,12 @@ class cstsmapmerge(ctools.cscript):
394 461 # Main routine entry point #
395 462 # ======================== #
396 463 if __name__ == '__main__':
397   - """
398   - Merges ts map slices.
399   - """
  464 +
400 465 # Create instance of application
401 466 app = cstsmapmerge(sys.argv)
402 467  
403 468 # Open logfile
404   - app.logFileOpen()
  469 + app._logFileOpen()
405 470  
406 471 # Execute application
407 472 app.execute()
... ...
test/test_cscripts.sh.in
... ... @@ -173,9 +173,9 @@ else
173 173 $ECHO " merged_models.xml file is not valid"
174 174 exit 1
175 175 fi
176   -# === comma-separated ===
  176 +# === semi-colon separated ===
177 177 @PYTHON_COVERAGE@@abs_top_srcdir@/cscripts/csmodelmerge.py \
178   - inmodels="data/crab.xml,data/source1.xml" \
  178 + inmodels="data/crab.xml;data/source1.xml" \
179 179 outmodel="merged_models2.xml" > csmodelmerge.log
180 180 $ECHO -n "."
181 181 if [ -s "merged_models2.xml" ]
... ... @@ -218,10 +218,10 @@ rm models.txt
218 218 # Test cstsmapmerge
219 219 # =================
220 220 $ECHO -n "Test cstsmapmerge: "
221   -# === comma-separated ===
  221 +# === semi-colon separated ===
222 222 chmod +x @abs_top_srcdir@/cscripts/cstsmapmerge.py
223 223 @PYTHON_COVERAGE@@abs_top_srcdir@/cscripts/cstsmapmerge.py \
224   - inmaps="data/tsmap_0.fits,data/tsmap_1.fits" \
  224 + inmaps="data/tsmap_0.fits;data/tsmap_1.fits" \
225 225 outmap="merged_tsmap1.fits" > cstsmapmerge.log
226 226 $ECHO -n "."
227 227 if [ -s "merged_tsmap1.fits" ]
... ...