csbutterfly.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 |
# csbutterfly class #
|
30 |
# ============ #
|
31 |
class csbutterfly(gl.GApplication): |
32 |
"""
|
33 |
This class implements an the computation of a butterfly.
|
34 |
It derives from the GammaLib::GApplication class which
|
35 |
provides support for parameter files, command line arguments,
|
36 |
and logging. In that way the Python script behaves just as a
|
37 |
regular ctool.
|
38 |
"""
|
39 |
def __init__(self, *argv): |
40 |
|
41 |
"""
|
42 |
Constructor.
|
43 |
"""
|
44 |
|
45 |
# Set name
|
46 |
self.name = "csbutterfly" |
47 |
self.version = "0.0.1" |
48 |
|
49 |
# Initialise some members
|
50 |
if isinstance(argv[0],gl.GObservations): |
51 |
self.obs = argv[0] |
52 |
else:
|
53 |
self.obs = gl.GObservations()
|
54 |
|
55 |
self.source = "CrabNebula" |
56 |
self.m_sigma = 1 |
57 |
self.m_emin = 0.0 #TeV |
58 |
self.m_emax = 0.0 #TeV |
59 |
self.m_nbins = 100 |
60 |
|
61 |
|
62 |
# Make sure that parfile exists
|
63 |
file = self.parfile()
|
64 |
|
65 |
# Initialise application
|
66 |
if len(argv) == 1: |
67 |
gl.GApplication.__init__(self, self.name, self.version) |
68 |
elif len(argv) ==2: |
69 |
gl.GApplication.__init__(self, self.name, self.version, argv[1:]) |
70 |
else:
|
71 |
raise TypeError("Invalid number of arguments given.") |
72 |
|
73 |
# Set logger properties
|
74 |
self.log_header()
|
75 |
self.log.date(True) |
76 |
|
77 |
# Return
|
78 |
return
|
79 |
|
80 |
def __del__(self): |
81 |
"""
|
82 |
Destructor.
|
83 |
"""
|
84 |
# Write separator into logger
|
85 |
if self.logTerse(): |
86 |
self.log("\n") |
87 |
|
88 |
# Return
|
89 |
return
|
90 |
|
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 = gl.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 = gl.GApplicationPars() |
107 |
pars.append(gl.GApplicationPar("infile", "f", "a", "events.fits","","","observation definition file")) |
108 |
pars.append(gl.GApplicationPar("stat","s","h","POISSON","","","Optimization statistics")) |
109 |
pars.append(gl.GApplicationPar("refit", "b", "h", "no","","", "Do refitting?")) |
110 |
pars.append(gl.GApplicationPar("srcmdl","f","a","$CTOOLS/share/models/crab.xml","","","Source model")) |
111 |
pars.append(gl.GApplicationPar("caldb","s","h","","","","Calibration database")) |
112 |
pars.append(gl.GApplicationPar("irf","s","a","cta_dummy_irf","","","Instrument response function")) |
113 |
pars.append(gl.GApplicationPar("edisp","b","h","no","","","Apply energy dispersion?")) |
114 |
pars.append(gl.GApplicationPar("logfile", "f", "h", "csbutterfly.log","","", "Log filename")) |
115 |
pars.append(gl.GApplicationPar("outfile", "f", "a", "CrabNebula.bf","","", "Outfile name")) |
116 |
pars.append(gl.GApplicationPar("Emin", "r", "a", "0.1","","TeV", "Minimum Energy")) |
117 |
pars.append(gl.GApplicationPar("Emax", "r", "a", "100","","TeV", "Maximum Energy")) |
118 |
pars.append(gl.GApplicationPar("nbins", "i", "h", "100","","", "Butterfly spacing")) |
119 |
pars.append(gl.GApplicationPar("sigma", "f", "h", "1","","", "Butterfly spacing")) |
120 |
pars.append_standard() |
121 |
pars.save(parfile) |
122 |
|
123 |
# Return
|
124 |
return
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
def get_parameters(self): |
130 |
"""
|
131 |
Get parameters from parfile and setup the observation.
|
132 |
"""
|
133 |
|
134 |
# Setup obs if it isn't already set
|
135 |
if self.obs.size() == 0: |
136 |
|
137 |
infile = self["infile"].filename() |
138 |
try:
|
139 |
self.obs = gl.GObservations(infile)
|
140 |
except:
|
141 |
obs0 = gl.GCTAObservation(infile) |
142 |
self.obs.append(obs0)
|
143 |
mdlfile = self["srcmdl"].filename() |
144 |
self.obs.models(mdlfile)
|
145 |
self.m_caldb = self["caldb"].string() |
146 |
self.m_irf = self["irf"].string() |
147 |
|
148 |
|
149 |
else:
|
150 |
self.m_caldb = "" |
151 |
self.m_irf = "" |
152 |
|
153 |
# get hidden parameters
|
154 |
self.m_stat = self["stat"].string() |
155 |
self.m_refit = self["refit"].boolean() |
156 |
self.m_edisp = self["edisp"].boolean() |
157 |
self.m_chatter = self["chatter"].integer() |
158 |
self.m_debug = self["debug"].boolean() |
159 |
self.m_clobber = self["clobber"].boolean() |
160 |
self.m_mode = self["mode"].string() |
161 |
self.m_logfile = self["logfile"].filename() |
162 |
|
163 |
if self.m_emin == 0.0 or self.m_emax == 0.0: |
164 |
self.m_emin = self["Emin"].real() |
165 |
self.m_emax = self["Emax"].real() |
166 |
self.m_nbins = self["nbins"].integer() |
167 |
|
168 |
|
169 |
def execute(self): |
170 |
"""
|
171 |
Execute the script.
|
172 |
"""
|
173 |
# Run the script
|
174 |
self.run()
|
175 |
|
176 |
# Return
|
177 |
return
|
178 |
|
179 |
|
180 |
def obs(self): |
181 |
"""
|
182 |
Return observations
|
183 |
"""
|
184 |
return self.obs.clone() |
185 |
|
186 |
|
187 |
def run(self): |
188 |
"""
|
189 |
Run the fit.
|
190 |
"""
|
191 |
|
192 |
# Switch screen logging on in debug mode
|
193 |
if self.logDebug(): |
194 |
self.log.cout(True) |
195 |
|
196 |
|
197 |
# Get parameters
|
198 |
self.get_parameters()
|
199 |
|
200 |
# Write input parameters into logger
|
201 |
if self.logTerse(): |
202 |
self.log_parameters()
|
203 |
self.log("\n") |
204 |
|
205 |
# Write observation into logger
|
206 |
if self.logTerse(): |
207 |
self.log("\n") |
208 |
self.log.header1("Observation") |
209 |
self.log(str(self.obs)) |
210 |
self.log("\n") |
211 |
|
212 |
# Write header
|
213 |
if self.logTerse(): |
214 |
self.log("\n") |
215 |
self.log.header1("Generate butterfly") |
216 |
|
217 |
# Check if observation container is already optimised
|
218 |
# might add a flag in the future for that
|
219 |
if self.obs.function().value() == 0.0: |
220 |
print "Optimising observation container" |
221 |
like = ct.ctlike(self.obs)
|
222 |
like.run() |
223 |
self.covar = like.obs().function().curvature().invert()
|
224 |
self.obs.models(like.obs().models().clone())
|
225 |
else:
|
226 |
self.covar = self.obs.function().curvature().invert() |
227 |
|
228 |
try:
|
229 |
m = self.obs.models()[self.source] |
230 |
except RuntimeError: |
231 |
sys.exit("Source \""+self.source+"\" not found in model container",sys._getframe().f_code.co_name) |
232 |
|
233 |
# Initialise energies in log binning
|
234 |
energies = [] |
235 |
step = (math.log10(self.m_emax)-math.log10(self.m_emin) )/ (self.m_nbins-1) |
236 |
for ibin in range(self.m_nbins): |
237 |
loge = math.log10(self.m_emin)+ibin*step
|
238 |
energies.append(pow(10,loge)) |
239 |
|
240 |
fluxes = [] |
241 |
errors = [] |
242 |
t=gl.GTime() |
243 |
|
244 |
self.fluxes = []
|
245 |
self.errors = []
|
246 |
for energy in energies: |
247 |
npars = self.obs.models().npars()
|
248 |
grad = gl.GVector(npars) |
249 |
e = gl.GEnergy(energy,"TeV")
|
250 |
k=0
|
251 |
value=0
|
252 |
for m in self.obs.models(): |
253 |
if m.name() == self.source: |
254 |
k+=m.spatial().size() |
255 |
spec = m.spectral() |
256 |
value = spec.eval_gradients(e,t) |
257 |
for i in range(spec.size()): |
258 |
grad[k] = spec[i].gradient() |
259 |
k+=1
|
260 |
k+=1
|
261 |
else:
|
262 |
k+=m.size() |
263 |
fluxes.append(value) |
264 |
vect = self.covar * grad
|
265 |
err2 = grad * vect |
266 |
err = math.sqrt(err2) |
267 |
errors.append(err) |
268 |
# if self.verbose:
|
269 |
# self.success("E = "+str(e)+", Flux = "+str("%.3e" % value)+" +- "+str("%.3e" % err)+" 1/cm2/s/MeV")
|
270 |
|
271 |
energies_MeV = [] |
272 |
for e in energies: |
273 |
energies_MeV.append(e*1e6)
|
274 |
|
275 |
|
276 |
self.save(self.outfile,energies_MeV,fluxes,errors) |
277 |
print "Finished: output written to "+self.outfile |
278 |
|
279 |
|
280 |
|
281 |
|
282 |
def save(self,outfile,energies,fluxes,errors): |
283 |
f=open(outfile,"w") |
284 |
f.write("# 1 energy [MeV] \n")
|
285 |
f.write("# 2 value [1/cm2/s/MeV] \n")
|
286 |
for i in range(len(energies)): |
287 |
y= fluxes[i]+errors[i] |
288 |
f.write(str(energies[i])+" "+str(y)+" \n") |
289 |
for i in range(len(energies)): |
290 |
y = fluxes[-(i+1)]-errors[-(i+1)] |
291 |
if y <1e-35: |
292 |
y=1e-35
|
293 |
f.write(str(energies[-(i+1)])+" "+str(y)+" \n") |
294 |
f.write(str(energies[0])+" "+str((fluxes[0]+errors[0]))+" \n") |
295 |
f.close() |
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
# ======================== #
|
304 |
# Main routine entry point #
|
305 |
# ======================== #
|
306 |
if __name__ == '__main__': |
307 |
"""
|
308 |
Generates butterfly for a source model.
|
309 |
"""
|
310 |
# Create instance of application
|
311 |
app = csbutterfly(sys.argv) |
312 |
|
313 |
# Open logfile
|
314 |
app.logFileOpen() |
315 |
|
316 |
# Execute application
|
317 |
app.execute() |
318 |
|