cslike.py
1 |
#! /usr/bin/env python
|
---|---|
2 |
# ==========================================================================
|
3 |
# This script generates the pull distribution for all model parameters.
|
4 |
#
|
5 |
# Copyright (C) 2011-2014 Juergen Knoedlseder
|
6 |
#
|
7 |
# This program is free software: you can redistribute it and/or modify
|
8 |
# it under the terms of the GNU General Public License as published by
|
9 |
# the Free Software Foundation, either version 3 of the License, or
|
10 |
# (at your option) any later version.
|
11 |
#
|
12 |
# This program is distributed in the hope that it will be useful,
|
13 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
# GNU General Public License for more details.
|
16 |
#
|
17 |
# You should have received a copy of the GNU General Public License
|
18 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19 |
#
|
20 |
# ==========================================================================
|
21 |
|
22 |
import gammalib as gl |
23 |
import ctools as ct |
24 |
import sys |
25 |
import math |
26 |
|
27 |
|
28 |
# ============ #
|
29 |
# cslike class #
|
30 |
# ============ #
|
31 |
class cslike(gl.GApplication): |
32 |
"""
|
33 |
This class implements an auxiliary python interface to ctlike.
|
34 |
It offers some additional functionality, like computing TS values,
|
35 |
and storing covariance matrix. It derives from the
|
36 |
GammaLib::GApplication class which provides support for parameter
|
37 |
files, command line arguments, and logging. In that way the Python
|
38 |
script behaves just as a regular ctool.
|
39 |
"""
|
40 |
def __init__(self, *argv): |
41 |
|
42 |
"""
|
43 |
Constructor.
|
44 |
"""
|
45 |
|
46 |
# Set name
|
47 |
self.name = "cslike" |
48 |
self.version = "0.0.1" |
49 |
|
50 |
# Initialise some members
|
51 |
if isinstance(argv[0],gl.GObservations): |
52 |
self.obs = argv[0] |
53 |
else:
|
54 |
self.obs = gl.GObservations()
|
55 |
self.outfile = "" |
56 |
self.m_value=0 |
57 |
self.m_covar = None |
58 |
self.m_status = -1 |
59 |
self.m_ts_method = "BIASED" |
60 |
self.ts = {}
|
61 |
self.LogL0 = {}
|
62 |
|
63 |
# Make sure that parfile exists
|
64 |
file = self.parfile()
|
65 |
|
66 |
# Initialise application
|
67 |
if len(argv) == 1: |
68 |
gl.GApplication.__init__(self, self.name, self.version) |
69 |
elif len(argv) ==2: |
70 |
gl.GApplication.__init__(self, self.name, self.version, argv[1:]) |
71 |
else:
|
72 |
raise TypeError("Invalid number of arguments given.") |
73 |
|
74 |
# Set logger properties
|
75 |
self.log_header()
|
76 |
self.log.date(True) |
77 |
|
78 |
# Return
|
79 |
return
|
80 |
|
81 |
def __del__(self): |
82 |
"""
|
83 |
Destructor.
|
84 |
"""
|
85 |
# Write separator into logger
|
86 |
if self.logTerse(): |
87 |
self.log("\n") |
88 |
|
89 |
# Return
|
90 |
return
|
91 |
|
92 |
def parfile(self): |
93 |
"""
|
94 |
Check if parfile exists. If parfile does not exist then create a
|
95 |
default parfile. This kluge avoids shipping the cscript with a parfile.
|
96 |
"""
|
97 |
# Set parfile name
|
98 |
parfile = self.name+".par" |
99 |
|
100 |
try:
|
101 |
pars = gl.GApplicationPars(parfile) |
102 |
except:
|
103 |
# Signal if parfile was not found
|
104 |
sys.stdout.write("Parfile "+parfile+" not found. Create default parfile.\n") |
105 |
|
106 |
# Create default parfile
|
107 |
pars = gl.GApplicationPars() |
108 |
pars.append(gl.GApplicationPar("infile", "f", "a", "events.fits","","","observation definition file")) |
109 |
pars.append(gl.GApplicationPar("stat","s","h","POISSON","","","Optimization statistics")) |
110 |
pars.append(gl.GApplicationPar("refit", "b", "h", "no","","", "Do refitting?")) |
111 |
pars.append(gl.GApplicationPar("srcmdl","f","a","$CTOOLS/share/models/crab.xml","","","Source model")) |
112 |
pars.append(gl.GApplicationPar("outfile","f","a","crab_optmodel.xml","","","Output file name")) |
113 |
pars.append(gl.GApplicationPar("caldb","s","h","","","","Calibration database")) |
114 |
pars.append(gl.GApplicationPar("irf","s","a","cta_dummy_irf","","","Instrument response function")) |
115 |
pars.append(gl.GApplicationPar("TSmethod", "s", "h", "BIASED","BIASED|UNBIASED","","Method to compute TS values (BIASED is faster)")) |
116 |
pars.append(gl.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?")) |
117 |
pars.append(gl.GApplicationPar("logfile", "f", "h", "cslike.log","","", "Log filename")) |
118 |
pars.append_standard() |
119 |
pars.save(parfile) |
120 |
|
121 |
# Return
|
122 |
return
|
123 |
|
124 |
def _add_ts(self): |
125 |
""" appends TS value to xml file """
|
126 |
|
127 |
xml = gl.GXml(self.m_outfile)
|
128 |
lib = xml.element("source_library", 0); |
129 |
for src_name in self.ts.keys(): |
130 |
for i in range(lib.elements()): |
131 |
src_element = lib.element("source", i);
|
132 |
if src_element.attribute("name") == src_name: |
133 |
src_element.attribute("TS",str(self.ts[src_name])) |
134 |
break
|
135 |
xml.save(self.m_outfile)
|
136 |
|
137 |
# append likelihood value to last line of the file
|
138 |
f = open(self.m_outfile,"a") |
139 |
f.write("loglike="+str(self.m_value)) |
140 |
f.close() |
141 |
|
142 |
|
143 |
def get_parameters(self): |
144 |
"""
|
145 |
Get parameters from parfile and setup the observation.
|
146 |
"""
|
147 |
|
148 |
# Setup obs if it isn't already set
|
149 |
if self.obs.size() == 0: |
150 |
|
151 |
infile = self["infile"].filename() |
152 |
try:
|
153 |
self.obs = gl.GObservations(infile)
|
154 |
except:
|
155 |
obs0 = gl.GCTAObservation(infile) |
156 |
self.obs.append(obs0)
|
157 |
mdlfile = self["srcmdl"].filename() |
158 |
self.obs.models(mdlfile)
|
159 |
self.m_caldb = self["caldb"].string() |
160 |
self.m_irf = self["irf"].string() |
161 |
|
162 |
else:
|
163 |
self.m_caldb = "" |
164 |
self.m_irf = "" |
165 |
|
166 |
# get hidden parameters
|
167 |
self.m_stat = self["stat"].string() |
168 |
self.m_refit = self["refit"].boolean() |
169 |
self.m_edisp = self["edisp"].boolean() |
170 |
self.m_chatter = self["chatter"].integer() |
171 |
self.m_debug = self["debug"].boolean() |
172 |
self.m_clobber = self["clobber"].boolean() |
173 |
self.m_mode = self["mode"].string() |
174 |
self.m_logfile = self["logfile"].filename() |
175 |
|
176 |
# get TS calculation method
|
177 |
self.m_ts_method = self["TSmethod"].string() |
178 |
|
179 |
# get outfile
|
180 |
if self.outfile == "": |
181 |
self.m_outfile = self["outfile"].filename() |
182 |
else:
|
183 |
self.m_outfile = self.outfile |
184 |
|
185 |
|
186 |
def execute(self): |
187 |
"""
|
188 |
Execute the script.
|
189 |
"""
|
190 |
# Run the script
|
191 |
self.run()
|
192 |
|
193 |
# Return
|
194 |
return
|
195 |
|
196 |
def covar(self): |
197 |
"""
|
198 |
Return covariance matrix
|
199 |
"""
|
200 |
return self.m_covar.clone() |
201 |
def obs(self): |
202 |
"""
|
203 |
Return observations
|
204 |
"""
|
205 |
return self.obs.clone() |
206 |
|
207 |
|
208 |
def run(self): |
209 |
"""
|
210 |
Run the fit.
|
211 |
"""
|
212 |
|
213 |
# Switch screen logging on in debug mode
|
214 |
if self.logDebug(): |
215 |
self.log.cout(True) |
216 |
|
217 |
|
218 |
# Get parameters
|
219 |
self.get_parameters()
|
220 |
|
221 |
# Write input parameters into logger
|
222 |
if self.logTerse(): |
223 |
self.log_parameters()
|
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 |
|
233 |
# Write header
|
234 |
if self.logTerse(): |
235 |
self.log("\n") |
236 |
self.log.header1("Generate pull distribution") |
237 |
|
238 |
# create ctlike instance with given parameters
|
239 |
like = ct.ctlike(self.obs)
|
240 |
like["stat"].string(self.m_stat) |
241 |
like["caldb"].string(self.m_caldb) |
242 |
like["irf"].string(self.m_irf) |
243 |
like["clobber"].boolean(self.m_clobber) |
244 |
like["debug"].boolean(self.m_debug) |
245 |
like["mode"].string(self.m_mode) |
246 |
like["logfile"].filename(self.m_logfile) |
247 |
like["edisp"].boolean(self.m_edisp) |
248 |
like["chatter"].integer(self["chatter"].integer()) |
249 |
|
250 |
# Perform the fit
|
251 |
if self["chatter"].integer()>0: |
252 |
print "Running ctlike ..." |
253 |
like.run() |
254 |
LogL1 = like.opt().value() |
255 |
|
256 |
# Store likelihood value and fit status
|
257 |
self.m_value = LogL1
|
258 |
self.m_status = like.opt().status()
|
259 |
|
260 |
# Get covariance matrix if fit has converged
|
261 |
try:
|
262 |
self.m_covar = like.obs().function().curvature().invert().clone()
|
263 |
except:
|
264 |
print "WARNING: Covariance matrix not determined successfully, FitStatus = "+str(self.status),sys._getframe().f_code.co_name |
265 |
|
266 |
# save best fit results if outfile is given
|
267 |
if not self.m_outfile == "": |
268 |
like.obs().models().save(self.m_outfile)
|
269 |
|
270 |
# assign optimised models
|
271 |
self.obs.models(like.obs().models().clone())
|
272 |
|
273 |
# Find sources with free parameters to calculate their TS values
|
274 |
freemodelnames = [] |
275 |
for m in like.obs().models(): |
276 |
for par in m: |
277 |
if par.is_free():
|
278 |
freemodelnames.append(m.name()) |
279 |
break
|
280 |
# Fix parameters to speed up computations: Method is inspired
|
281 |
# from Fermi Science tools, but might lead to biased results
|
282 |
if self.m_ts_method == "BIASED": |
283 |
for par in m: |
284 |
par.fix() |
285 |
|
286 |
# save initial models for TS computation
|
287 |
bck_models = like.obs().models().clone() |
288 |
|
289 |
# Loop over models with free parameters
|
290 |
for name in freemodelnames: |
291 |
# exclude background from TS computation
|
292 |
if isinstance(like.obs().models()[name],gl.GModelData): |
293 |
continue
|
294 |
|
295 |
# remove respective model from container
|
296 |
like.obs().models().remove(name) |
297 |
|
298 |
# rerun ctlike without model
|
299 |
like0 = ct.ctlike(like.obs()) |
300 |
like0.run() |
301 |
|
302 |
# get loglike value
|
303 |
loglike0 = like0.opt().value() |
304 |
TS = 2* (loglike0-LogL1)
|
305 |
|
306 |
# append TS value to dictionary
|
307 |
self.ts[name]=TS
|
308 |
|
309 |
# store likelihood value
|
310 |
self.LogL0[name]=loglike0
|
311 |
|
312 |
if self.m_chatter > 0: |
313 |
print "TS value of "+name+": "+str(TS) |
314 |
|
315 |
# Reset models to best-fit state
|
316 |
like.obs().models(bck_models) |
317 |
|
318 |
# Add TS values to optimised model files
|
319 |
if not self.m_outfile == "": |
320 |
self._add_ts()
|
321 |
|
322 |
|
323 |
|
324 |
# ======================== #
|
325 |
# Main routine entry point #
|
326 |
# ======================== #
|
327 |
if __name__ == '__main__': |
328 |
"""
|
329 |
Runs ctlike with some extensions.
|
330 |
"""
|
331 |
# Create instance of application
|
332 |
app = cslike(sys.argv) |
333 |
|
334 |
# Open logfile
|
335 |
app.logFileOpen() |
336 |
|
337 |
# Execute application
|
338 |
app.execute() |
339 |
|