Commit 39e9c7a9ef47cf59d1cf393f9d470cfe055063ea

Authored by knodlseder
1 parent a30f58c8

implement plotting

Showing 1 changed file with 274 additions and 71 deletions
test/example_binned.py
1 #! /usr/bin/env python 1 #! /usr/bin/env python
2 # ===========================================================================================# 2 # ===========================================================================================#
3 -# This script illustrates how to build a binned analysis chain using CTAtools. 3 +# This script illustrates how to build a binned analysis pipeline using CTAtools.
4 # 4 #
5 -# If matplotlib is installed, the spectrum will be displayed on the screen. 5 +# If matplotlib is installed, a counts spectrum and an offset histogram will be displayed.
6 # ===========================================================================================# 6 # ===========================================================================================#
7 from ctatools import * 7 from ctatools import *
  8 +from gammalib import *
8 from math import * 9 from math import *
  10 +import os
  11 +import glob
9 12
10 13
11 -# =================== #  
12 -# Processing pipeline #  
13 -# =================== #  
14 -def process(): 14 +# ============================= #
  15 +# Analysis pipeline - version 1 #
  16 +# ============================= #
  17 +def pipeline_v1():
15 """ 18 """
16 - Binned analysis processing pipeline. 19 + Binned analysis pipeline - save intermediate results.
  20 +
  21 + This function implements an analysis pipeline that successively calls
  22 + ctobssim, ctbin and ctlike by saving the intermediate results as FITS
  23 + files on disk. This replicates the way how the analysis would be done
  24 + in a ftools-like approach.
17 """ 25 """
18 # Set script parameters 26 # Set script parameters
19 model_name = "data/crab.xml" 27 model_name = "data/crab.xml"
@@ -42,6 +50,7 @@ def process(): @@ -42,6 +50,7 @@ def process():
42 50
43 # Simulate events 51 # Simulate events
44 sim = ctobssim() 52 sim = ctobssim()
  53 + sim.logFileOpen() # We need this to explicitely open the log file in Python mode
45 sim["infile"].filename(model_name) 54 sim["infile"].filename(model_name)
46 sim["outfile"].filename(events_name) 55 sim["outfile"].filename(events_name)
47 sim["caldb"].string(caldb) 56 sim["caldb"].string(caldb)
@@ -53,7 +62,7 @@ def process(): @@ -53,7 +62,7 @@ def process():
53 sim["tmax"].real(tstop) 62 sim["tmax"].real(tstop)
54 sim["emin"].real(emin) 63 sim["emin"].real(emin)
55 sim["emax"].real(emax) 64 sim["emax"].real(emax)
56 - sim.run() 65 + sim.execute()
57 print "Simulated events ("+str(sim.celapse())+" CPU seconds)" 66 print "Simulated events ("+str(sim.celapse())+" CPU seconds)"
58 67
59 # Update timing 68 # Update timing
@@ -62,6 +71,7 @@ def process(): @@ -62,6 +71,7 @@ def process():
62 71
63 # Bin events into counts map 72 # Bin events into counts map
64 bin = ctbin() 73 bin = ctbin()
  74 + bin.logFileOpen() # We need this to explicitely open the log file in Python mode
65 bin["evfile"].filename(events_name) 75 bin["evfile"].filename(events_name)
66 bin["outfile"].filename(cntmap_name) 76 bin["outfile"].filename(cntmap_name)
67 bin["emin"].real(emin) 77 bin["emin"].real(emin)
@@ -74,7 +84,7 @@ def process(): @@ -74,7 +84,7 @@ def process():
74 bin["xref"].real(ra) 84 bin["xref"].real(ra)
75 bin["yref"].real(dec) 85 bin["yref"].real(dec)
76 bin["proj"].string(proj) 86 bin["proj"].string(proj)
77 - bin.run() 87 + bin.execute()
78 print "Binned events into counts map ("+str(bin.celapse())+" CPU seconds)" 88 print "Binned events into counts map ("+str(bin.celapse())+" CPU seconds)"
79 89
80 # Update timing 90 # Update timing
@@ -83,6 +93,7 @@ def process(): @@ -83,6 +93,7 @@ def process():
83 93
84 # Perform maximum likelihood fitting 94 # Perform maximum likelihood fitting
85 like = ctlike() 95 like = ctlike()
  96 + like.logFileOpen() # We need this to explicitely open the log file in Python mode
86 like["cntmap"].filename(cntmap_name) 97 like["cntmap"].filename(cntmap_name)
87 like["srcmdl"].filename(model_name) 98 like["srcmdl"].filename(model_name)
88 like["outmdl"].filename(result_name) 99 like["outmdl"].filename(result_name)
@@ -90,7 +101,7 @@ def process(): @@ -90,7 +101,7 @@ def process():
90 like["stat"].string("POISSON") 101 like["stat"].string("POISSON")
91 like["caldb"].string(caldb) 102 like["caldb"].string(caldb)
92 like["irf"].string(irf) 103 like["irf"].string(irf)
93 - like.run() 104 + like.execute()
94 print "Maximum likelihood fitting ("+str(like.celapse())+" CPU seconds)" 105 print "Maximum likelihood fitting ("+str(like.celapse())+" CPU seconds)"
95 106
96 # Update timing 107 # Update timing
@@ -105,74 +116,242 @@ def process(): @@ -105,74 +116,242 @@ def process():
105 return 116 return
106 117
107 118
108 -# ============ #  
109 -# Show photons #  
110 -# ============ #  
111 -def show_photons(photons, xmlname, e_min, e_max, area, duration, ebins=30): 119 +# ============================= #
  120 +# Analysis pipeline - version 2 #
  121 +# ============================= #
  122 +def pipeline_v2():
112 """ 123 """
113 - Show photons using matplotlib (if available). 124 + Binned analysis pipeline - keep intermediate results in memory.
  125 +
  126 + This function implements an analysis pipeline that successively calls
  127 + ctobssim, ctbin and ctlike without saving the intermediate results as
  128 + FITS files on disk. All data are hold in memory.
  129 +
  130 + At the end, results are plotted (if matplotlib is installed)
  131 + """
  132 + # Set script parameters
  133 + model_name = "data/crab.xml"
  134 + caldb = "irf"
  135 + irf = "kb_E_50h_v3"
  136 + ra = 83.63
  137 + dec = 22.01
  138 + rad_sim = 10.0
  139 + tstart = 0.0
  140 + tstop = 1800.0
  141 + emin = 0.1
  142 + emax = 100.0
  143 + enumbins = 20
  144 + nxpix = 200
  145 + nypix = 200
  146 + binsz = 0.02
  147 + coordsys = "CEL"
  148 + proj = "CAR"
  149 +
  150 + # Initialise timing
  151 + wall_seconds = 0.0
  152 + cpu_seconds = 0.0
  153 +
  154 + # Simulate events
  155 + sim = ctobssim()
  156 + sim["infile"].filename(model_name)
  157 + sim["caldb"].string(caldb)
  158 + sim["irf"].string(irf)
  159 + sim["ra"].real(ra)
  160 + sim["dec"].real(dec)
  161 + sim["rad"].real(rad_sim)
  162 + sim["tmin"].real(tstart)
  163 + sim["tmax"].real(tstop)
  164 + sim["emin"].real(emin)
  165 + sim["emax"].real(emax)
  166 + sim.run()
  167 + print "Simulated events ("+str(sim.celapse())+" CPU seconds)"
  168 +
  169 + # Update timing
  170 + wall_seconds += sim.telapse()
  171 + cpu_seconds += sim.celapse()
  172 +
  173 + # Bin events into counts map
  174 + bin = ctbin(sim.obs())
  175 + bin["emin"].real(emin)
  176 + bin["emax"].real(emax)
  177 + bin["enumbins"].integer(enumbins)
  178 + bin["nxpix"].integer(nxpix)
  179 + bin["nypix"].integer(nypix)
  180 + bin["binsz"].real(binsz)
  181 + bin["coordsys"].string(coordsys)
  182 + bin["xref"].real(ra)
  183 + bin["yref"].real(dec)
  184 + bin["proj"].string(proj)
  185 + bin.run()
  186 + print "Binned events into counts map ("+str(bin.celapse())+" CPU seconds)"
  187 +
  188 + # Update timing
  189 + wall_seconds += bin.telapse()
  190 + cpu_seconds += bin.celapse()
  191 +
  192 + # Perform maximum likelihood fitting
  193 + like = ctlike(bin.obs())
  194 + like.run()
  195 + print "Maximum likelihood fitting ("+str(like.celapse())+" CPU seconds)"
  196 +
  197 + # Update timing
  198 + wall_seconds += like.telapse()
  199 + cpu_seconds += like.celapse()
  200 +
  201 + # Show total times
  202 + print "Total wall time elapsed: "+str(wall_seconds)+" seconds"
  203 + print "Total CPU time used ...: "+str(cpu_seconds)+" seconds"
  204 +
  205 + # Show model fitting results
  206 + #print like.obs().models()
  207 +
  208 + # Plot counts
  209 + plot_counts(bin.obs())
  210 +
  211 + # Return
  212 + return
  213 +
  214 +
  215 +# =========== #
  216 +# Plot counts #
  217 +# =========== #
  218 +def plot_counts(observations):
  219 + """
  220 + Plot counts.
114 """ 221 """
115 # Only proceed if matplotlib is available 222 # Only proceed if matplotlib is available
116 try: 223 try:
117 # Import matplotlib 224 # Import matplotlib
118 import matplotlib.pyplot as plt 225 import matplotlib.pyplot as plt
119 226
120 - # Create figure  
121 - plt.figure(1)  
122 - plt.title("MC simulated photon spectrum ("+str(e_min)+'-'+str(e_max)+" TeV)") 227 + # Set legend fontsize
  228 + params = {'legend.fontsize': 10}
  229 + plt.rcParams.update(params)
  230 +
  231 + # Set plot styles
  232 + styles = ['b-', 'g-', 'y-', 'n-']
  233 +
  234 + # Dump header
  235 + print ""
  236 + print "Make plots (using matplotlib):"
  237 + print "=============================="
123 238
124 - # Setup energy range covered by data  
125 - emin = GEnergy()  
126 - emax = GEnergy()  
127 - emin.TeV(e_min)  
128 - emax.TeV(e_max)  
129 - ebds = GEbounds()  
130 - ebds.setlog(emin, emax, ebins) 239 + # Create figure 1
  240 + plt.figure(1,figsize=(12,6))
  241 + plt.subplots_adjust(hspace=.7)
  242 +
  243 + # Create subplot 1
  244 + plt.subplot(121)
  245 + plt.title("Spectrum (summed over all pixels)")
  246 +
  247 + # Loop over observations
  248 + for obs in observations:
131 249
132 - # Create energy axis  
133 - energy = []  
134 - for i in range(ebds.size()):  
135 - energy.append(ebds.elogmean(i).TeV())  
136 -  
137 - # Fill histogram  
138 - counts = [0.0 for i in range(ebds.size())]  
139 - for photon in photons:  
140 - index = ebds.index(photon.energy())  
141 - counts[index] = counts[index] + 1.0  
142 -  
143 - # Create error bars  
144 - error = [sqrt(c) for c in counts]  
145 -  
146 - # Get model values  
147 - models = GModels(xmlname)  
148 - crab = cast_GModelSky(models[0])  
149 - model = []  
150 - d = GSkyDir()  
151 - d.radec_deg(83.6331, 22.0145)  
152 - t = GTime()  
153 - for i in range(ebds.size()):  
154 - eval = ebds.elogmean(i)  
155 - ewidth = ebds.emax(i) - ebds.emin(i)  
156 - f = crab.value(d, eval, t) * area * duration * ewidth.MeV()  
157 - model.append(f)  
158 -  
159 - # Plot data  
160 - plt.loglog(energy, counts, 'ro')  
161 - plt.errorbar(energy, counts, error, fmt=None, ecolor='r')  
162 -  
163 - # Plot model  
164 - plt.plot(energy, model, 'b-')  
165 -  
166 - # Set axes 250 + # Get event cube
  251 + cube = cast_GCTAEventCube(obs.events())
  252 +
  253 + # Create energy axis
  254 + energy = []
  255 + ebounds = cube.ebounds()
  256 + for i in range(ebounds.size()):
  257 + energy.append(ebounds.elogmean(i).TeV())
  258 +
  259 + # Create spectrum
  260 + print "Extract data:"
  261 + counts = [0.0 for i in range(ebounds.size())]
  262 + for bin in cube:
  263 + index = ebounds.index(bin.energy())
  264 + counts[index] = counts[index] + bin.counts()
  265 +
  266 + # Create error bars
  267 + error = [sqrt(c) for c in counts]
  268 +
  269 + # Plot spectrum
  270 + plt.loglog(energy, counts, 'ro', label='data')
  271 + plt.errorbar(energy, counts, error, fmt=None, ecolor='r')
  272 +
  273 + # Extract models
  274 + print "Extract models:"
  275 + sum_model = [0.0 for i in range(ebounds.size())]
  276 + for k, m in enumerate(observations.models()):
  277 + print "- "+m.name()
  278 + model = [0.0 for i in range(ebounds.size())]
  279 + for bin in cube:
  280 + index = ebounds.index(bin.energy())
  281 + prob = m.eval(bin, obs)
  282 + model[index] = model[index] + prob * bin.size()
  283 + for i in range(ebounds.size()):
  284 + sum_model[i] = sum_model[i] + model[i]
  285 + plt.loglog(energy, model, styles[k], label=m.name())
  286 + plt.loglog(energy, sum_model, 'r-', label='total')
  287 +
  288 + # Put labels
167 plt.xlabel("Energy (TeV)") 289 plt.xlabel("Energy (TeV)")
168 - plt.ylabel("Number of incident photons") 290 + plt.ylabel("Counts")
  291 + plt.legend(loc="lower left")
  292 +
  293 + # Create subplot 2
  294 + plt.subplot(122)
  295 + plt.title("Offset (summed over all energies)")
  296 +
  297 + # Set Crab direction
  298 + crab = GSkyDir()
  299 + crab.radec_deg(83.63, 22.01)
  300 +
  301 + # Loop over observations
  302 + for obs in observations:
  303 +
  304 + # Get event cube
  305 + cube = cast_GCTAEventCube(obs.events())
  306 +
  307 + # Create offset histogram
  308 + print "Extract data:"
  309 + nx = 30
  310 + doffset2 = 0.01
  311 + offset2 = [(i+0.5)*doffset2 for i in range(nx)]
  312 + counts = [0.0 for i in range(nx)]
  313 + for bin in cube:
  314 + off = bin.dir().dist_deg(crab)
  315 + off2 = off*off
  316 + index = int(off2/doffset2)
  317 + if index < nx:
  318 + counts[index] = counts[index] + bin.counts()
  319 +
  320 + # Create error bars
  321 + error = [sqrt(c) for c in counts]
169 322
170 - # Allocate histogram  
171 - # Show plot 323 + # Plot distribution
  324 + plt.semilogy(offset2, counts, 'ro', label='data')
  325 +
  326 + # Extract models
  327 + print "Extract models:"
  328 + sum_model = [0.0 for i in range(nx)]
  329 + for k, m in enumerate(observations.models()):
  330 + print "- "+m.name()
  331 + model = [0.0 for i in range(nx)]
  332 + for bin in cube:
  333 + off = bin.dir().dist_deg(crab)
  334 + off2 = off*off
  335 + index = int(off2/doffset2)
  336 + if index < nx:
  337 + prob = m.eval(bin, obs)
  338 + model[index] = model[index] + prob * bin.size()
  339 + for i in range(nx):
  340 + sum_model[i] = sum_model[i] + model[i]
  341 + plt.plot(offset2, model, styles[k], label=m.name())
  342 + plt.plot(offset2, sum_model, 'r-', label='total')
  343 + plt.ylim(ymin=0.1)
  344 +
  345 + # Put labels
  346 + plt.xlabel("Offset (deg^2)")
  347 + plt.ylabel("Counts")
  348 + plt.legend(loc="upper right")
  349 +
  350 + # Show counts spectra
172 plt.show() 351 plt.show()
173 352
174 except: 353 except:
175 - print "Matplotlib is not (correctly) installed on your system. No data are shown." 354 + print "Matplotlib is not (correctly) installed on your system. No counts spectra are shown."
176 355
177 # Return 356 # Return
178 return 357 return
@@ -190,14 +369,38 @@ if __name__ == &#39;__main__&#39;: @@ -190,14 +369,38 @@ if __name__ == &#39;__main__&#39;:
190 - Event simulation 369 - Event simulation
191 - Event binning 370 - Event binning
192 - Binned maximum likelihood fitting 371 - Binned maximum likelihood fitting
  372 +
  373 + Two variations of the script are implemented. One that calls the classes
  374 + like executables, storing the intermediate results on disk, and a second
  375 + that uses the results of the precedent class directly without storing
  376 + any data on disk.
  377 +
  378 + The second variation will also show a counts spectrum of the data after
  379 + fitting (only if matplotlib is available on your system). Note that the
  380 + counts spectrum is summed over the entire data space, i.e. no event selection
  381 + around the Crab has been performed.
193 """ 382 """
194 # Dump header 383 # Dump header
195 - print "*****************************"  
196 - print "* CTA binned analysis script *"  
197 - print "*****************************" 384 + print "*************************************"
  385 + print "* CTA binned analysis scripts *"
  386 + print "*************************************"
  387 + print "... this script will take 1-2 minutes"
198 388
199 - # Process data  
200 - process() 389 + # Remove any existing result files
  390 + list = [glob.glob("*.fits"), glob.glob("*.log"), glob.glob("*.xml")]
  391 + for files in list:
  392 + for file in files:
  393 + os.remove(file)
201 394
202 - # Show photons  
203 - #show_photons(photons, xmlname, e_min, e_max, area, duration) 395 + # Save intermediate results on disk
  396 + print ""
  397 + print "Executable analysis pipeline:"
  398 + print "============================="
  399 + pipeline_v1()
  400 +
  401 + # Do analysis in memory
  402 + print ""
  403 + print "In memory analysis pipeline:"
  404 + print "============================"
  405 + pipeline_v2()
  406 + print ""