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

Last change on this file since 14422 was 14422, checked in by neise, 12 years ago
added destructor to SlowData and checked if constructor file path exists
  • Property svn:executable set to *
File size: 35.3 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 columns = self.columns
625 local = None
626
627 number_of_elements = int(columns[colname][0])
628 size_of_elements_in_bytes = int(columns[colname][1])
629 ctypecode_of_elements = columns[colname][2]
630 physical_unit_of_elements = columns[colname][3]
631
632 # snippet from the C++ source code, or header file to be precise:
633 #case 'L': gLog << "bool(8)"; break;
634 #case 'B': gLog << "byte(8)"; break;
635 #case 'I': gLog << "short(16)"; break;
636 #case 'J': gLog << "int(32)"; break;
637 #case 'K': gLog << "int(64)"; break;
638 #case 'E': gLog << "float(32)"; break;
639 #case 'D': gLog << "double(64)"; break;
640
641
642
643 # the fields inside the columns can either contain single numbers,
644 # or whole arrays of numbers as well.
645 # we treat single elements differently...
646 if number_of_elements == 1:
647 # allocate some memory for a single number according to its type
648 if ctypecode_of_elements == 'J': # J is for a 4byte int, i.e. an unsigned long
649 local = ctypes.c_ulong()
650 un_c_type = long
651 elif ctypecode_of_elements == 'I': # I is for a 2byte int, i.e. an unsinged int
652 local = ctypes.c_ushort()
653 un_c_type = int
654 elif ctypecode_of_elements == 'B': # B is for a byte
655 local = ctypes.c_ubyte()
656 un_c_type = int
657 elif ctypecode_of_elements == 'D':
658 local = ctypes.c_double()
659 un_c_type = float
660 elif ctypecode_of_elements == 'E':
661 local = ctypes.c_float()
662 un_c_type = float
663 elif ctypecode_of_elements == 'A':
664 local = ctypes.c_uchar()
665 un_c_type = chr
666 elif ctypecode_of_elements == 'K':
667 local = ctypes.c_ulonglong()
668 un_c_type = long
669 else:
670 raise TypeError('unknown ctypecode_of_elements:',ctypecode_of_elements)
671 else:
672 if ctypecode_of_elements == 'B': # B is for a byte
673 nptype = np.int8
674 elif ctypecode_of_elements == 'A': # A is for a char .. but I don't know how to handle it
675 nptype = np.int8
676 elif ctypecode_of_elements == 'I': # I is for a 2byte int
677 nptype = np.int16
678 elif ctypecode_of_elements == 'J': # J is for a 4byte int
679 nptype = np.int32
680 elif ctypecode_of_elements == 'K': # B is for a byte
681 nptype = np.int64
682 elif ctypecode_of_elements == 'E': # B is for a byte
683 nptype = np.float32
684 elif ctypecode_of_elements == 'D': # B is for a byte
685 nptype = np.float64
686 else:
687 raise TypeError('unknown ctypecode_of_elements:',ctypecode_of_elements)
688 local = np.zeros( number_of_elements, nptype)
689
690 # Set the Pointer Address
691 f.SetPtrAddress(colname, local)
692 self._table_cols[colname] = local
693 if number_of_elements > 1:
694 self.__dict__[colname] = local
695 self.dict[colname] = local
696 else:
697 # remove any traces of ctypes:
698 self.__dict__[colname] = local.value
699 self.dict[colname] = local.value
700 self._registered_cols.append(colname)
701
702
703 def _treat_meta_dict(self):
704 """make 'interesting' meta information available like normal members.
705 non interesting are:
706 TFORM, TUNIT, and TTYPE
707 since these are available via the columns dict.
708 """
709
710 self.number_of_rows = self.meta['NAXIS2'][0]
711 self.number_of_columns = self.meta['TFIELDS'][0]
712
713 # there are some information in the meta dict, which are alsways there:
714 # there are regarded as not interesting:
715 uninteresting_meta = {}
716 uninteresting_meta['arraylike'] = {}
717 uninteresting = ['NAXIS', 'NAXIS1', 'NAXIS2',
718 'TFIELDS',
719 'XTENSION','EXTNAME','EXTREL',
720 'BITPIX', 'PCOUNT', 'GCOUNT',
721 'ORIGIN',
722 'PACKAGE', 'COMPILED', 'CREATOR',
723 'TELESCOP','TIMESYS','TIMEUNIT','VERSION']
724 for key in uninteresting:
725 if key in self.meta:
726 uninteresting_meta[key]=self.meta[key]
727 del self.meta[key]
728
729 # the table meta data contains
730
731
732 # shortcut to access the meta dict. But this needs to
733 # be cleaned up quickly!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
734 meta = self.meta
735
736 # loop over keys:
737 # * try to find array-like keys
738 arraylike = {}
739 singlelike = []
740 for key in self.meta:
741 stripped = key.rstrip('1234567890')
742 if stripped == key:
743 singlelike.append(key)
744 else:
745 if stripped not in arraylike:
746 arraylike[stripped] = 0
747 else:
748 arraylike[stripped] += 1
749 newmeta = {}
750 for key in singlelike:
751 newmeta[key.lower()] = meta[key]
752 for key in arraylike:
753 uninteresting_meta['arraylike'][key.lower()] = []
754 for i in range(arraylike[key]+1):
755 if key+str(i) in meta:
756 uninteresting_meta['arraylike'][key.lower()].append(meta[key+str(i)])
757 self.ui_meta = uninteresting_meta
758 # make newmeta self
759 for key in newmeta:
760 self.__dict__[key]=newmeta[key]
761
762 dict = self.__dict__.copy()
763 del dict['meta']
764 del dict['ui_meta']
765 self.dict = dict
766
767 def __iter__(self):
768 """ iterator """
769 return self
770
771 def next(self):
772 """ use to iterate over the file
773
774 do not forget to call register() before iterating over the file
775 call show() in order to find out, what parameters register() accepts.
776 or just call register('all') in case you are unsure.
777
778 returns self
779 """
780 # abbreviaition
781 f = self.f
782
783 # Here one might check, if looping makes any sense, and if not
784 # one could stop looping or so...
785 # like this:
786 #
787 # if len(self._registered_cols) == 0:
788 # print 'warning: looping without any registered columns'
789 if self._current_row < self.number_of_rows:
790 if f.GetNextRow() == False:
791 raise StopIteration
792 for col in self._registered_cols:
793 if isinstance(self._table_cols[col], ctypes._SimpleCData):
794 self.__dict__[col] = self._table_cols[col].value
795 self.dict[col] = self._table_cols[col].value
796
797 for col in self.stacked_cols:
798 if isinstance(self.dict[col], type(np.array(''))):
799 self.stacked_cols[col] = np.vstack( (self.stacked_cols[col],self.dict[col]) )
800 else:
801 self.stacked_cols[col] = np.vstack( (self.stacked_cols[col],np.array(self.dict[col])) )
802 self._current_row += 1
803 else:
804 raise StopIteration
805 return self
806
807 def show(self):
808 """
809 """
810 pprint.pprint(self.dict)
811
812
813
814
815class fnames( object ):
816 """ organize file names of a FACT data run
817
818 """
819
820 def __init__(self, specifier = ['012', '023', '2011', '11', '24'],
821 rpath = '/scratch_nfs/res/bsl/',
822 zipped = True):
823 """
824 specifier : list of strings defined as:
825 [ 'DRS calibration file', 'Data file', 'YYYY', 'MM', 'DD']
826
827 rpath : directory path for the results; YYYYMMDD will be appended to rpath
828 zipped : use zipped (True) or unzipped (Data)
829
830 """
831
832 self.specifier = specifier
833 self.rpath = rpath
834 self.zipped = zipped
835
836 self.make( self.specifier, self.rpath, self.zipped )
837
838
839 def make( self, specifier, rpath, zipped ):
840 """ create (make) the filenames
841
842 names : dictionary of filenames, tags { 'data', 'drscal', 'results' }
843 data : name of the data file
844 drscal : name of the drs calibration file
845 results : radikal of file name(s) for results (to be completed by suffixes)
846 """
847
848 self.specifier = specifier
849
850 if zipped:
851 dpath = '/data00/fact-construction/raw/'
852 ext = '.fits.gz'
853 else:
854 dpath = '/data03/fact-construction/raw/'
855 ext = '.fits'
856
857 year = specifier[2]
858 month = specifier[3]
859 day = specifier[4]
860
861 yyyymmdd = year + month + day
862 dfile = specifier[1]
863 cfile = specifier[0]
864
865 rpath = rpath + yyyymmdd + '/'
866 self.rpath = rpath
867 self.names = {}
868
869 tmp = dpath + year + '/' + month + '/' + day + '/' + yyyymmdd + '_'
870 self.names['data'] = tmp + dfile + ext
871 self.names['drscal'] = tmp + cfile + '.drs' + ext
872 self.names['results'] = rpath + yyyymmdd + '_' + dfile + '_' + cfile
873
874 self.data = self.names['data']
875 self.drscal = self.names['drscal']
876 self.results = self.names['results']
877
878 def info( self ):
879 """ print complete filenames
880
881 """
882
883 print 'file names:'
884 print 'data: ', self.names['data']
885 print 'drs-cal: ', self.names['drscal']
886 print 'results: ', self.names['results']
887
888# end of class definition: fnames( object )
889
890def _test_SlowData( filename ):
891 print '-'*70
892 print "opened :", filename, " as 'file'"
893 print
894 print '-'*70
895 print 'type file.show() to look at its contents'
896 print "type file.register( columnname ) or file.register('all') in order to register columns"
897 print
898 print " due column-registration you declare, that you would like to retrieve the contents of one of the columns"
899 print " after column-registration, the 'file' has new member variables, they are named like the columns"
900 print " PLEASE NOTE: immediatly after registration, the members exist, but they are empty."
901 print " the values are assigned only, when you call file.next() or when you loop over the 'file'"
902 print
903 print "in order to loop over it, just go like this:"
904 print "for row in file:"
905 print " print row.columnname_one, row.columnname_two"
906 print
907 print ""
908 print '-'*70
909
910
911
912def _test_iter( nevents ):
913 """ test for function __iter__ """
914
915 data_file_name = '/fact/raw/2011/11/24/20111124_117.fits.gz'
916 calib_file_name = '/fact/raw/2011/11/24/20111124_114.drs.fits.gz'
917 print 'the files for this test are:'
918 print 'data file:', data_file_name
919 print 'calib file:', calib_file_name
920# data_file_name = '/home/luster/win7/FACT/data/raw/20120114/20120114_028.fits.gz'
921# calib_file_name = '/home/luster/win7/FACT/data/raw/20120114/20120114_022.drs.fits.gz'
922 run = RawData( data_file_name, calib_file_name , return_dict=True)
923
924 for event in run:
925 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']
926 if run.event_id == nevents:
927 break
928
929if __name__ == '__main__':
930 """ tests """
931 import sys
932 if len(sys.argv) == 1:
933 print 'showing test of iterator of RawData class'
934 print 'in order to test the SlowData classe please use:', sys.argv[0], 'fits-file-name'
935 _test_iter(10)
936
937
938 else:
939 print 'showing test of SlowData class'
940 print 'in case you wanted to test the RawData class, please give no commandline arguments'
941 file = SlowData(sys.argv[1])
942 _test_SlowData(sys.argv[1])
Note: See TracBrowser for help on using the repository browser.