source: fact/tools/pyscripts/pyfact/pyfact.py@ 17893

Last change on this file since 17893 was 17803, checked in by dneise, 11 years ago
adjusted libz loading to new_pyfact/pyfact.py
  • Property svn:executable set to *
File size: 39.1 KB
Line 
1#!/usr/bin/python -tt
2#
3# Werner Lustermann, Dominik Neise
4# ETH Zurich, TU Dortmund
5#
6import sys
7from ctypes import *
8import numpy as np
9import pprint # for SlowData
10from scipy import signal
11
12# get the ROOT stuff + my shared libs
13import ROOT
14# factfits_h.so is made from factfits.h and is used to access the data
15# make sure the location of factfits_h.so is in LD_LIBRARY_PATH.
16# having it in PYTHONPATH is *not* sufficient
17hostname = ROOT.gSystem.HostName()
18libz_path_dict = {
19 # hostname : /path/to/libz.so
20 'isdc' : "/usr/lib64/libz.so",
21 'neiseLenovo' : "/usr/lib/libz.so",
22 'factcontrol' : "/usr/lib/libz.so",
23 "max-K50AB" : "/usr/lib/x86_64-linux-gnu/libz.so",
24 "watz" : "/usr/lib/x86_64-linux-gnu/libz.so",
25 "grolsch" : "/usr/lib/i386-linux-gnu/libz.so",
26}
27libz_loaded = False
28for my_hostname in libz_path_dict:
29 if my_hostname in hostname:
30 ROOT.gSystem.Load(libz_path_dict[my_hostname])
31 libz_loaded = True
32if not libz_loaded:
33 print """Warning - Warning - Warning - Warning - Warning - Warning - Warning
34 I most probably need to load libz.so but I don't know where it is.
35
36 Please edit pyfact.py around line 16-24 and insert your hostname and your
37 path to your libz.so
38 Sorry for the inconvenience.
39 """
40 sys.exit(-1)
41
42
43root_make_string = ROOT.gSystem.GetMakeSharedLib()
44if not "-std=c++0x" in root_make_string:
45 root_make_string = root_make_string.replace('$Opt', '$Opt -std=c++0x -D HAVE_ZLIB')
46ROOT.gSystem.SetMakeSharedLib(root_make_string)
47
48ROOT.gROOT.ProcessLine(".L extern_Mars_mcore/izstream.h+O")
49ROOT.gROOT.ProcessLine(".L extern_Mars_mcore/fits.h+O")
50ROOT.gROOT.ProcessLine(".L extern_Mars_mcore/zfits.h+O")
51ROOT.gROOT.ProcessLine(".L extern_Mars_mcore/factfits.h+O")
52ROOT.gROOT.ProcessLine(".L calfactfits.h+O")
53
54ROOT.gInterpreter.GenerateDictionary("map<string,fits::Entry>","map;string;extern_Mars_mcore/fits.h")
55ROOT.gInterpreter.GenerateDictionary("pair<string,fits::Entry>","map;string;extern_Mars_mcore/fits.h")
56ROOT.gInterpreter.GenerateDictionary("map<string,fits::Table::Column>","map;string;extern_Mars_mcore/fits.h")
57ROOT.gInterpreter.GenerateDictionary("pair<string,fits::Table::Column>","map;string;extern_Mars_mcore/fits.h")
58
59#ROOT.gSystem.Load('my_string_h.so')
60ROOT.gSystem.Load('extern_Mars_mcore/fits_h.so')
61ROOT.gSystem.Load('extern_Mars_mcore/izstream_h.so')
62ROOT.gSystem.Load('extern_Mars_mcore/zfits_h.so')
63ROOT.gSystem.Load('extern_Mars_mcore/factfits_h.so')
64ROOT.gSystem.Load('calfactfits_h.so')
65from ROOT import *
66
67class RawDataFeeder( object ):
68 """ Wrapper class for RawData class
69 capable of iterating over multiple RawData Files
70 """
71
72 def __init__(self, filelist):
73 """ *filelist* list of files to iterate over
74 the list should contain tuples, or sublists of two filenames
75 the first should be a data file (\*.fits.gz)
76 the second should be an amplitude calibration file(\*.drs.fits.gz)
77 """
78
79 self.__module__ = 'pyfact'
80
81 # sanity check for input
82 if type(filelist) != type(list()):
83 raise TypeError('filelist should be a list')
84 for entry in filelist:
85 if len(entry) != 2:
86 raise TypeError('the entries of filelist should have length == 2')
87 for path in entry:
88 if type(path) != type(str()):
89 raise TypeError('the entries of filelist should be path, i.e. of type str()')
90 #todo check if 'path' is a valid path
91 # else: throw an Exception, or Warning?
92
93 self.filelist = filelist
94 self._current_RawData = RawData(filelist[0][0], filelist[0][1], return_dict=True)
95 del filelist[0]
96
97 def __iter__(self):
98 return self
99
100 def next():
101 """ Method being called by the iterator.
102 Since the RawData Objects are simply looped over, the event_id from the
103 RawData object will not be unique.
104 Each RawData obejct will start with event_id = 1 as usual.
105 """
106 try:
107 return self._current_RawData.next()
108 except StopIteration:
109 # current_RawData was completely processed
110 # delete it (I hope this calls the destructor of the fits file and/or closes it)
111 del self._current_RawData
112 # and remake it, if possible
113 if len(self.filelist) > 0:
114 self._current_RawData = RawData(filelist[0][0], filelist[0][1], return_dict=True)
115 del filelist[0]
116 else:
117 raise
118
119
120
121class RawData( object ):
122 """ raw data access and calibration
123
124 class is **iterable**
125
126 - open raw data file and drs calibration file
127 - performs amplitude calibration
128 - performs baseline substraction if wanted
129 - provides all data in an array:
130 row = number of pixel
131 col = length of region of interest
132
133 """
134
135
136 def __init__(self, data_file_name, calib_file_name,
137 baseline_file_name='',
138 return_dict = True,
139 use_CalFactFits = True,
140 do_calibration = True,
141 user_action_calib=lambda acal_data, data, blm, tom, gm, scells, nroi: None):
142 """ -constructor-
143
144 - open data file and calibration data file
145 - get basic information about the data in data_file_name
146 - allocate buffers for data access
147
148 *data_file_name* : fits or fits.gz file of the data including the path
149
150 *calib_file_name* : fits or fits.gz file containing DRS calibration data
151
152 *baseline_file_name* : npy file containing the baseline values
153
154 *return_dict* : this option will be removed in future releases.
155 formerly the next() method returned only a subset of (important) event information,
156 and it was not transparent how to retrieve the other (less important) information.
157 Nowadays next() returns self.__dict__ which contains everything we were able to find in the fits file.
158
159 *use_CalFactFits* : formerly the DRS amplitude calibration was
160 implemented in python. But for performance reasons this was now moved into
161 a C++ class called CalFactFits. For test purposes, this option can be set to
162 False, but this is not really maintained anymore. If DRS the DRS calibration algorithm is
163 beeing updated in C++ it may not be updated in the python implementation.
164
165 *do_calibration* : In case *use_CalFactFits* is False, one may choose
166 not to calibrate the data at all, thus safe quite some time.
167 This is imho only needed in case one is interesting in learning something about the
168 calibration algorithm itself.
169
170 *user_action_calib* : callback function, intended for tests of the DRS calibration algorithm.
171 but since this is not done in the Python regime anymore, this function is never called.
172 (depending on *use_CalFactFits* of course)
173 """
174 self.__module__='pyfact'
175 # manual implementation of default value, but I need to find out
176 # if the user of this class is aware of the new option
177 if return_dict == False:
178 print 'DEPRECATION WARNING:'
179 print 'you are using RawData in a way, which is nor supported anymore.'
180 print ' Please set: return_dict = True, in the __init__ call'
181 self.return_dict = return_dict
182 self.use_CalFactFits = use_CalFactFits
183
184 self.do_calibration = do_calibration
185
186 self.data_file_name = data_file_name
187 self.calib_file_name = calib_file_name
188 self.baseline_file_name = baseline_file_name
189
190 self.user_action_calib = user_action_calib
191
192 # baseline correction: True / False
193 if len(baseline_file_name) == 0:
194 self.correct_baseline = False
195 else:
196 self.correct_baseline = True
197
198
199 # access data file
200 if use_CalFactFits:
201 try:
202 data_file = CalFactFits(data_file_name, calib_file_name)
203 except IOError:
204 print 'problem accessing data file: ', data_file_name
205 raise # stop ! no data
206
207 #: either CalFactFits object or FactFits object, depending on *use_CalFactFits*
208 self.data_file = data_file
209 #: 1440x300 nparray containing the event data. pixel sorted according to CHID
210 self.data = np.empty( data_file.npix * data_file.nroi, np.float64)
211 data_file.SetNpcaldataPtr(self.data)
212 self.data = self.data.reshape( data_file.npix, data_file.nroi )
213 #: copy of data. here for historical reasons
214 self.acal_data = self.data
215 #: region of interest. (number of DRS slices read).
216 # for FACT data mostly 300. for special runs sometimes 1024.
217 self.nroi = data_file.nroi
218 #: number of Pixel in FACT. should be 1440
219 self.npix = data_file.npix
220 #: the total number of events in the data_file
221 self.nevents = data_file.nevents
222
223 # Data per event
224 #: starting at 1
225 self.event_id = None
226
227 #: data=4 ; the rest I don't know by heart .. should be documented here :-)
228 self.trigger_type = None
229 #self.start_cells = None
230 #self.board_times = None
231 #: slice where drs readout started for all DRS chips (160) .. but enlarged to the size of 1440 pixel. thus there are always 9 equal numbers inside.
232 self.start_cells = np.zeros( self.npix, np.int16 )
233 #: each FAD has an onboard clock running from startup time. Currently I don't know the time unit. However this is an array of 40 times, since we have 40 boards.
234 self.board_times = np.zeros( 40, np.int32 )
235 self._unixtime_tuple = np.zeros( 2, np.int32 )
236 self.unixtime = None
237
238 # data_file is a CalFactFits object
239 # data_file.datafile is one of the two FactFits objects hold by a CalFactFits.
240 # sorry for the strange naming ..
241 data_file.datafile.SetPtrAddress('StartCellData', self.start_cells)
242 data_file.datafile.SetPtrAddress('BoardTime', self.board_times)
243 data_file.datafile.SetPtrAddress('UnixTimeUTC', self._unixtime_tuple)
244
245
246 else:
247 try:
248 data_file = factfits(self.data_file_name)
249 except IOError:
250 print 'problem accessing data file: ', data_file_name
251 raise # stop ! no data
252
253 self.data_file = data_file
254
255 # get basic information about the data file
256 self.nroi = data_file.GetUInt('NROI')
257 self.npix = data_file.GetUInt('NPIX')
258 self.nevents = data_file.GetNumRows()
259
260 # allocate the data memories
261 self.event_id = c_ulong()
262 self.trigger_type = c_ushort()
263 self.data = np.zeros( self.npix * self.nroi, np.int16 ).reshape(self.npix ,self.nroi)
264 self.start_cells = np.zeros( self.npix, np.int16 )
265 self.board_times = np.zeros( 40, np.int32 )
266 self._unixtime_tuple = np.zeros(2, np.int32 )
267
268 # set the pointers to the data++
269 data_file.SetPtrAddress('EventNum', self.event_id)
270 data_file.SetPtrAddress('TriggerType', self.trigger_type)
271 data_file.SetPtrAddress('StartCellData', self.start_cells)
272 data_file.SetPtrAddress('Data', self.data)
273 data_file.SetPtrAddress('BoardTime', self.board_times)
274 data_file.SetPtrAddress('UnixTimeUTC', self._unixtime_tuple)
275
276 # open the calibration file
277 try:
278 calib_file = factfits(self.calib_file_name)
279 except IOError:
280 print 'problem accessing calibration file: ', calib_file_name
281 raise
282 #: drs calibration file
283 self.calib_file = calib_file
284
285 baseline_mean = calib_file.GetN('BaselineMean')
286 gain_mean = calib_file.GetN('GainMean')
287 trigger_offset_mean = calib_file.GetN('TriggerOffsetMean')
288
289 self.Nblm = baseline_mean / self.npix
290 self.Ngm = gain_mean / self.npix
291 self.Ntom = trigger_offset_mean / self.npix
292
293 self.blm = np.zeros(baseline_mean, np.float32).reshape(self.npix , self.Nblm)
294 self.gm = np.zeros(gain_mean, np.float32).reshape(self.npix , self.Ngm)
295 self.tom = np.zeros(trigger_offset_mean, np.float32).reshape(self.npix , self.Ntom)
296
297 calib_file.SetPtrAddress('BaselineMean', self.blm)
298 calib_file.SetPtrAddress('GainMean', self.gm)
299 calib_file.SetPtrAddress('TriggerOffsetMean', self.tom)
300 calib_file.GetRow(0)
301
302 # make calibration constants double, so we never need to roll
303 self.blm = np.hstack((self.blm, self.blm))
304 self.gm = np.hstack((self.gm, self.gm))
305 self.tom = np.hstack((self.tom, self.tom))
306
307 self.v_bsl = np.zeros(self.npix) # array of baseline values (all ZERO)
308
309 def __iter__(self):
310 """ iterator """
311 return self
312
313 def next(self):
314 """ used by __iter__
315
316 returns self.__dict__
317 """
318 if self.use_CalFactFits:
319 if self.data_file.GetCalEvent() == False:
320 raise StopIteration
321 else:
322 self.event_id = self.data_file.event_id
323 self.trigger_type = self.data_file.event_triggertype
324 #self.start_cells = self.data_file.event_offset
325 #self.board_times = self.data_file.event_boardtimes
326 #self.acal_data = self.data.copy().reshape(self.data_file.npix, self.data_file.nroi)
327
328 self.unixtime = self._unixtime_tuple[0] + self._unixtime_tuple[1]/1.e6
329
330 else:
331 if self.data_file.GetNextRow() == False:
332 raise StopIteration
333 else:
334 if self.do_calibration == True:
335 self.calibrate_drs_amplitude()
336
337 #print 'nevents = ', self.nevents, 'event_id = ', self.event_id.value
338 if self.return_dict:
339 return self.__dict__
340 else:
341 return self.acal_data, self.start_cells, self.trigger_type.value
342
343 def next_event(self):
344 """ ---- DEPRICATED ----
345
346 load the next event from disk and calibrate it
347 """
348 if self.use_CalFactFits:
349 self.data_file.GetCalEvent()
350 else:
351 self.data_file.GetNextRow()
352 self.calibrate_drs_amplitude()
353
354 def calibrate_drs_amplitude(self):
355 """ --- DEPRICATED ---
356
357 since the DRS calibration is done by the C++ class CalFactFits
358
359 perform the drs amplitude calibration of the event data
360 """
361 # shortcuts
362 blm = self.blm
363 gm = self.gm
364 tom = self.tom
365
366 to_mV = 2000./4096.
367 #: 2D array with amplitude calibrated dat in mV
368 acal_data = self.data * to_mV # convert ADC counts to mV
369
370
371 for pixel in range( self.npix ):
372 #shortcuts
373 sc = self.start_cells[pixel]
374 roi = self.nroi
375 # rotate the pixel baseline mean to the Data startCell
376 acal_data[pixel,:] -= blm[pixel,sc:sc+roi]
377 # the 'trigger offset mean' does not need to be rolled
378 # on the contrary, it seems there is an offset in the DRS data,
379 # which is related to its distance to the startCell, not to its
380 # distance to the beginning of the physical pipeline in the DRS chip
381 acal_data[pixel,:] -= tom[pixel,0:roi]
382 # rotate the pixel gain mean to the Data startCell
383 acal_data[pixel,:] /= gm[pixel,sc:sc+roi]
384
385
386 self.acal_data = acal_data * 1907.35
387
388 self.user_action_calib( self.acal_data,
389 np.reshape(self.data, (self.npix, self.nroi) ), blm, tom, gm, self.start_cells, self.nroi)
390
391
392 def baseline_read_values(self, file, bsl_hist='bsl_sum/hplt_mean'):
393 """
394 open ROOT file with baseline histogram and read baseline values
395
396 *file* : name of the root file
397
398 *bsl_hist* : path to the histogram containing the basline values
399 """
400
401 try:
402 f = TFile(file)
403 except:
404 print 'Baseline data file could not be read: ', file
405 return
406
407 h = f.Get(bsl_hist)
408
409 for i in range(self.npix):
410 self.v_bsl[i] = h.GetBinContent(i+1)
411
412 f.Close()
413
414 def baseline_correct(self):
415 """ subtract baseline from the data
416
417 DN 08.06.2011: I didn't use this function at all so far... don't know how well it works.
418 """
419
420 for pixel in range(self.npix):
421 self.acal_data[pixel,:] -= self.v_bsl[pixel]
422
423 def info(self):
424 """ print run information
425
426 not very well implemented ... we need more info here.
427 """
428 print 'data file: ', self.data_file_name
429 print 'calib file: ', self.calib_file_name
430 print '... we need more information printed here ... '
431
432# -----------------------------------------------------------------------------
433class RawDataFake( object ):
434 """ raw data FAKE access similar to real RawData access
435
436 DO NOT USE ... its not working
437 """
438
439
440 def __init__(self, data_file_name, calib_file_name,
441 user_action_calib=lambda acal_data, data, blm, tom, gm, scells, nroi: None,
442 baseline_file_name=''):
443 self.__module__='pyfact'
444
445 self.nroi = 300
446 self.npix = 9
447 self.nevents = 1000
448
449 self.simulator = None
450
451 self.time = np.ones(1024) * 0.5
452
453
454 self.event_id = c_ulong(0)
455 self.trigger_type = c_ushort(4)
456 self.data = np.zeros( self.npix * self.nroi, np.int16 ).reshape(self.npix ,self.nroi)
457 self.start_cells = np.zeros( self.npix, np.int16 )
458 self.board_times = np.zeros( 40, np.int32 )
459 def __iter__(self):
460 """ iterator """
461 return self
462
463 def next(self):
464 """ used by __iter__ """
465 self.event_id = c_ulong(self.event_id.value + 1)
466 self.board_times = self.board_times + 42
467
468 if self.event_id.value >= self.nevents:
469 raise StopIteration
470 else:
471 self._make_event_data()
472
473 return self.__dict__
474
475 def _make_event_data(self):
476 sample_times = self.time.cumsum() - time[0]
477
478 # random start cell
479 self.start_cells = np.ones( self.npix, np.int16 ) * np.random.randint(0,1024)
480
481 starttime = self.start_cells[0]
482
483 signal = self._std_sinus_simu(sample_times, starttime)
484
485 data = np.vstack( (signal,signal) )
486 for i in range(8):
487 data = np.vstack( (data,signal) )
488
489 self.data = data
490
491 def _std_sinus_simu(self, times, starttime):
492 period = 10 # in ns
493
494 # give a jitter on starttime
495 starttime = np.random.normal(startime, 0.05)
496
497 phase = 0.0
498 signal = 10 * np.sin(times * 2*np.pi/period + starttime + phase)
499
500 # add some noise
501 noise = np.random.normal(0.0, 0.5, signal.shape)
502 signal += noise
503 return signal
504
505 def info(self):
506 """ print run information
507
508 """
509
510 print 'data file: ', data_file_name
511 print 'calib file: ', calib_file_name
512 print 'calibration file'
513 print 'N baseline_mean: ', self.Nblm
514 print 'N gain mean: ', self.Ngm
515 print 'N TriggeroffsetMean: ', self.Ntom
516
517# -----------------------------------------------------------------------------
518import ctypes
519
520class SlowData( object ):
521 """ -Fact SlowData File-
522
523 A Python wrapper for the fits-class implemented in factfits.h
524 provides easy access to the fits file meta data.
525
526 * dictionary of file metadata - self.meta
527 * dict of table metadata - self.columns
528 * variable table column access, thus possibly increased speed while looping
529 """
530 def __del__(self):
531 del self.f
532
533 def __init__(self, path):
534 """ creates meta and columns dictionaries
535 """
536 import os
537
538 if not os.path.exists(path):
539 raise IOError(path+' was not found')
540 self.path = path
541 self.__module__ = 'pyfact'
542 try:
543 self.f = factfits(path)
544 except IOError:
545 print 'problem accessing data file: ', data_file_name
546 raise # stop ! no data
547
548 self.meta = self._make_meta_dict()
549 self.columns = self._make_columns_dict()
550
551 self._treat_meta_dict()
552
553
554 # list of columns, which are already registered
555 # see method register()
556 self._registered_cols = []
557 # dict of column data, this is used, in order to be able to remove
558 # the ctypes of
559 self._table_cols = {}
560
561 # I need to count the rows, since the normal loop mechanism seems not to work.
562 self._current_row = 0
563
564 self.stacked_cols = {}
565
566 def _make_meta_dict__old(self):
567 """ This method retrieves meta information about the fits file and
568 stores this information in a dict
569 return: dict
570 key: string - all capital letters
571 value: tuple( numerical value, string comment)
572 """
573 # abbreviation
574 f = self.f
575
576 # intermediate variables for file metadata dict generation
577
578 keys=f.GetPy_KeyKeys()
579 values=f.GetPy_KeyValues()
580 comments=f.GetPy_KeyComments()
581 types=f.GetPy_KeyTypes()
582
583 if len(keys) != len(values):
584 raise TypeError('len(keys)',len(keys),' != len(values)', len(values))
585 if len(keys) != len(types):
586 raise TypeError('len(keys)',len(keys),' != len(types)', len(types))
587 if len(keys) != len(comments):
588 raise TypeError('len(keys)',len(keys),' != len(comments)', len(comments))
589
590 meta_dict = {}
591 for i in range(len(keys)):
592 type = types[i]
593 if type == 'I':
594 value = int(values[i])
595 elif type == 'F':
596 value = float(values[i])
597 elif type == 'B':
598 if values[i] == 'T':
599 value = True
600 elif values[i] == 'F':
601 value = False
602 else:
603 raise TypeError("meta-type is 'B', but meta-value is neither 'T' nor 'F'. meta-value:",values[i])
604 elif type == 'T':
605 value = values[i]
606 else:
607 raise TypeError("unknown meta-type: known meta types are: I,F,B and T. meta-type:",type)
608 meta_dict[keys[i]]=(value, comments[i])
609 return meta_dict
610
611 def _make_meta_dict(self):
612 meta_dict = {}
613 for key,entry in self.f.GetKeys():
614 type = entry.type
615 fitsString = entry.fitsString # the original 80-char line from the FITS header
616 comment = entry.comment
617 value = entry.value
618
619 if type == 'I':
620 value = int(value)
621 elif type == 'F':
622 value = float(value)
623 elif type == 'B':
624 if value == 'T':
625 value = True
626 elif value == 'F':
627 value = False
628 else:
629 raise TypeError("meta-type is 'B', but meta-value is neither 'T' nor 'F'. meta-value:",value)
630 elif type == 'T':
631 value = value
632 else:
633 raise TypeError("unknown meta-type: known meta types are: I,F,B and T. meta-type:",type)
634 meta_dict[key]=(value, comment)
635 return meta_dict
636
637
638
639 def _make_columns_dict(self):
640 """ This method retrieves information about the columns
641 stored inside the fits files internal binary table.
642 returns: dict
643 key: string column name -- all capital letters
644 values: tuple(
645 number of elements in table field - integer
646 size of element in bytes -- this is not really interesting for any user
647 might be ommited in future versions
648 type - a single character code -- should be translated into
649 a comrehensible word
650 unit - string like 'mV' or 'ADC count'
651 """
652 ## abbreviation
653 #f = self.f
654 #
655 ## intermediate variables for file table-metadata dict generation
656 #keys=f.GetPy_ColumnKeys()
657 ##offsets=self.GetPy_ColumnOffsets() #not needed on python level...
658 #nums=f.GetPy_ColumnNums()
659 #sizes=f.GetPy_ColumnSizes()
660 #types=f.GetPy_ColumnTypes()
661 #units=f.GetPy_ColumnUnits()
662
663 ## zip the values
664 #values = zip(nums,sizes,types,units)
665 ## create the columns dictionary
666 #columns = dict(zip(keys ,values))
667
668
669 columns = {}
670 for key,col in self.f.GetColumns():
671 columns[key]=( col.num, col.size, col.type, col.unit)
672 return columns
673
674 def stack(self, on=True):
675 self.next()
676 for col in self._registered_cols:
677 if isinstance( self.dict[col], type(np.array('')) ):
678 self.stacked_cols[col] = self.dict[col]
679 else:
680# elif isinstance(self.dict[col], ctypes._SimpleCData):
681 self.stacked_cols[col] = np.array(self.dict[col])
682# else:
683# raise TypeError("I don't know how to stack "+col+". It is of type: "+str(type(self.dict[col])))
684
685 def register(self, col_name):
686 """ register for a column in the fits file
687
688 after the call, this SlowData object will have a new member variable
689 self.col_name, if col_name is a key in self.colums
690
691 the value will be updated after each call of next(), or while iterating over self.
692 NB: the initial value is zero(s)
693
694 *col_name* : name of a key in self.columns, or 'all' to choose all.
695 """
696 columns = self.columns
697 if col_name.lower() == 'all':
698 for col in columns:
699 self._register(col)
700 else:
701 #check if colname is in columns:
702 if col_name not in columns:
703 error_msg = 'colname:'+ col_name +' is not a column in the binary table.\n'
704 error_msg+= 'possible colnames are\n'
705 for key in columns:
706 error_msg += key+' '
707 raise KeyError(error_msg)
708 else:
709 self._register(col_name)
710
711 # 'private' method, do not use
712 def _register( self, colname):
713
714 columns = self.columns
715 f = self.f
716 local = None
717
718 number_of_elements = int(columns[colname][0])
719 size_of_elements_in_bytes = int(columns[colname][1])
720 ctypecode_of_elements = columns[colname][2]
721 physical_unit_of_elements = columns[colname][3]
722
723 # snippet from the C++ source code, or header file to be precise:
724 #case 'L': gLog << "bool(8)"; break;
725 #case 'B': gLog << "byte(8)"; break;
726 #case 'I': gLog << "short(16)"; break;
727 #case 'J': gLog << "int(32)"; break;
728 #case 'K': gLog << "int(64)"; break;
729 #case 'E': gLog << "float(32)"; break;
730 #case 'D': gLog << "double(64)"; break;
731
732
733
734 # the fields inside the columns can either contain single numbers,
735 # or whole arrays of numbers as well.
736 # we treat single elements differently...
737 if number_of_elements == 0:
738 return
739 if number_of_elements == 1:
740 # allocate some memory for a single number according to its type
741 if ctypecode_of_elements == 'J': # J is for a 4byte int, i.e. an unsigned long
742 local = ctypes.c_ulong()
743 un_c_type = long
744 elif ctypecode_of_elements == 'I': # I is for a 2byte int, i.e. an unsinged int
745 local = ctypes.c_ushort()
746 un_c_type = int
747 elif ctypecode_of_elements == 'B': # B is for a byte
748 local = ctypes.c_ubyte()
749 un_c_type = int
750 elif ctypecode_of_elements == 'D':
751 local = ctypes.c_double()
752 un_c_type = float
753 elif ctypecode_of_elements == 'E':
754 local = ctypes.c_float()
755 un_c_type = float
756 elif ctypecode_of_elements == 'A':
757 local = ctypes.c_uchar()
758 un_c_type = chr
759 elif ctypecode_of_elements == 'K':
760 local = ctypes.c_ulonglong()
761 un_c_type = long
762 else:
763 raise TypeError('unknown ctypecode_of_elements:',ctypecode_of_elements)
764 else:
765 if ctypecode_of_elements == 'B': # B is for a byte
766 nptype = np.int8
767 elif ctypecode_of_elements == 'A': # A is for a char .. but I don't know how to handle it
768 nptype = np.int8
769 elif ctypecode_of_elements == 'I': # I is for a 2byte int
770 nptype = np.int16
771 elif ctypecode_of_elements == 'J': # J is for a 4byte int
772 nptype = np.int32
773 elif ctypecode_of_elements == 'K': # B is for a byte
774 nptype = np.int64
775 elif ctypecode_of_elements == 'E': # B is for a byte
776 nptype = np.float32
777 elif ctypecode_of_elements == 'D': # B is for a byte
778 nptype = np.float64
779 else:
780 raise TypeError('unknown ctypecode_of_elements:',ctypecode_of_elements)
781 local = np.zeros( number_of_elements, nptype)
782
783 # Set the Pointer Address
784 try:
785 f.SetPtrAddress(colname, local)
786 except TypeError:
787 print 'something was wrong with SetPtrAddress()'
788 print 'Type of colname', type(colname)
789 print 'colname:', colname
790 print 'Type of local', type(local)
791 print 'length of local', len(local)
792 print 'local should be alle zeros, since "local = np.zeros( number_of_elements, nptype)" '
793 raise
794
795 self._table_cols[colname] = local
796 if number_of_elements > 1:
797 self.__dict__[colname] = local
798 self.dict[colname] = local
799 else:
800 # remove any traces of ctypes:
801 self.__dict__[colname] = local.value
802 self.dict[colname] = local.value
803 self._registered_cols.append(colname)
804
805
806 def _treat_meta_dict(self):
807 """make 'interesting' meta information available like normal members.
808 non interesting are:
809 TFORM, TUNIT, and TTYPE
810 since these are available via the columns dict.
811 """
812
813 self.number_of_rows = self.meta['NAXIS2'][0]
814 self.number_of_columns = self.meta['TFIELDS'][0]
815
816 # there are some information in the meta dict, which are alsways there:
817 # there are regarded as not interesting:
818 uninteresting_meta = {}
819 uninteresting_meta['arraylike'] = {}
820 uninteresting = ['NAXIS', 'NAXIS1', 'NAXIS2',
821 'TFIELDS',
822 'XTENSION','EXTNAME','EXTREL',
823 'BITPIX', 'PCOUNT', 'GCOUNT',
824 'ORIGIN',
825 'PACKAGE', 'COMPILED', 'CREATOR',
826 'TELESCOP','TIMESYS','TIMEUNIT','VERSION']
827 for key in uninteresting:
828 if key in self.meta:
829 uninteresting_meta[key]=self.meta[key]
830 del self.meta[key]
831
832 # the table meta data contains
833
834
835 # shortcut to access the meta dict. But this needs to
836 # be cleaned up quickly!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
837 meta = self.meta
838
839 # loop over keys:
840 # * try to find array-like keys
841 arraylike = {}
842 singlelike = []
843 for key in self.meta:
844 stripped = key.rstrip('1234567890')
845 if stripped == key:
846 singlelike.append(key)
847 else:
848 if stripped not in arraylike:
849 arraylike[stripped] = 0
850 else:
851 arraylike[stripped] += 1
852 newmeta = {}
853 for key in singlelike:
854 newmeta[key.lower()] = meta[key]
855 for key in arraylike:
856 uninteresting_meta['arraylike'][key.lower()] = []
857 for i in range(arraylike[key]+1):
858 if key+str(i) in meta:
859 uninteresting_meta['arraylike'][key.lower()].append(meta[key+str(i)])
860 self.ui_meta = uninteresting_meta
861 # make newmeta self
862 for key in newmeta:
863 self.__dict__[key]=newmeta[key]
864
865 dict = self.__dict__.copy()
866 del dict['meta']
867 del dict['ui_meta']
868 self.dict = dict
869
870 def __iter__(self):
871 """ iterator """
872 return self
873
874 def next(self):
875 """ use to iterate over the file
876
877 do not forget to call register() before iterating over the file
878 call show() in order to find out, what parameters register() accepts.
879 or just call register('all') in case you are unsure.
880
881 returns self
882 """
883 # abbreviaition
884 f = self.f
885
886 # Here one might check, if looping makes any sense, and if not
887 # one could stop looping or so...
888 # like this:
889 #
890 # if len(self._registered_cols) == 0:
891 # print 'warning: looping without any registered columns'
892 if self._current_row < self.number_of_rows:
893 if f.GetNextRow() == False:
894 raise StopIteration
895 for col in self._registered_cols:
896 if isinstance(self._table_cols[col], ctypes._SimpleCData):
897 self.__dict__[col] = self._table_cols[col].value
898 self.dict[col] = self._table_cols[col].value
899
900 for col in self.stacked_cols:
901 if isinstance(self.dict[col], type(np.array(''))):
902 self.stacked_cols[col] = np.vstack( (self.stacked_cols[col],self.dict[col]) )
903 else:
904 self.stacked_cols[col] = np.vstack( (self.stacked_cols[col],np.array(self.dict[col])) )
905 self._current_row += 1
906 else:
907 raise StopIteration
908 return self
909
910 def show(self):
911 """
912 """
913 pprint.pprint(self.dict)
914
915
916
917
918class fnames( object ):
919 """ organize file names of a FACT data run
920
921 """
922
923 def __init__(self, specifier = ['012', '023', '2011', '11', '24'],
924 rpath = '/scratch_nfs/res/bsl/',
925 zipped = True):
926 """
927 specifier : list of strings defined as:
928 [ 'DRS calibration file', 'Data file', 'YYYY', 'MM', 'DD']
929
930 rpath : directory path for the results; YYYYMMDD will be appended to rpath
931 zipped : use zipped (True) or unzipped (Data)
932
933 """
934
935 self.specifier = specifier
936 self.rpath = rpath
937 self.zipped = zipped
938
939 self.make( self.specifier, self.rpath, self.zipped )
940
941
942 def make( self, specifier, rpath, zipped ):
943 """ create (make) the filenames
944
945 names : dictionary of filenames, tags { 'data', 'drscal', 'results' }
946 data : name of the data file
947 drscal : name of the drs calibration file
948 results : radikal of file name(s) for results (to be completed by suffixes)
949 """
950
951 self.specifier = specifier
952
953 if zipped:
954 dpath = '/data00/fact-construction/raw/'
955 ext = '.fits.gz'
956 else:
957 dpath = '/data03/fact-construction/raw/'
958 ext = '.fits'
959
960 year = specifier[2]
961 month = specifier[3]
962 day = specifier[4]
963
964 yyyymmdd = year + month + day
965 dfile = specifier[1]
966 cfile = specifier[0]
967
968 rpath = rpath + yyyymmdd + '/'
969 self.rpath = rpath
970 self.names = {}
971
972 tmp = dpath + year + '/' + month + '/' + day + '/' + yyyymmdd + '_'
973 self.names['data'] = tmp + dfile + ext
974 self.names['drscal'] = tmp + cfile + '.drs' + ext
975 self.names['results'] = rpath + yyyymmdd + '_' + dfile + '_' + cfile
976
977 self.data = self.names['data']
978 self.drscal = self.names['drscal']
979 self.results = self.names['results']
980
981 def info( self ):
982 """ print complete filenames
983
984 """
985
986 print 'file names:'
987 print 'data: ', self.names['data']
988 print 'drs-cal: ', self.names['drscal']
989 print 'results: ', self.names['results']
990
991# end of class definition: fnames( object )
992
993def _test_SlowData( filename ):
994 print '-'*70
995 print "opened :", filename, " as 'file'"
996 print
997 print '-'*70
998 print 'type file.show() to look at its contents'
999 print "type file.register( columnname ) or file.register('all') in order to register columns"
1000 print
1001 print " due column-registration you declare, that you would like to retrieve the contents of one of the columns"
1002 print " after column-registration, the 'file' has new member variables, they are named like the columns"
1003 print " PLEASE NOTE: immediatly after registration, the members exist, but they are empty."
1004 print " the values are assigned only, when you call file.next() or when you loop over the 'file'"
1005 print
1006 print "in order to loop over it, just go like this:"
1007 print "for row in file:"
1008 print " print row.columnname_one, row.columnname_two"
1009 print
1010 print ""
1011 print '-'*70
1012
1013
1014
1015def _test_iter( nevents ):
1016 """ test for function __iter__ """
1017
1018 data_file_name = '/fact/raw/2011/11/24/20111124_117.fits.gz'
1019 calib_file_name = '/fact/raw/2011/11/24/20111124_114.drs.fits.gz'
1020 print 'the files for this test are:'
1021 print 'data file:', data_file_name
1022 print 'calib file:', calib_file_name
1023 run = RawData( data_file_name, calib_file_name , return_dict=True)
1024
1025 for event in run:
1026 print 'ev ', event['event_id'], 'data[0,0] = ', event['acal_data'][0,0], 'start_cell[0] = ', event['start_cells'][0], 'trigger type = ', event['trigger_type']
1027 if run.event_id == nevents:
1028 break
1029
1030if __name__ == '__main__':
1031 """ tests """
1032
1033 f = fits(sys.argv[1])
1034 test_m1 = ROOT.std.map(str,ROOT.fits.Entry)()
1035 test_m2 = ROOT.std.map(str,ROOT.fits.Table.Column)()
1036 print "len(test_m1)", len(test_m1)
1037 print "len(test_m2)", len(test_m2)
1038
1039 for k1 in f.GetKeys():
1040 pass
1041 print k1
1042 for k2 in f.GetColumns():
1043 pass
1044 print k2
1045
1046 sd = SlowData(sys.argv[1])
Note: See TracBrowser for help on using the repository browser.