1 | #!/usr/bin/python -itt
|
---|
2 |
|
---|
3 | import struct
|
---|
4 | import sys
|
---|
5 | import numpy as np
|
---|
6 | from pprint import pprint
|
---|
7 |
|
---|
8 | import rlcompleter
|
---|
9 | import readline
|
---|
10 | readline.parse_and_bind('tab: complete')
|
---|
11 |
|
---|
12 | import matplotlib.pyplot as plt
|
---|
13 |
|
---|
14 | class Run(object):
|
---|
15 | def __init__(self, header):
|
---|
16 | self.header = header
|
---|
17 | self.footer = None
|
---|
18 | self.events = []
|
---|
19 | self.header_dict = {}
|
---|
20 | self.makeHeaderDict()
|
---|
21 |
|
---|
22 | def makeHeaderDict(self):
|
---|
23 | """ this is just moving the plain 272-float numbers from the original
|
---|
24 | header int a dict.
|
---|
25 | Not sure, if the key naming is well done :-/
|
---|
26 | """
|
---|
27 | if not len(self.header) == 272:
|
---|
28 | raise TypeException('self.header length must be 272, but is: '+str(len(self.header)))
|
---|
29 |
|
---|
30 | h = self.header
|
---|
31 | d = self.header_dict
|
---|
32 |
|
---|
33 | d['run number']=h[0]
|
---|
34 | d['date of begin run']=int(round(h[1]))
|
---|
35 | d['version of program']=h[2]
|
---|
36 |
|
---|
37 | n_obs_levels = int(round(h[3]))
|
---|
38 | if (n_obs_levels < 1) or (n_obs_levels > 10):
|
---|
39 | ValueException('number of observation levels n must be 0 < n < 11, but is: '+str(h[3]))
|
---|
40 | obs_levels = []
|
---|
41 | for i in range(4,4+n_obs_levels):
|
---|
42 | obs_levels.append(h[i])
|
---|
43 | d['observation levels']=tuple(obs_levels)
|
---|
44 | del obs_levels, n_obs_levels
|
---|
45 |
|
---|
46 | d['slope of energy spektrum']=h[14]
|
---|
47 | d['energy range']=(h[15],h[16])
|
---|
48 | d['flag for EGS4 treatment of em. component'] = h[17]
|
---|
49 | d['flag for NKG treatment of em. component'] = h[18]
|
---|
50 |
|
---|
51 | d['kin. energy cutoff for hadrons in GeV'] = h[19]
|
---|
52 | d['kin. energy cutoff for muons in GeV'] = h[20]
|
---|
53 | d['kin. energy cutoff for electrons in GeV'] = h[21]
|
---|
54 | d['energy cutoff for photons in GeV'] = h[22]
|
---|
55 |
|
---|
56 | physical_constants = []
|
---|
57 | for i in range(23,73):
|
---|
58 | physical_constants.append(h[i])
|
---|
59 | d['phyiscal constants']=tuple(physical_constants)
|
---|
60 | del physical_constants
|
---|
61 |
|
---|
62 | d['X-displacement of inclined observation plane'] = h[73]
|
---|
63 | d['Y-displacement of inclined observation plane'] = h[74]
|
---|
64 | d['Z-displacement of inclined observation plane'] = h[75]
|
---|
65 | d['theta angle of normal vector of inclined observation plane'] = h[76]
|
---|
66 | d['phi angle of normal vector of inclined observation plane'] = h[77]
|
---|
67 |
|
---|
68 | # now some constants, I don't understand
|
---|
69 | cka = []
|
---|
70 | for i in range(93,133):
|
---|
71 | cka.append(h[i])
|
---|
72 | d['CKA'] = tuple(cka)
|
---|
73 | del cka
|
---|
74 |
|
---|
75 | ceta = []
|
---|
76 | for i in range(133,138):
|
---|
77 | ceta.append(h[i])
|
---|
78 | d['CETA'] = tuple(ceta)
|
---|
79 | del ceta
|
---|
80 |
|
---|
81 | cstrba = []
|
---|
82 | for i in range(138,149):
|
---|
83 | cstrba.append(h[i])
|
---|
84 | d['CSTRBA'] = tuple(cstrba)
|
---|
85 | del cstrba
|
---|
86 |
|
---|
87 | d['scatter range in x direction for Cherenkov'] = h[246]
|
---|
88 | d['scatter range in y direction for Cherenkov'] = h[247]
|
---|
89 |
|
---|
90 | hlay = []
|
---|
91 | for i in range(248,253):
|
---|
92 | hlay.append(h[i])
|
---|
93 | d['HLAY'] = tuple(hlay)
|
---|
94 | del hlay
|
---|
95 |
|
---|
96 | aatm = []
|
---|
97 | for i in range(253,258):
|
---|
98 | aatm.append(h[i])
|
---|
99 | d['AATM'] = tuple(aatm)
|
---|
100 | del aatm
|
---|
101 |
|
---|
102 | batm = []
|
---|
103 | for i in range(258,263):
|
---|
104 | batm.append(h[i])
|
---|
105 | d['BATM'] = tuple(batm)
|
---|
106 | del batm
|
---|
107 |
|
---|
108 | catm = []
|
---|
109 | for i in range(263,268):
|
---|
110 | catm.append(h[i])
|
---|
111 | d['CATM'] = tuple(catm)
|
---|
112 | del catm
|
---|
113 |
|
---|
114 | d['NFLAIN'] = h[268]
|
---|
115 | d['NFLDIF'] = h[269]
|
---|
116 | d['NFLPI0 + 100 x NFLPIF'] = h[270]
|
---|
117 | d['NFLCHE + 100 x NFRAGM'] = h[271]
|
---|
118 |
|
---|
119 |
|
---|
120 | def __repr__(self):
|
---|
121 | print " Run Header "
|
---|
122 | print "="*70
|
---|
123 | pprint(self.header_dict)
|
---|
124 | print "="*70
|
---|
125 | print " End of: Run Header "
|
---|
126 | print "="*70
|
---|
127 | return ""
|
---|
128 |
|
---|
129 |
|
---|
130 |
|
---|
131 | class Event(object):
|
---|
132 | def __init__(self, header):
|
---|
133 | self.header = header
|
---|
134 | self.footer = None
|
---|
135 | self.photons = None
|
---|
136 | self.header_dict = {}
|
---|
137 | self.makeHeaderDict()
|
---|
138 |
|
---|
139 | # Info dict should contain just some quick infor about
|
---|
140 | # this event.
|
---|
141 | # * core location
|
---|
142 | # * energy
|
---|
143 | # * primary incident angle in radian
|
---|
144 | # *
|
---|
145 | # * number of photons
|
---|
146 | self.info_dict = {}
|
---|
147 |
|
---|
148 |
|
---|
149 | def makeInfoDict(self):
|
---|
150 |
|
---|
151 | d = self.info_dict
|
---|
152 |
|
---|
153 | d['energy'] = self.header_dict['total energy in GeV']
|
---|
154 | d['angle_zenith'] = self.header_dict['angle in radian: (zenith, azimuth)'][0]
|
---|
155 | d['angle_azimuth'] = self.header_dict['angle in radian: (zenith, azimuth)'][1]
|
---|
156 | d['core_loc'] = self.header_dict['core location for scattered events in cm: (x,y)'][0]
|
---|
157 | d['event_id'] = self.header_dict['event number']
|
---|
158 | d['momentum'] = np.array(self.header_dict['momentum in GeV/c in (x, y, -z) direction;'])
|
---|
159 |
|
---|
160 | #print "self.photons is None", self.photons is None
|
---|
161 | if self.photons is None:
|
---|
162 | d['num_photons'] = 0
|
---|
163 | else:
|
---|
164 | d['num_photons'] = self.photons.shape[0]
|
---|
165 |
|
---|
166 | def printInfoDict(self):
|
---|
167 | print " Info Dict "
|
---|
168 | print "="*70
|
---|
169 | pprint(self.info_dict)
|
---|
170 | print "="*70
|
---|
171 | print " End of: Info Dict "
|
---|
172 | print "="*70
|
---|
173 | return ""
|
---|
174 |
|
---|
175 |
|
---|
176 | def makeHeaderDict(self):
|
---|
177 | if not len(self.header) == 272:
|
---|
178 | raise TypeException('self.header length must be 272, but is: '+str(len(self.header)))
|
---|
179 |
|
---|
180 | h = self.header
|
---|
181 | d = self.header_dict
|
---|
182 |
|
---|
183 | d['event number'] = long( round(h[0]) )
|
---|
184 | d['particle id (particle code or A x 100 + Z for nuclei)'] = long( round(h[1]) )
|
---|
185 |
|
---|
186 | d['total energy in GeV'] = h[2]
|
---|
187 | d['starting altitude in g/cm2'] = h[3]
|
---|
188 | d['number of first target if fixed'] = h[4]
|
---|
189 | d['z coordinate (height) of first interaction in cm'] = h[5]
|
---|
190 | d['momentum in GeV/c in (x, y, -z) direction;'] = (h[6],h[7],h[8])
|
---|
191 | d['angle in radian: (zenith, azimuth)'] = (h[9],h[10])
|
---|
192 |
|
---|
193 | n_random_number_sequences = int(round(h[11]))
|
---|
194 | if (n_random_number_sequences < 1) or (n_random_number_sequences > 10):
|
---|
195 | ValueException('number of random number sequences n must be 0 < n < 11, but is: '+str(h[11]))
|
---|
196 | rand_number_sequences = []
|
---|
197 | for i in range(12,12 + 3*n_random_number_sequences,3):
|
---|
198 | seed = long( round(h[i]) )
|
---|
199 | number_of_calls = long(round(h[i+2]))*1e6 + long(round(h[i+1]))
|
---|
200 | rand_number_sequences.append( (seed, number_of_calls) )
|
---|
201 | d['random number sequences: (seed, # of offset random calls)']=tuple(rand_number_sequences)
|
---|
202 |
|
---|
203 | # these are already in run_header ...
|
---|
204 | # can be used for sanity check
|
---|
205 | # -------------------------------------------------------
|
---|
206 | d['run number'] = h[42]
|
---|
207 | d['date of begin run (yymmdd)'] = int( round(h[43]) )
|
---|
208 | d['version of program'] = h[44]
|
---|
209 |
|
---|
210 | n_obs_levels = int(round(h[45]))
|
---|
211 | if (n_obs_levels < 1) or (n_obs_levels > 10):
|
---|
212 | ValueException('number of observation levels n must be 0 < n < 11, but is: '+str(h[3]))
|
---|
213 | obs_levels = []
|
---|
214 | for i in range(46,46+n_obs_levels):
|
---|
215 | obs_levels.append(h[i])
|
---|
216 | d['observation levels']=tuple(obs_levels)
|
---|
217 | del obs_levels, n_obs_levels
|
---|
218 |
|
---|
219 | d['slope of energy spektrum']=h[56]
|
---|
220 | d['energy range']=(h[57],h[58])
|
---|
221 |
|
---|
222 | d['kin. energy cutoff for hadrons in GeV'] = h[59]
|
---|
223 | d['kin. energy cutoff for muons in GeV'] = h[60]
|
---|
224 | d['kin. energy cutoff for electrons in GeV'] = h[61]
|
---|
225 | d['energy cutoff for photons in GeV'] = h[62]
|
---|
226 |
|
---|
227 | d['NFLAIN'] = h[63]
|
---|
228 | d['NFLDIF'] = h[64]
|
---|
229 | d['NFLPI0'] = h[65]
|
---|
230 | d['NFLPIF'] = h[66]
|
---|
231 | d['NFLCHE'] = h[67]
|
---|
232 | d['NFRAGM'] = h[68]
|
---|
233 |
|
---|
234 | d["Earth's magnetic field in uT: (x,z)"] = ( h[69], h[70] )
|
---|
235 |
|
---|
236 | d['flag for activating EGS4'] = h[71]
|
---|
237 | d['flag for activating NKG'] = h[72]
|
---|
238 | d['low-energy hadr. model flag (1.=GHEISHA, 2.=UrQMD, 3.=FLUKA)'] = h[73]
|
---|
239 | d['high-energy hadr. model flag (0.=HDPM,1.=VENUS, 2.=SIBYLL,3.=QGSJET, 4.=DPMJET, 5.=NE X US, 6.=EPOS)'] = h[74]
|
---|
240 | d['CERENKOV Flag (is a bitmap --> usersguide)'] = hex(int(round(h[75])))
|
---|
241 | d['NEUTRINO flag'] = h[76]
|
---|
242 | d['CURVED flag (0=standard, 2=CURVED)'] = h[77]
|
---|
243 | d['computer flag (3=UNIX, 4=Macintosh)'] = h[78]
|
---|
244 | d['theta interval (in degree): (lower, upper edge) '] = ( h[79], h[80] )
|
---|
245 | d['phi interval (in degree): (lower, upper edge) '] = ( h[81], h[82] )
|
---|
246 |
|
---|
247 | d['Cherenkov bunch size in the case of Cherenkov calculations'] = h[83]
|
---|
248 | d['number of Cherenkov detectors in (x, y) direction'] = (h[84], h[85])
|
---|
249 | d['grid spacing of Cherenkov detectors in cm (x, y) direction'] = ( h[86], h[87])
|
---|
250 | d['length of each Cherenkov detector in cm in (x, y) direction'] = ( h[88], h[89])
|
---|
251 | d['Cherenkov output directed to particle output file (= 0.) or Cherenkov output file (= 1.)'] = h[90]
|
---|
252 |
|
---|
253 | d['angle (in rad) between array x-direction and magnetic north'] = h[91]
|
---|
254 | d['flag for additional muon information on particle output file'] = h[92]
|
---|
255 | d['step length factor for multiple scattering step length in EGS4'] = h[93]
|
---|
256 | d['Cherenkov bandwidth in nm: (lower, upper) end'] = ( h[94], h[95] )
|
---|
257 | d['number i of uses of each Cherenkov event'] = h[96]
|
---|
258 |
|
---|
259 | core_location_for_scattered_events_x_location = []
|
---|
260 | core_location_for_scattered_events_y_location = []
|
---|
261 | for i in range(97,117):
|
---|
262 | core_location_for_scattered_events_x_location.append(h[i])
|
---|
263 | for i in range(117,137):
|
---|
264 | core_location_for_scattered_events_y_location.append(h[i])
|
---|
265 | d['core location for scattered events in cm: (x,y)'] = zip(
|
---|
266 | core_location_for_scattered_events_x_location,
|
---|
267 | core_location_for_scattered_events_y_location)
|
---|
268 |
|
---|
269 | d['SIBYLL interaction flag (0.= no SIBYLL, 1.=vers.1.6; 2.=vers.2.1)'] = h[137]
|
---|
270 | d['SIBYLL cross-section flag (0.= no SIBYLL, 1.=vers.1.6; 2.=vers.2.1)'] = h[138]
|
---|
271 | d['QGSJET interact. flag (0.=no QGSJET, 1.=QGSJETOLD,2.=QGSJET01c, 3.=QGSJET-II)'] = h[139]
|
---|
272 | d['QGSJET X-sect. flag (0.=no QGSJET, 1.=QGSJETOLD,2.=QGSJET01c, 3.=QGSJET-II)'] = h[140]
|
---|
273 | d['DPMJET interaction flag (0.=no DPMJET, 1.=DPMJET)'] = h[141]
|
---|
274 | d['DPMJET cross-section flag (0.=no DPMJET, 1.=DPMJET)'] = h[142]
|
---|
275 | d['VENUS/NE X US/EPOS cross-section flag (0=neither, 1.=VENUSSIG,2./3.=NEXUSSIG, 4.=EPOSSIG)'] = h[143]
|
---|
276 | d['muon multiple scattering flag (1.=Moliere, 0.=Gauss)'] = h[144]
|
---|
277 | d['NKG radial distribution range in cm'] = h[145]
|
---|
278 | d['EFRCTHN energy fraction of thinning level hadronic'] = h[146]
|
---|
279 | d['EFRCTHN x THINRAT energy fraction of thinning level em-particles'] = h[147]
|
---|
280 | d['actual weight limit WMAX for thinning hadronic'] = h[148]
|
---|
281 | d['actual weight limit WMAX x WEITRAT for thinning em-particles'] = h[149]
|
---|
282 | d['max. radius (in cm) for radial thinning'] = h[150]
|
---|
283 | d['viewing cone VIEWCONE (in deg): (inner, outer) angle'] = (h[151], h[152])
|
---|
284 | d['transition energy high-energy/low-energy model (in GeV)'] = h[153]
|
---|
285 | d['skimming incidence flag (0.=standard, 1.=skimming)'] = h[154]
|
---|
286 | d['altitude (cm) of horizontal shower axis (skimming incidence)'] = h[155]
|
---|
287 | d['starting height (cm)'] = h[156]
|
---|
288 | d['flag indicating that explicite charm generation is switched on'] = h[156]
|
---|
289 | d['flag for hadron origin of electromagnetic subshower on particle tape'] = h[157]
|
---|
290 | d['flag for observation level curvature (CURVOUT) (0.=flat, 1.=curved)'] = h[166]
|
---|
291 |
|
---|
292 | def printHeaderDict(self):
|
---|
293 | print " Event Header "
|
---|
294 | print "="*70
|
---|
295 | pprint(self.header_dict)
|
---|
296 | print "="*70
|
---|
297 | print " End of: Event Header "
|
---|
298 | print "="*70
|
---|
299 |
|
---|
300 | def __repr__(self):
|
---|
301 | self.printInfoDict()
|
---|
302 | return ""
|
---|
303 |
|
---|
304 |
|
---|
305 | ##############################################################################
|
---|
306 | ##############################################################################
|
---|
307 | #
|
---|
308 | # Two helper functions for plotting the contents of a corsika run follow
|
---|
309 | #
|
---|
310 |
|
---|
311 | def plot_x_y(run, x, y, bins):
|
---|
312 |
|
---|
313 | plt.ion()
|
---|
314 | fig = plt.figure()
|
---|
315 |
|
---|
316 | if y == None:
|
---|
317 | # find min and max of x
|
---|
318 | xmin = np.inf
|
---|
319 | xmax = -np.inf
|
---|
320 |
|
---|
321 | for ev in run.events:
|
---|
322 | if ev.photons[:,x].min() < xmin:
|
---|
323 | xmin = ev.photons[:,x].min()
|
---|
324 | if ev.photons[:,x].max() > xmax:
|
---|
325 | xmax = ev.photons[:,x].max()
|
---|
326 |
|
---|
327 | ra = np.array([xmin,xmax])
|
---|
328 | Hsum = None
|
---|
329 | for ev in run.events:
|
---|
330 | p = ev.photons
|
---|
331 | H,xb = np.histogram(p[:,x], bins=bins, range=ra)
|
---|
332 | if Hsum == None:
|
---|
333 | Hsum = H
|
---|
334 | else:
|
---|
335 | Hsum += H
|
---|
336 |
|
---|
337 | width = 0.7*(xb[1]-xb[0])
|
---|
338 | center = (xb[:-1]+xb[1:])/2
|
---|
339 | plt.bar(center, Hsum, align = 'center', width = width)
|
---|
340 |
|
---|
341 | else:
|
---|
342 |
|
---|
343 |
|
---|
344 | # find min and max of x and y
|
---|
345 | xmin = np.inf
|
---|
346 | xmax = -np.inf
|
---|
347 | ymin = np.inf
|
---|
348 | ymax = -np.inf
|
---|
349 |
|
---|
350 | for ev in run.events:
|
---|
351 | #print x,".min=", ev.photons[:,x].min()
|
---|
352 | #print x,".max=", ev.photons[:,x].max()
|
---|
353 | #print y,".min=", ev.photons[:,y].min()
|
---|
354 | #print y,".max=", ev.photons[:,y].max()
|
---|
355 |
|
---|
356 | if ev.photons is None: continue
|
---|
357 |
|
---|
358 |
|
---|
359 | if ev.photons[:,x].min() < xmin:
|
---|
360 | xmin = ev.photons[:,x].min()
|
---|
361 | if ev.photons[:,x].max() > xmax:
|
---|
362 | xmax = ev.photons[:,x].max()
|
---|
363 | if ev.photons[:,y].min() < ymin:
|
---|
364 | ymin = ev.photons[:,y].min()
|
---|
365 | if ev.photons[:,y].max() > ymax:
|
---|
366 | ymax = ev.photons[:,y].max()
|
---|
367 |
|
---|
368 | #xmin, ymin = -1, -1
|
---|
369 | #xmax, ymax = 1,1
|
---|
370 | # gather complete histo
|
---|
371 | ra = np.array([xmin,xmax,ymin,ymax]).reshape(2,2)
|
---|
372 | Hsum = None
|
---|
373 | for ev in run.events:
|
---|
374 | p = ev.photons
|
---|
375 | if p is None: continue
|
---|
376 | H,xb,yb = np.histogram2d(p[:,x], p[:,y],bins=[bins,bins], range=ra)
|
---|
377 | if Hsum == None:
|
---|
378 | Hsum = H
|
---|
379 | else:
|
---|
380 | Hsum += H
|
---|
381 |
|
---|
382 | extent = [ yb[0] , yb[-1] , xb[0] , xb[-1] ]
|
---|
383 | plt.imshow(Hsum, extent=extent, interpolation='nearest')
|
---|
384 |
|
---|
385 | plt.colorbar()
|
---|
386 |
|
---|
387 |
|
---|
388 | def plot_xy_rel_core_location(run, bins):
|
---|
389 |
|
---|
390 | plt.ion()
|
---|
391 | fig = plt.figure()
|
---|
392 |
|
---|
393 |
|
---|
394 | # find min and max of x and y
|
---|
395 | xmin = np.inf
|
---|
396 | xmax = -np.inf
|
---|
397 | ymin = np.inf
|
---|
398 | ymax = -np.inf
|
---|
399 |
|
---|
400 | for ev in run.events:
|
---|
401 | #print x,".min=", ev.photons[:,x].min()
|
---|
402 | #print x,".max=", ev.photons[:,x].max()
|
---|
403 | #print y,".min=", ev.photons[:,y].min()
|
---|
404 | #print y,".max=", ev.photons[:,y].max()
|
---|
405 |
|
---|
406 | if ev.photons is None: continue
|
---|
407 |
|
---|
408 | # get core-location from event header dictionary
|
---|
409 | cl = np.array(ev.header_dict['core location for scattered events in cm: (x,y)'][0])
|
---|
410 |
|
---|
411 | xy = ev.photons[:,1:3] - cl
|
---|
412 |
|
---|
413 |
|
---|
414 | if xy[:,0].min() < xmin:
|
---|
415 | xmin = xy[:,0].min()
|
---|
416 | if xy[:,0].max() > xmax:
|
---|
417 | xmax = xy[:,0].max()
|
---|
418 | if xy[:,1].min() < ymin:
|
---|
419 | ymin = xy[:,1].min()
|
---|
420 | if xy[:,1].max() > ymax:
|
---|
421 | ymax = xy[:,1].max()
|
---|
422 |
|
---|
423 | #xmin, ymin = -1, -1
|
---|
424 | #xmax, ymax = 1,1
|
---|
425 | # gather complete histo
|
---|
426 | ra = np.array([xmin,xmax,ymin,ymax]).reshape(2,2)
|
---|
427 | Hsum = None
|
---|
428 | for index, ev in enumerate(run.events):
|
---|
429 | if ev.photons is None: continue
|
---|
430 | # get core-location from event header dictionary
|
---|
431 | cl = np.array(ev.header_dict['core location for scattered events in cm: (x,y)'][0])
|
---|
432 | xy = ev.photons[:,1:3] - cl
|
---|
433 |
|
---|
434 | H,xb,yb = np.histogram2d(xy[:,0], xy[:,1], bins=[bins,bins], range=ra)
|
---|
435 | if Hsum == None:
|
---|
436 | Hsum = H
|
---|
437 | else:
|
---|
438 | Hsum += H
|
---|
439 | #print "index: ", index, "H.sum:", H.sum()
|
---|
440 | #print "index: ", index, "Hsum.sum:", Hsum.sum()
|
---|
441 |
|
---|
442 | extent = [ yb[0] , yb[-1] , xb[0] , xb[-1] ]
|
---|
443 | plt.imshow(Hsum, extent=extent, interpolation='nearest')
|
---|
444 | plt.colorbar()
|
---|
445 | return Hsum
|
---|
446 |
|
---|
447 |
|
---|
448 |
|
---|
449 |
|
---|
450 |
|
---|
451 | def read_corsika_file( filename ):
|
---|
452 | f = open(filename, 'rb')
|
---|
453 |
|
---|
454 | while True: # the only break out of this while is finding a run footer block - "RUNE"
|
---|
455 |
|
---|
456 | # Each "block" in a corika file is always 273 "words" long.
|
---|
457 | # a work is a 4-byte value, usually a double, but might also be an int.
|
---|
458 | # The first "word" of a block is sometimes a sequence of 4 ascii
|
---|
459 | # charaters, in order to tell the reader, what type of block follows next.
|
---|
460 | # these sequences can be:
|
---|
461 | # * "RUNH" - a run header block
|
---|
462 | # * "RUNE" - a run footer block
|
---|
463 | # * "EVTH" - an event header block
|
---|
464 | # * "EVTE" - an event footer block
|
---|
465 | # the corsika manual states more of there, but those are not
|
---|
466 | # expected in a corsika "cer-file".
|
---|
467 | block = f.read(273*4)
|
---|
468 | if not block:
|
---|
469 | raise Exception('End of file '+ filename +' reached, before ' +
|
---|
470 | '"RUNE" word found. File might be corrupted.')
|
---|
471 | if not len(block) == 273*4:
|
---|
472 | raise Exception("""While reading %s a block smaller than 273*4 bytes
|
---|
473 | was read, this might be due to non-blocking read
|
---|
474 | which is clearly a bug in this software.
|
---|
475 | you might want to simply try again. """ % (filename,))
|
---|
476 |
|
---|
477 | # what ever block we have, we treat the first 4 bytes as
|
---|
478 | # a 4-character string. In case it is not one of the
|
---|
479 | # "block descriptors", we assume it is a data-block.
|
---|
480 | block_type = block[0:4]
|
---|
481 |
|
---|
482 | # this reader has no error handling of the underlying file at all.
|
---|
483 | # so in case there is an EVTH found before a RUNH the reader will
|
---|
484 | # probably totally freak out :-)
|
---|
485 | # This is a task for captain state-machine :-)
|
---|
486 |
|
---|
487 | if block_type == 'RUNH':
|
---|
488 | # if we hava a run-header block, the trailing 272 words are
|
---|
489 | # unpacked into a list of floats. from this list a Run-object
|
---|
490 | # is created, which is mainly empty up to that point.
|
---|
491 | run_header = struct.unpack( '272f', block[4:] )
|
---|
492 | run = Run(run_header)
|
---|
493 |
|
---|
494 | elif block_type == 'EVTH':
|
---|
495 | # directly after the RUNH an event header is expected.
|
---|
496 | # so if that event header is found. again the trailing
|
---|
497 | # 272 words areunpacked into a list of 272 floats and used to
|
---|
498 | # call the constructor of an Event instance.
|
---|
499 | # this event, still it is mainly empty, is appended to the
|
---|
500 | # list of events, maintained by the run object.
|
---|
501 | #
|
---|
502 | # go on reading in the else-block of this of condition.
|
---|
503 | event_header = struct.unpack( '272f', block[4:] )
|
---|
504 | event = Event(event_header)
|
---|
505 | run.events.append(event)
|
---|
506 |
|
---|
507 | elif block_type == 'EVTE':
|
---|
508 | # after all data blocks for a certain event have been found
|
---|
509 | # we will eventually find a event footer block.
|
---|
510 | # the footer does not contain much information, but it is still
|
---|
511 | # safed with the current event.
|
---|
512 | # It could be used to check, if we found a footer, which fits to the
|
---|
513 | # event header, but that is not done here.
|
---|
514 | event.footer = struct.unpack( '272f', block[4:] )
|
---|
515 |
|
---|
516 | # When footer is found, we know that the event is
|
---|
517 | # fully read. So we can make a python dict, which contains
|
---|
518 | # some short info about his event, like the number of photons
|
---|
519 | # which is not stored in the event header.
|
---|
520 | event.makeInfoDict()
|
---|
521 | event = None
|
---|
522 |
|
---|
523 | elif block_type == 'RUNE':
|
---|
524 | # when the run footer block is found, we know we have
|
---|
525 | # reached the end of the file, since as far as I have seen
|
---|
526 | # there is only one run in one corsika file.
|
---|
527 | # In addition one can find a lot of zeroes at the end of a corsika
|
---|
528 | # file after the run footer block, so it is not safe to
|
---|
529 | # simply read until the end of file and try to parse everything.
|
---|
530 | run.footer = struct.unpack( '272f', block[4:] )
|
---|
531 | break
|
---|
532 | else:
|
---|
533 | # since there was no block-descripter found, we assume this
|
---|
534 | # block is a data block. the 273 words of this block are
|
---|
535 | # again unpacked into 273 floats this time.
|
---|
536 | # from the corsika manual we know, that a data block is grouped
|
---|
537 | # into 39 groups of 7 words, so we form an np.array of shape
|
---|
538 | # 39 x 7
|
---|
539 | data_block = struct.unpack( '273f', block )
|
---|
540 | data_array = np.array( data_block, dtype=np.float32 ).reshape(39,7)
|
---|
541 |
|
---|
542 | # the data we read in here, does not comply with the original
|
---|
543 | # data sheet.
|
---|
544 | # corsika was edited by somebody calles Dorota from Lodz in order to write the
|
---|
545 | # wavelength of the simulated cherenkov photons and some other info.
|
---|
546 | # I did not find any docu, but had a look into the code, which
|
---|
547 | # luckily is stored right next to the binary. :-)
|
---|
548 | # according to that code the 1st element of the 7-element-tuple
|
---|
549 | # belonging to each cherenkov-photon stores 3 types of information:
|
---|
550 | # a parameter called J ... which might be the parent particles type
|
---|
551 | # a parameter called IMOV, which seems to be constant = 1
|
---|
552 | # and which seems to have something to do with reusing showers
|
---|
553 | # a parameter called WAVELENGTH which seems to be the cherekov
|
---|
554 | # photon wavelength in nm.
|
---|
555 | #
|
---|
556 | # the code looks like this:
|
---|
557 | # J*100000. + IMOV*1000. + WAVELENGTH
|
---|
558 | new_extra_data = np.zeros( (39,3), dtype=np.float32 )
|
---|
559 | for index, row in enumerate(data_array):
|
---|
560 | integer = long(round(row[0]))
|
---|
561 | J = integer/100000
|
---|
562 | IMOV = (integer-J*100000)/1000
|
---|
563 | WAVELENGTH = np.mod(row[0], 1000.)
|
---|
564 | new_extra_data[index,0] = J
|
---|
565 | new_extra_data[index,1] = IMOV
|
---|
566 | new_extra_data[index,2] = WAVELENGTH
|
---|
567 |
|
---|
568 | data_array = np.concatenate( (data_array,new_extra_data), axis=1)
|
---|
569 |
|
---|
570 | # so at this point we have made from the 39x7 shaped array an
|
---|
571 | # 39x10 shaped array.
|
---|
572 | # the contents of this array are 4 byte floats and have
|
---|
573 | # the following meansings:
|
---|
574 | # d = data_array[0]
|
---|
575 | # d[0] - encoded info from dorota. see index:7,8,9
|
---|
576 | # d[1] - x position in cm; x-axis points north in corsika
|
---|
577 | # d[2] - y position in cm; y-axis points west in corsika
|
---|
578 | # both positions are measured at the "observation level" aka ground
|
---|
579 | # d[3] - direction cosine to x-axis, called "u" in corsika
|
---|
580 | # d[4] - direction cosine to y-axis, called "v" in corsika
|
---|
581 | # d[5] - time since first interaction in ns
|
---|
582 | # d[6] - height of production in cm;
|
---|
583 | #
|
---|
584 | # now we can't be sure, since we have it reverse engineered.
|
---|
585 | # d[7] - "J", maybe particle ID of ch. light emitting particle
|
---|
586 | # d[8] - "IMOV", apparently always 1, connection with reusing of showers
|
---|
587 | # d[9] - "WAVELENGTH" wavelength of the photon in nm.
|
---|
588 |
|
---|
589 |
|
---|
590 | # since a data block has a fixed size, it can contain
|
---|
591 | # empty rows, we want to find the first empty row here.
|
---|
592 | first_empty_row_index = False
|
---|
593 | for row_index, row in enumerate(data_array):
|
---|
594 | if (row == 0.).all():
|
---|
595 | first_empty_row_index = row_index
|
---|
596 | break
|
---|
597 |
|
---|
598 | # and here we simply cut off all empty rows.
|
---|
599 | if first_empty_row_index:
|
---|
600 | data_array = data_array[ 0:first_empty_row_index, : ]
|
---|
601 |
|
---|
602 | # here we append our data_array to the np.array, which is stored
|
---|
603 | # in the current event. keep in mind that the current event can simply
|
---|
604 | # be retrieved by "event".
|
---|
605 | if event.photons == None:
|
---|
606 | event.photons = data_array
|
---|
607 | else:
|
---|
608 | event.photons = np.concatenate( (event.photons, data_array), axis=0 )
|
---|
609 |
|
---|
610 | f.close()
|
---|
611 |
|
---|
612 | return run
|
---|
613 |
|
---|
614 |
|
---|
615 |
|
---|
616 |
|
---|
617 | if __name__ == '__main__':
|
---|
618 |
|
---|
619 | run = read_corsika_file( sys.argv[1] )
|
---|
620 | print 'Run object "run" created.'
|
---|
621 | print 'type: print run'
|
---|
622 | print 'or'
|
---|
623 | print 'for event in run.events:'
|
---|
624 | print ' for photon in event.photons:'
|
---|
625 | print ' print photon'
|
---|
626 | print
|
---|
627 | print 'to loop over everyting...'
|
---|
628 |
|
---|
629 |
|
---|
630 |
|
---|
631 | #plot_x_y(run, 3,4,1000)
|
---|
632 | #plt.title("photon distribution ... in theta vs phi ... or other way round")
|
---|
633 | #plot_x_y(run, 1,2,1000)
|
---|
634 | #plt.title("photon distribution ... x vs y .. or other way round")
|
---|
635 |
|
---|
636 | #Hsum = plot_xy_rel_core_location(run, 100)
|
---|
637 | #plt.title("photon distribution 100 bins; file:" + sys.argv[1])
|
---|
638 | #plt.xlabel('photon y location relative to "core location" in cm')
|
---|
639 | #plt.ylabel('photon x location relative to "core location" in cm')
|
---|
640 |
|
---|