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

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