Commit 8d8bb720c913ea606da6d4d5cefaa743fdf5be73

Authored by Jürgen Knödlseder
1 parent f497c92a

Use input model for sensitivity computation and simplify the parameter interface (#1426)

ChangeLog
1 -2015-02-16 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 1 +2015-02-20 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
2 2
3 * Version 1.0.0 released 3 * Version 1.0.0 released
4 ======================== 4 ========================
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 Adapt to GammaLib 1.0.0 interface 6 Adapt to GammaLib 1.0.0 interface
7 Do not import automatically Python scripts 7 Do not import automatically Python scripts
8 Support INDEF/NONE/UNDEF/UNDEFINED parameters in ctselect 8 Support INDEF/NONE/UNDEF/UNDEFINED parameters in ctselect
  9 + Modify cssens parameter interface (#1426)
9 10
10 11
11 2015-01-08 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> 12 2015-01-08 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
@@ -2,7 +2,7 @@ New Features and Important Changes in ctools 1.0.0 @@ -2,7 +2,7 @@ New Features and Important Changes in ctools 1.0.0
2 2
3 Author(s) : Juergen Knoedlseder 3 Author(s) : Juergen Knoedlseder
4 4
5 -16 February 2015 5 +20 February 2015
6 6
7 7
8 1. Introduction 8 1. Introduction
@@ -99,7 +99,10 @@ None @@ -99,7 +99,10 @@ None
99 99
100 18. Scripts 100 18. Scripts
101 ----------- 101 -----------
102 -None 102 +The user parameter interface of cssens has been modified. The script
  103 +now reads a source model that is used for sensitivity estimation.
  104 +This provides more flexibility in the choice of the model for
  105 +sensitivity estimation.
103 106
104 107
105 19. caldb 108 19. caldb
doc/source/reference_manual/cssens.rst
@@ -12,19 +12,38 @@ Synopsis @@ -12,19 +12,38 @@ Synopsis
12 Computes the CTA sensitivity for a number of energy bins using :doc:`ctlike`. 12 Computes the CTA sensitivity for a number of energy bins using :doc:`ctlike`.
13 Crab spectra are fitted in narrow energy bins to simulated data, and the 13 Crab spectra are fitted in narrow energy bins to simulated data, and the
14 flux level is determined that leads to a particular significance. 14 flux level is determined that leads to a particular significance.
  15 +The significance is estimated using the Test Statistic value.
  16 +The simplified assumption is made that the significance (in Gaussian
  17 +sigma) is the square root of the Test Statistic.
15 18
16 19
17 General parameters 20 General parameters
18 ------------------ 21 ------------------
19 22
20 -``outfile [file]``  
21 - ASCII file containing the sensitivity values.  
22 - 23 +``(inobs = NONE) [file]``
  24 + Input event list, counts cube or observation definition XML file.
  25 +
  26 +``inmodel [file]``
  27 + Input source model XML file.
  28 +
  29 +``srcname [string]``
  30 + Name of the source in the source model XML file which should be used
  31 + for sensitivity computation.
  32 +
  33 +``(offset = 0.0) [real]``
  34 + Offset angle of source in field of view (in degrees).
  35 +
23 ``caldb [string]`` 36 ``caldb [string]``
24 Calibration database. 37 Calibration database.
25 38
26 ``irf [string]`` 39 ``irf [string]``
27 Instrumental response function. 40 Instrumental response function.
  41 +
  42 +``(deadc = 0.95) [real]``
  43 + Average deadtime correction factor.
  44 +
  45 +``outfile [file]``
  46 + ASCII file containing the sensitivity values.
28 47
29 ``type <point|gauss|shell|disk> [string]`` 48 ``type <point|gauss|shell|disk> [string]``
30 Source model type. 49 Source model type.
@@ -32,14 +51,20 @@ General parameters @@ -32,14 +51,20 @@ General parameters
32 ``offset [real]`` 51 ``offset [real]``
33 Source offset angle (in degrees). 52 Source offset angle (in degrees).
34 53
35 -``bkg [string]``  
36 - Background model file function (none=power law for subarray E).  
37 -  
38 ``duration [real]`` 54 ``duration [real]``
39 Effective exposure time (in seconds). 55 Effective exposure time (in seconds).
40 56
41 ``rad [real]`` 57 ``rad [real]``
42 - Radius of ROI (in degrees). 58 + Radius of Region of Interest (RoI) (in degrees).
  59 +
  60 +``emin [real]``
  61 + Lower energy limit for sensitivity computation (in TeV).
  62 +
  63 +``emax [real]``
  64 + Upper energy limit for sensitivity computation (in TeV).
  65 +
  66 +``bins [integer]``
  67 + Number of energy bins for sensitivity computation.
43 68
44 ``(enumbins = 0) [integer]`` 69 ``(enumbins = 0) [integer]``
45 Number of energy bins (0=unbinned). 70 Number of energy bins (0=unbinned).
@@ -53,18 +78,6 @@ General parameters @@ -53,18 +78,6 @@ General parameters
53 ``(sigma = 5.0) [real]`` 78 ``(sigma = 5.0) [real]``
54 Significance threshold. 79 Significance threshold.
55 80
56 -``(ts_use = yes) [boolean]``  
57 - Use TS for signifiance estimation?  
58 -  
59 -``(index = -2.48) [real]``  
60 - Spectral index for source model.  
61 -  
62 -``(radius = 0.1) [real]``  
63 - Extended source model radius (in degrees).  
64 -  
65 -``(width = 0.05) [real]``  
66 - Extended source model width (in degrees).  
67 -  
68 ``(max_iter = 50) [integer]`` 81 ``(max_iter = 50) [integer]``
69 Maximum number of iterations. 82 Maximum number of iterations.
70 83
@@ -96,6 +109,9 @@ Standard parameters @@ -96,6 +109,9 @@ Standard parameters
96 ``(mode = ql) [string]`` 109 ``(mode = ql) [string]``
97 Mode of automatic parameters (default is "ql", i.e. "query and learn"). 110 Mode of automatic parameters (default is "ql", i.e. "query and learn").
98 111
  112 +``(logfile = cssens.log) [filename]``
  113 + Log filename.
  114 +
99 115
100 Related tools 116 Related tools
101 ------------- 117 -------------
doc/source/reference_manual/cttsmap.rst
@@ -3,13 +3,13 @@ @@ -3,13 +3,13 @@
3 cttsmap 3 cttsmap
4 ======= 4 =======
5 5
6 -Generate a Test Statistics map. 6 +Generate a Test Statistic map.
7 7
8 8
9 Synopsis 9 Synopsis
10 -------- 10 --------
11 11
12 -This tool generates a test statistics map. 12 +This tool generates a Test Statistic map.
13 13
14 14
15 General parameters 15 General parameters
scripts/cssens.py
1 #! /usr/bin/env python 1 #! /usr/bin/env python
2 # ========================================================================== 2 # ==========================================================================
3 -# This script computes the CTA sensitivity for a number of energy bins using  
4 -# ctlike. Crab spectra are fitted in narrow energy bins to simulated data,  
5 -# and the flux level is determined that leads to a particular significance.  
6 -#  
7 -# The significance can be either determined using the Test Statistic (which  
8 -# is defined as twice the likelihood difference between fitting with and  
9 -# without the test source) or using the source model scaling factor (or  
10 -# Prefactor) divided by its statistical error. The latter has been turned  
11 -# out to be more reliable, and is used as the default parameter.  
12 -#  
13 -# As background model, a GCTAModelRadialGauss model is used for the spatial  
14 -# component. For the spectral component, either a file function is used  
15 -# (specified by the "bkg" parameter), or if left blank, a power law adjusted  
16 -# to Konrad's configuration "E" performance file is employed.  
17 -#  
18 -# The source is modelled as a point source 3 +# This script computes the CTA sensitivity.
19 # 4 #
20 # Copyright (C) 2011-2015 Juergen Knoedlseder 5 # Copyright (C) 2011-2015 Juergen Knoedlseder
21 # 6 #
@@ -46,10 +31,12 @@ import math @@ -46,10 +31,12 @@ import math
46 # ============ # 31 # ============ #
47 class cssens(ctools.cscript): 32 class cssens(ctools.cscript):
48 """ 33 """
49 - This class implements the sensitivity computation script. It derives  
50 - from the ctools.cscript class which provides support for parameter  
51 - files, command line arguments, and logging. In that way the Python  
52 - script behaves just as a regular ctool. 34 + This class computes the CTA sensitivity for a number of energy bins using
  35 + ctlike. Spectra are fitted in narrow energy bins to simulated data,
  36 + and the flux level is determined that leads to a particular significance.
  37 +
  38 + The significance is determined using the Test Statistic, defined as twice
  39 + the likelihood difference between fitting with and without the test source.
53 """ 40 """
54 def __init__(self, *argv): 41 def __init__(self, *argv):
55 """ 42 """
@@ -58,6 +45,11 @@ class cssens(ctools.cscript): @@ -58,6 +45,11 @@ class cssens(ctools.cscript):
58 # Set name 45 # Set name
59 self.name = "cssens" 46 self.name = "cssens"
60 self.version = "1.0.0" 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
61 53
62 # Make sure that parfile exists 54 # Make sure that parfile exists
63 file = self.parfile() 55 file = self.parfile()
@@ -101,29 +93,31 @@ class cssens(ctools.cscript): @@ -101,29 +93,31 @@ class cssens(ctools.cscript):
101 pars = gammalib.GApplicationPars(parfile) 93 pars = gammalib.GApplicationPars(parfile)
102 except: 94 except:
103 # Signal if parfile was not found 95 # Signal if parfile was not found
104 - sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n") 96 + print("Parfile "+parfile+" not found. Create default parfile.")
105 97
106 # Create default parfile 98 # Create default parfile
107 pars = gammalib.GApplicationPars() 99 pars = gammalib.GApplicationPars()
108 - pars.append(gammalib.GApplicationPar("outfile","f","h","sensitivity.dat","","","Output file name"))  
109 - pars.append(gammalib.GApplicationPar("caldb","s","a","$CTOOLS/share/caldb/data/cta/dummy","","","Calibration database")) 100 + pars.append(gammalib.GApplicationPar("inobs","f","h","NONE","","","Event list, counts cube, or observation definition file"))
  101 + pars.append(gammalib.GApplicationPar("inmodel","f","a","$CTOOLS/share/models/crab.xml","","","Source model"))
  102 + pars.append(gammalib.GApplicationPar("srcname","s","a","Crab","","","Source name"))
  103 + pars.append(gammalib.GApplicationPar("offset","r","h","0.0","","","Source offset angle (deg)"))
  104 + pars.append(gammalib.GApplicationPar("caldb","s","a","dummy","","","Calibration database"))
110 pars.append(gammalib.GApplicationPar("irf","s","a","cta_dummy_irf","","","Instrument response function")) 105 pars.append(gammalib.GApplicationPar("irf","s","a","cta_dummy_irf","","","Instrument response function"))
111 - pars.append(gammalib.GApplicationPar("type","s","a","point","","","Source model type (point/gauss/shell/disk)"))  
112 - pars.append(gammalib.GApplicationPar("offset","r","a","0.0","","","Source offset angle (deg)"))  
113 - pars.append(gammalib.GApplicationPar("bkg","s","a","$CTOOLS/share/models/bkg_dummy.txt","","","Background model file function (none=power law for E)")) 106 + pars.append(gammalib.GApplicationPar("deadc","r","h","0.95","0","1","Deadtime correction factor"))
  107 + pars.append(gammalib.GApplicationPar("outfile","f","h","sensitivity.dat","","","Output file name"))
114 pars.append(gammalib.GApplicationPar("duration","r","a","180000.0","","","Effective exposure time (s)")) 108 pars.append(gammalib.GApplicationPar("duration","r","a","180000.0","","","Effective exposure time (s)"))
115 pars.append(gammalib.GApplicationPar("rad","r","a","5.0","","","Radius of ROI (deg)")) 109 pars.append(gammalib.GApplicationPar("rad","r","a","5.0","","","Radius of ROI (deg)"))
  110 + pars.append(gammalib.GApplicationPar("emin","r","a","0.020","","","Lower energy limit (TeV)"))
  111 + pars.append(gammalib.GApplicationPar("emax","r","a","200.0","","","Upper energy limit (TeV)"))
  112 + pars.append(gammalib.GApplicationPar("bins","i","a","21","","","Number of energy bins for differential sensitivity computation"))
116 pars.append(gammalib.GApplicationPar("enumbins","i","h","0","","","Number of energy bins (0=unbinned)")) 113 pars.append(gammalib.GApplicationPar("enumbins","i","h","0","","","Number of energy bins (0=unbinned)"))
117 pars.append(gammalib.GApplicationPar("npix","i","h","200","","","Number of pixels for binned")) 114 pars.append(gammalib.GApplicationPar("npix","i","h","200","","","Number of pixels for binned"))
118 pars.append(gammalib.GApplicationPar("binsz","r","h","0.05","","","Pixel size for binned (deg/pixel)")) 115 pars.append(gammalib.GApplicationPar("binsz","r","h","0.05","","","Pixel size for binned (deg/pixel)"))
119 pars.append(gammalib.GApplicationPar("sigma","r","h","5.0","","","Significance threshold")) 116 pars.append(gammalib.GApplicationPar("sigma","r","h","5.0","","","Significance threshold"))
120 - pars.append(gammalib.GApplicationPar("ts_use","b","h","yes","","","Use TS?"))  
121 - pars.append(gammalib.GApplicationPar("index","r","h","-2.48","","","Assumed spectral index"))  
122 - pars.append(gammalib.GApplicationPar("radius","r","h","0.1","","","Extended source model radius"))  
123 - pars.append(gammalib.GApplicationPar("width","r","h","0.05","","","Extended source model width"))  
124 pars.append(gammalib.GApplicationPar("max_iter","i","h","50","","","Maximum number of iterations")) 117 pars.append(gammalib.GApplicationPar("max_iter","i","h","50","","","Maximum number of iterations"))
125 pars.append(gammalib.GApplicationPar("num_avg","i","h","3","","","Number of iterations for sliding average")) 118 pars.append(gammalib.GApplicationPar("num_avg","i","h","3","","","Number of iterations for sliding average"))
126 pars.append_standard() 119 pars.append_standard()
  120 + pars.append(gammalib.GApplicationPar("logfile","f","h","cssens.log","","","Log filename"))
127 pars.save(parfile) 121 pars.save(parfile)
128 122
129 # Return 123 # Return
@@ -131,31 +125,48 @@ class cssens(ctools.cscript): @@ -131,31 +125,48 @@ class cssens(ctools.cscript):
131 125
132 def get_parameters(self): 126 def get_parameters(self):
133 """ 127 """
134 - Get parameters from parfile. 128 + Get user parameters from parfile.
135 """ 129 """
136 - # Get parameters  
137 - self.m_outfile = self["outfile"].filename()  
138 - self.m_caldb = self["caldb"].string()  
139 - self.m_irf = self["irf"].string()  
140 - self.m_type = self["type"].string()  
141 - self.m_offset = self["offset"].real()  
142 - self.m_bkg = self["bkg"].string()  
143 - self.m_duration = self["duration"].real()  
144 - self.m_roi = self["rad"].real()  
145 - self.m_nbins = self["enumbins"].integer()  
146 - self.m_npix = self["npix"].integer()  
147 - self.m_binsz = self["binsz"].real() 130 + # Set observation if not done before
  131 + if self.obs == None or self.obs.size() == 0:
  132 + self.obs = self.set_obs()
  133 +
  134 + # Set models if we have none
  135 + if self.obs.models().size() == 0:
  136 + self.obs.models(self["inmodel"].filename())
  137 +
  138 + # Get source name
  139 + self.m_srcname = self["srcname"].string()
  140 +
  141 + # Read further parameters
  142 + self.m_outfile = self["outfile"].filename()
  143 + self.m_emin = self["emin"].real()
  144 + self.m_emax = self["emax"].real()
  145 + self.m_bins = self["bins"].integer()
  146 +
  147 + # Read parameters for binned if requested
  148 + self.m_enumbins = self["enumbins"].integer()
  149 + if not self.m_enumbins == 0:
  150 + self.m_npix = self["npix"].integer()
  151 + self.m_binsz = self["binsz"].real()
  152 + else:
  153 + # Set dummy values (required by obsutils)
  154 + self.m_npix = 0
  155 + self.m_binsz = 0.0
  156 +
  157 + # Read remaining parameters
148 self.m_ts_thres = self["sigma"].real()*self["sigma"].real() 158 self.m_ts_thres = self["sigma"].real()*self["sigma"].real()
149 - self.m_use_ts = self["ts_use"].boolean()  
150 - self.m_index = self["index"].real()  
151 - self.m_radius = self["radius"].real()  
152 - self.m_width = self["width"].real()  
153 self.m_max_iter = self["max_iter"].integer() 159 self.m_max_iter = self["max_iter"].integer()
154 self.m_num_avg = self["num_avg"].integer() 160 self.m_num_avg = self["num_avg"].integer()
155 - 161 +
156 # Set some fixed parameters 162 # Set some fixed parameters
157 self.m_log = False # Logging in client tools 163 self.m_log = False # Logging in client tools
158 self.m_debug = self["debug"].boolean() # Debugging in client tools 164 self.m_debug = self["debug"].boolean() # Debugging in client tools
  165 +
  166 + # Derive some parameters
  167 + self.m_ebounds = gammalib.GEbounds(self.m_bins, \
  168 + gammalib.GEnergy(self.m_emin, "TeV"), \
  169 + gammalib.GEnergy(self.m_emax, "TeV"))
159 170
160 # Return 171 # Return
161 return 172 return
@@ -190,17 +201,8 @@ class cssens(ctools.cscript): @@ -190,17 +201,8 @@ class cssens(ctools.cscript):
190 colnames = ['loge', 'emin', 'emax', 'crab', 'pflux', 'eflux', 'diffSens'] 201 colnames = ['loge', 'emin', 'emax', 'crab', 'pflux', 'eflux', 'diffSens']
191 results = [] 202 results = []
192 203
193 - # Initialise models. Note that we centre the point source at the Galactic  
194 - # center as our observation is also centred at the Galactic centre, so  
195 - # we're onaxis.  
196 - bkg_model = gammalib.GModels()  
197 - full_model = gammalib.GModels()  
198 - bkg_model.append(self.set_bkg_model())  
199 - full_model.append(self.set_bkg_model())  
200 - full_model.append(self.set_src_model(0.0, \  
201 - self.m_offset, \  
202 - type=self.m_type, \  
203 - index=self.m_index)) 204 + # Initialise models
  205 + full_model, bkg_model = self.set_models()
204 206
205 # Write models into logger 207 # Write models into logger
206 if self.logTerse(): 208 if self.logTerse():
@@ -218,21 +220,19 @@ class cssens(ctools.cscript): @@ -218,21 +220,19 @@ class cssens(ctools.cscript):
218 self.log("\n") 220 self.log("\n")
219 self.log.header1("Sensitivity determination") 221 self.log.header1("Sensitivity determination")
220 222
221 - # Loop over energy bands. The number of energy band is still  
222 - # fixed ...  
223 - for ieng in range(21): 223 + # Loop over energy bins
  224 + for ieng in range(self.m_ebounds.size()):
224 225
225 # Set energies 226 # Set energies
226 - loge = -1.7 + ieng * 0.2  
227 - emean = pow(10.0, loge)  
228 - emin = pow(10.0, loge-0.1)  
229 - emax = pow(10.0, loge+0.1) 227 + emean = self.m_ebounds.elogmean(ieng).TeV()
  228 + emin = self.m_ebounds.emin(ieng).TeV()
  229 + emax = self.m_ebounds.emax(ieng).TeV()
230 230
231 - # Setup observation(s)  
232 - obs = self.set_obs(emin=emin, emax=emax) 231 + # Set energy boundaries
  232 + self.set_obs_ebounds(emin, emax)
233 233
234 # Determine sensitivity 234 # Determine sensitivity
235 - result = self.get_sensitivity(obs, bkg_model, full_model) 235 + result = self.get_sensitivity(self.obs, bkg_model, full_model)
236 236
237 # Write results 237 # Write results
238 if ieng == 0: 238 if ieng == 0:
@@ -252,10 +252,10 @@ class cssens(ctools.cscript): @@ -252,10 +252,10 @@ class cssens(ctools.cscript):
252 252
253 # Return 253 # Return
254 return 254 return
255 - 255 +
256 def set_obs(self, lpnt=0.0, bpnt=0.0, emin=0.1, emax=100.0): 256 def set_obs(self, lpnt=0.0, bpnt=0.0, emin=0.1, emax=100.0):
257 """ 257 """
258 - Returns an observation container with a single CTA observation. 258 + Returns an observation container.
259 259
260 Keywords: 260 Keywords:
261 lpnt - Galactic longitude of pointing [deg] (default: 0.0) 261 lpnt - Galactic longitude of pointing [deg] (default: 0.0)
@@ -263,134 +263,125 @@ class cssens(ctools.cscript): @@ -263,134 +263,125 @@ class cssens(ctools.cscript):
263 emin - Minimum energy [TeV] (default: 0.1) 263 emin - Minimum energy [TeV] (default: 0.1)
264 emax - Maximum energy [TeV] (default: 100.0) 264 emax - Maximum energy [TeV] (default: 100.0)
265 """ 265 """
266 - # Allocate observation container  
267 - obs = gammalib.GObservations() 266 + # If an observation was provided on input then load it from XML file
  267 + filename = self["inobs"].filename()
  268 + if filename != "NONE" and filename != "":
  269 + obs = self.get_observations()
  270 +
  271 + # ... otherwise allocate a single observation
  272 + else:
  273 +
  274 + # Read relevant user parameters
  275 + caldb = self["caldb"].string()
  276 + irf = self["irf"].string()
  277 + deadc = self["deadc"].real()
  278 + duration = self["duration"].real()
  279 + rad = self["rad"].real()
  280 +
  281 + # Allocate observation container
  282 + obs = gammalib.GObservations()
268 283
269 - # Set single pointing  
270 - pntdir = gammalib.GSkyDir()  
271 - pntdir.lb_deg(lpnt, bpnt) 284 + # Set single pointing
  285 + pntdir = gammalib.GSkyDir()
  286 + pntdir.lb_deg(lpnt, bpnt)
272 287
273 - # Create CTA observation  
274 - run = obsutils.set_obs(pntdir, caldb=self.m_caldb, irf=self.m_irf, \  
275 - duration=self.m_duration, \  
276 - emin=emin, emax=emax, rad=self.m_roi) 288 + # Create CTA observation
  289 + run = obsutils.set_obs(pntdir, caldb=caldb, irf=irf, \
  290 + duration=duration, deadc=deadc, \
  291 + emin=emin, emax=emax, rad=rad)
277 292
278 - # Append observation to container  
279 - obs.append(run) 293 + # Append observation to container
  294 + obs.append(run)
  295 +
  296 + # Set source position
  297 + offset = self["offset"].real()
  298 + pntdir.lb_deg(lpnt, bpnt+offset)
  299 + self.m_ra = pntdir.ra_deg()
  300 + self.m_dec = pntdir.dec_deg()
280 301
281 # Return observation container 302 # Return observation container
282 return obs 303 return obs
283 -  
284 - def set_bkg_model(self, fitidx=False): 304 +
  305 + def set_obs_ebounds(self, emin, emax):
285 """ 306 """
286 - Setup standard CTA background model. We use a simple power law here,  
287 - scaled to Konrad's E configuration performance table.  
288 -  
289 - Keywords:  
290 - fitidx - Fit spectral index (default: False) 307 + Set energy boundaries for observation in container.
291 """ 308 """
292 - # Define radial component  
293 - radial = gammalib.GCTAModelRadialGauss(3.0) 309 + # Loop over all observations in container
  310 + for obs in self.obs:
294 311
295 - # Define spectral component. If a background model is given then  
296 - # use a file function. Otherwise use a static power law.  
297 - if len(self.m_bkg) > 0:  
298 - spectrum = gammalib.GModelSpectralFunc(self.m_bkg, 1.0)  
299 - else:  
300 - spectrum = gammalib.GModelSpectralPlaw(61.8e-6, -1.85, gammalib.GEnergy(1.0, "TeV"))  
301 - if fitidx:  
302 - spectrum["Index"].free()  
303 - else:  
304 - spectrum["Index"].fix() 312 + # Set energy boundaries
  313 + ebounds = gammalib.GEbounds(gammalib.GEnergy(emin, "TeV"), \
  314 + gammalib.GEnergy(emax, "TeV"))
  315 + obs.events().ebounds(ebounds)
305 316
306 - # Create background model  
307 - model = gammalib.GCTAModelRadialAcceptance(radial, spectrum)  
308 - model.name("Background")  
309 - model.instruments("CTA")  
310 -  
311 - # Return background model  
312 - return model  
313 -  
314 - def set_src_model(self, l, b, flux=1.0, index=-2.48, \  
315 - type="point", fitpos=False, fitidx=False):  
316 - """  
317 - Returns a single source with Crab-like spectrum. The source flux  
318 - can be scaled in Crab units. The Crab spectrum is based on MAGIC  
319 - observations (Albert et al. 2008, ApJ, 674, 1037). 317 + # Return
  318 + return
320 319
321 - Parameters:  
322 - l - Galactic longitude of source location [deg]  
323 - b - Galactic latitude of source location [deg]  
324 - Keywords:  
325 - flux - Source flux [Crabs]  
326 - index - Spectral index  
327 - type - Source type ("point", "gauss", "disk", "shell")  
328 - fitpos - Fit position and size? (default: False)  
329 - fitidx - Fit index? (default: False) 320 + def set_models(self, fitpos=False, fitspec=False):
330 """ 321 """
331 - # Set source location  
332 - location = gammalib.GSkyDir()  
333 - location.lb_deg(l, b)  
334 -  
335 - # Set source spectrum  
336 - spectrum = gammalib.GModelSpectralPlaw(flux*5.7e-16, index, gammalib.GEnergy(0.3, "TeV"))  
337 - if fitidx:  
338 - spectrum["Index"].free() 322 + Set full and background model.
  323 + """
  324 + # Retrieve full model from observation container
  325 + full_model = self.obs.models().copy()
  326 +
  327 + # Get source model
  328 + model = full_model[self.m_srcname]
  329 +
  330 + # Check that model has a Prefactor
  331 + if not model.has_par("Prefactor"):
  332 + msg = "Model \""+self.m_srcname+"\" has no parameter \"Prefactor\"."+ \
  333 + " Only spectral models with a \"Prefactor\" parameter are supported."
  334 + raise gammalib.GException.invalid_value("cssens", msg)
  335 +
  336 + # Set source position
  337 + if self.m_ra != None and self.m_dec != None:
  338 + if model.has_par("RA") and model.has_par("DEC"):
  339 + model["RA"].value(self.m_ra)
  340 + model["DEC"].value(self.m_dec)
  341 +
  342 + # Fit or fix spatial parameters
  343 + if fitpos:
  344 + if model.has_par("RA"):
  345 + model["RA"].free()
  346 + if model.has_par("DEC"):
  347 + model["DEC"].free()
  348 + if model.has_par("Sigma"):
  349 + model["Sigma"].free()
  350 + if model.has_par("Radius"):
  351 + model["Radius"].free()
  352 + if model.has_par("Width"):
  353 + model["Width"].free()
339 else: 354 else:
340 - spectrum["Index"].fix()  
341 -  
342 - # Set source  
343 - if type == "point":  
344 - spatial = gammalib.GModelSpatialPointSource(location)  
345 - if fitpos:  
346 - spatial["RA"].free()  
347 - spatial["DEC"].free()  
348 - else:  
349 - spatial["RA"].fix()  
350 - spatial["DEC"].fix()  
351 - elif type == "gauss":  
352 - spatial = gammalib.GModelSpatialRadialGauss(location, self.m_radius)  
353 - if fitpos:  
354 - spatial["RA"].free()  
355 - spatial["DEC"].free()  
356 - spatial["Sigma"].free()  
357 - else:  
358 - spatial["RA"].fix()  
359 - spatial["DEC"].fix()  
360 - spatial["Sigma"].fix()  
361 - elif type == "disk":  
362 - spatial = gammalib.GModelSpatialRadialDisk(location, self.m_radius)  
363 - if fitpos:  
364 - spatial["RA"].free()  
365 - spatial["DEC"].free()  
366 - spatial["Radius"].free()  
367 - else:  
368 - spatial["RA"].fix()  
369 - spatial["DEC"].fix()  
370 - spatial["Radius"].fix()  
371 - elif type == "shell":  
372 - spatial = gammalib.GModelSpatialRadialShell(location, self.m_radius, self.m_width)  
373 - if fitpos:  
374 - spatial["RA"].free()  
375 - spatial["DEC"].free()  
376 - spatial["Radius"].free()  
377 - spatial["Width"].free()  
378 - else:  
379 - spatial["RA"].fix()  
380 - spatial["DEC"].fix()  
381 - spatial["Radius"].fix()  
382 - spatial["Width"].fix() 355 + if model.has_par("RA"):
  356 + model["RA"].fix()
  357 + if model.has_par("DEC"):
  358 + model["DEC"].fix()
  359 + if model.has_par("Sigma"):
  360 + model["Sigma"].fix()
  361 + if model.has_par("Radius"):
  362 + model["Radius"].fix()
  363 + if model.has_par("Width"):
  364 + model["Width"].fix()
  365 +
  366 + # Fit or fix spectral parameters
  367 + if fitspec:
  368 + if model.has_par("Index"):
  369 + model["Index"].free()
  370 + if model.has_par("Cutoff"):
  371 + model["Cutoff"].free()
383 else: 372 else:
384 - self.log("ERROR: Unknown source type '"+type+"'.\n")  
385 - return None  
386 - source = gammalib.GModelSky(spatial, spectrum)  
387 -  
388 - # Set source name  
389 - source.name("Test")  
390 -  
391 - # Return source  
392 - return source  
393 - 373 + if model.has_par("Index"):
  374 + model["Index"].fix()
  375 + if model.has_par("Cutoff"):
  376 + model["Cutoff"].fix()
  377 +
  378 + # Create background model
  379 + bkg_model = full_model.copy()
  380 + bkg_model.remove(self.m_srcname)
  381 +
  382 + # Return models
  383 + return full_model, bkg_model
  384 +
394 def get_sensitivity(self, obs, bkg_model, full_model): 385 def get_sensitivity(self, obs, bkg_model, full_model):
395 """ 386 """
396 Determine sensitivity for a given observations. 387 Determine sensitivity for a given observations.
@@ -413,10 +404,25 @@ class cssens(ctools.cscript): @@ -413,10 +404,25 @@ class cssens(ctools.cscript):
413 erg_width = (emax.TeV()-emin.TeV()) * tev2erg 404 erg_width = (emax.TeV()-emin.TeV()) * tev2erg
414 break 405 break
415 406
  407 + # Compute Crab unit (this is the factor with which the Prefactor needs
  408 + # to be multiplied to get 1 Crab
  409 + crab_flux = self.get_crab_flux(emin, emax)
  410 + src_flux = full_model[self.m_srcname].spectral().flux(emin, emax)
  411 + crab_unit = crab_flux/src_flux
  412 +
416 # Write header 413 # Write header
417 if self.logTerse(): 414 if self.logTerse():
418 self.log("\n") 415 self.log("\n")
419 self.log.header2("Energies: "+str(emin)+" - "+str(emax)) 416 self.log.header2("Energies: "+str(emin)+" - "+str(emax))
  417 + self.log.parformat("Crab flux")
  418 + self.log(crab_flux)
  419 + self.log(" ph/cm2/s\n")
  420 + self.log.parformat("Source model flux")
  421 + self.log(src_flux)
  422 + self.log(" ph/cm2/s\n")
  423 + self.log.parformat("Crab unit factor")
  424 + self.log(crab_unit)
  425 + self.log("\n")
420 426
421 # Initialise loop 427 # Initialise loop
422 flux_value = [] 428 flux_value = []
@@ -424,7 +430,7 @@ class cssens(ctools.cscript): @@ -424,7 +430,7 @@ class cssens(ctools.cscript):
424 eflux_value = [] 430 eflux_value = []
425 diffSens_value = [] 431 diffSens_value = []
426 iter = 0 432 iter = 0
427 - test_flux = 0.1 # This is the initial test flux in Crab units 433 + test_cflux = 0.1 # This is the initial test flux in Crab units (100 mCrab)
428 434
429 # Loop until we break 435 # Loop until we break
430 while True: 436 while True:
@@ -437,13 +443,14 @@ class cssens(ctools.cscript): @@ -437,13 +443,14 @@ class cssens(ctools.cscript):
437 self.log.header2("Iteration "+str(iter)) 443 self.log.header2("Iteration "+str(iter))
438 444
439 # Set test flux 445 # Set test flux
440 - src_model = full_model.copy()  
441 - crab_flux = src_model["Test"]['Prefactor'].value()  
442 - src_model["Test"]['Prefactor'].value(crab_flux*test_flux) 446 + src_model = full_model.copy()
  447 + crab_prefactor = src_model[self.m_srcname]['Prefactor'].value() * crab_unit
  448 + src_model[self.m_srcname]['Prefactor'].value(crab_prefactor * test_cflux)
443 obs.models(src_model) 449 obs.models(src_model)
444 450
  451 +
445 # Simulate events 452 # Simulate events
446 - sim = obsutils.sim(obs, nbins=self.m_nbins, seed=iter, \ 453 + sim = obsutils.sim(obs, nbins=self.m_enumbins, seed=iter, \
447 binsz=self.m_binsz, npix=self.m_npix, \ 454 binsz=self.m_binsz, npix=self.m_npix, \
448 log=self.m_log, debug=self.m_debug) 455 log=self.m_log, debug=self.m_debug)
449 456
@@ -544,22 +551,24 @@ class cssens(ctools.cscript): @@ -544,22 +551,24 @@ class cssens(ctools.cscript):
544 continue 551 continue
545 552
546 # Get fitted Crab, photon and energy fluxes 553 # Get fitted Crab, photon and energy fluxes
547 - cflux = result_all["Test"]['Prefactor'].value() / crab_flux  
548 - cflux_err = result_all["Test"]['Prefactor'].error() / crab_flux  
549 - pflux = result_all["Test"].spectral().flux(emin, emax)  
550 - eflux = result_all["Test"].spectral().eflux(emin, emax) 554 + cflux = result_all[self.m_srcname]['Prefactor'].value() / crab_prefactor
  555 + cflux_err = result_all[self.m_srcname]['Prefactor'].error() / crab_prefactor
  556 + pflux = result_all[self.m_srcname].spectral().flux(emin, emax)
  557 + eflux = result_all[self.m_srcname].spectral().eflux(emin, emax)
551 558
552 # Compute differential sensitivity 559 # Compute differential sensitivity
553 diffSens = pflux / erg_width * erg_mean*erg_mean 560 diffSens = pflux / erg_width * erg_mean*erg_mean
554 561
555 # Compute flux correction factor based on average TS 562 # Compute flux correction factor based on average TS
556 correct = 1.0 563 correct = 1.0
557 - if self.m_use_ts:  
558 - if ts > 0:  
559 - correct = math.sqrt(self.m_ts_thres/ts)  
560 - else:  
561 - if cflux > 0 and cflux_err > 0:  
562 - correct = math.sqrt(self.m_ts_thres)/(cflux/cflux_err) 564 + if ts > 0:
  565 + correct = math.sqrt(self.m_ts_thres/ts)
  566 + #if self.m_use_ts:
  567 + # if ts > 0:
  568 + # correct = math.sqrt(self.m_ts_thres/ts)
  569 + #else:
  570 + # if cflux > 0 and cflux_err > 0:
  571 + # correct = math.sqrt(self.m_ts_thres)/(cflux/cflux_err)
563 572
564 # Compute extrapolated fluxes 573 # Compute extrapolated fluxes
565 flux = correct * cflux 574 flux = correct * cflux
@@ -631,8 +640,8 @@ class cssens(ctools.cscript): @@ -631,8 +640,8 @@ class cssens(ctools.cscript):
631 640
632 # Compare average flux to last average 641 # Compare average flux to last average
633 if iter > self.m_num_avg: 642 if iter > self.m_num_avg:
634 - if test_flux > 0:  
635 - ratio = flux/test_flux 643 + if test_cflux > 0:
  644 + ratio = flux/test_cflux
636 645
637 # We have 2 convergence criteria: 646 # We have 2 convergence criteria:
638 # 1. The average flux does not change 647 # 1. The average flux does not change
@@ -648,7 +657,7 @@ class cssens(ctools.cscript): @@ -648,7 +657,7 @@ class cssens(ctools.cscript):
648 break 657 break
649 658
650 # Use average for next iteration 659 # Use average for next iteration
651 - test_flux = flux 660 + test_cflux = flux
652 661
653 # Exit loop if number of trials exhausted 662 # Exit loop if number of trials exhausted
654 if (iter >= self.m_max_iter): 663 if (iter >= self.m_max_iter):
@@ -712,6 +721,19 @@ class cssens(ctools.cscript): @@ -712,6 +721,19 @@ class cssens(ctools.cscript):
712 # Return result 721 # Return result
713 return result 722 return result
714 723
  724 + def get_crab_flux(self, emin, emax):
  725 + """
  726 + Determine the Crab photon flux in a given energy interval.
  727 + """
  728 + # Set Crab spectral model
  729 + crab = gammalib.GModelSpectralPlaw(5.7e-16, -2.48, gammalib.GEnergy(0.3, "TeV"))
  730 +
  731 + # Determine flux
  732 + flux = crab.flux(emin, emax)
  733 +
  734 + # Return flux
  735 + return flux
  736 +
715 737
716 # ======================== # 738 # ======================== #
717 # Main routine entry point # 739 # Main routine entry point #
test/test_cscripts.sh.in
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 # ========================================================================== 2 # ==========================================================================
3 # This script tests all cscripts that are shipped with ctools 3 # This script tests all cscripts that are shipped with ctools
4 # 4 #
5 -# Copyright (C) 2011-2013 Juergen Knoedlseder 5 +# Copyright (C) 2011-2015 Juergen Knoedlseder
6 # 6 #
7 # This program is free software: you can redistribute it and/or modify 7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by 8 # it under the terms of the GNU General Public License as published by
@@ -66,6 +66,7 @@ chmod +x @abs_top_srcdir@/scripts/cspull.py @@ -66,6 +66,7 @@ chmod +x @abs_top_srcdir@/scripts/cspull.py
66 deadc=0.95 \ 66 deadc=0.95 \
67 rad=5.0 \ 67 rad=5.0 \
68 npix=200 \ 68 npix=200 \
  69 + debug=yes \
69 binsz=0.05 > cspull.log 70 binsz=0.05 > cspull.log
70 $ECHO -n "." 71 $ECHO -n "."
71 if [ -s "pull.dat" ] 72 if [ -s "pull.dat" ]
@@ -81,25 +82,27 @@ $ECHO &quot; ok&quot; @@ -81,25 +82,27 @@ $ECHO &quot; ok&quot;
81 # 82 #
82 # Test cssens 83 # Test cssens
83 # =========== 84 # ===========
84 -#$ECHO -n "Test cssens: "  
85 -#chmod +x @abs_top_srcdir@/scripts/cssens.py  
86 -#@abs_top_srcdir@/scripts/cssens.py \  
87 -# duration=3600.0 \  
88 -# caldb="irf" \  
89 -# irf="cta_dummy_irf" \  
90 -# type="point" \  
91 -# offset="0.0" \  
92 -# bkg="data/bkg_dummy.txt" \  
93 -# rad=5.0 > cssens.log  
94 -#echo @ECHO_N@ "."  
95 -#if [ -s "sensitivity.dat" ]  
96 -#then  
97 -# $ECHO -n "."  
98 -#else  
99 -# echo " sensitivity.dat file is not valid"  
100 -# exit 1  
101 -#fi  
102 -#echo " ok" 85 +$ECHO -n "Test cssens: "
  86 +chmod +x @abs_top_srcdir@/scripts/cssens.py
  87 +@abs_top_srcdir@/scripts/cssens.py \
  88 + inmodel="data/crab.xml" \
  89 + srcname="Crab" \
  90 + caldb="irf" \
  91 + irf="cta_dummy_irf" \
  92 + duration=1800.0 \
  93 + rad=3.0 \
  94 + emin=1.0 \
  95 + emax=10.0 \
  96 + bins=1 > cssens.log
  97 +$ECHO -n "."
  98 +if [ -s "sensitivity.dat" ]
  99 +then
  100 + $ECHO -n "."
  101 +else
  102 + $ECHO " sensitivity.dat file is not valid"
  103 + exit 1
  104 +fi
  105 +$ECHO " ok"
103 106
104 107
105 # 108 #