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

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