source: fact/tools/pyscripts/simulation/readcorsika.py

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