Changeset 13505 for fact


Ignore:
Timestamp:
05/02/12 12:53:13 (13 years ago)
Author:
neise
Message:
added class SlowData. This class was formerly known as FactFits in module factfits.py
File:
1 edited

Legend:

Unmodified
Added
Removed
  • fact/tools/pyscripts/pyfact/pyfact.py

    r13441 r13505  
    66from ctypes import *
    77import numpy as np
     8import pprint # for SlowData
    89from scipy import signal
    910
     
    380381       
    381382# -----------------------------------------------------------------------------
     383
     384class SlowData( fits ):
     385    """ -Fact Fits File-
     386        A Python wrapper for the fits-class implemented in pyfits.h
     387        provides easy access to the fits file meta data.
     388        * dictionary of file metadata - self.meta
     389        * dict of table metadata - self.columns
     390        * variable table column access, thus possibly increased speed while looping
     391    """
     392    def __init__(self, path):
     393        """ creates meta and columns dictionaries
     394        """
     395        self.path = path
     396        try:
     397            fits.__init__(self,path)
     398        except IOError:
     399            print 'problem accessing data file: ', data_file_name
     400            raise  # stop ! no data
     401       
     402        self.meta = self._make_meta_dict()
     403        self.columns = self._make_columns_dict()
     404       
     405        self.treat_meta_dict()
     406       
     407       
     408        # list of columns, which are already registered
     409        # see method register()
     410        self._registered_cols = []
     411        # dict of column data, this is used, in order to be able to remove
     412        # the ctypes of
     413        self._table_cols = {}
     414       
     415        # I need to count the rows, since the normal loop mechanism seems not to work.
     416        self._current_row = 0
     417       
     418        self.stacked_cols = {}
     419
     420    def _make_meta_dict(self):
     421        """ This method retrieves meta information about the fits file and
     422            stores this information in a dict
     423            return: dict
     424                key: string - all capital letters
     425                value: tuple( numerical value, string comment)
     426        """
     427        # intermediate variables for file metadata dict generation
     428        keys=self.GetPy_KeyKeys()
     429        values=self.GetPy_KeyValues()
     430        comments=self.GetPy_KeyComments()
     431        types=self.GetPy_KeyTypes()
     432       
     433        if len(keys) != len(values):
     434            raise TypeError('len(keys)',len(keys),' != len(values)', len(values))
     435        if len(keys) != len(types):
     436            raise TypeError('len(keys)',len(keys),' != len(types)', len(types))
     437        if len(keys) != len(comments):
     438            raise TypeError('len(keys)',len(keys),' != len(comments)', len(comments))
     439       
     440        meta_dict = {}
     441        for i in range(len(keys)):
     442            type = types[i]
     443            if type == 'I':
     444                value = int(values[i])
     445            elif type == 'F':
     446                value = float(values[i])
     447            elif type == 'B':
     448                if values[i] == 'T':
     449                    value = True
     450                elif values[i] == 'F':
     451                    value = False
     452                else:
     453                    raise TypeError("meta-type is 'B', but meta-value is neither 'T' nor 'F'. meta-value:",values[i])
     454            elif type == 'T':
     455                value = values[i]
     456            else:
     457                raise TypeError("unknown meta-type: known meta types are: I,F,B and T. meta-type:",type)
     458            meta_dict[keys[i]]=(value, comments[i])
     459        return meta_dict
     460
     461
     462    def _make_columns_dict(self):
     463        """ This method retrieves information about the columns
     464            stored inside the fits files internal binary table.
     465            returns: dict
     466                key:    string column name -- all capital letters
     467                values: tuple(
     468                    number of elements in table field - integer
     469                    size of element in bytes -- this is not really interesting for any user
     470                            might be ommited in future versions
     471                    type - a single character code -- should be translated into
     472                            a comrehensible word
     473                    unit - string like 'mV' or 'ADC count'
     474        """
     475        # intermediate variables for file table-metadata dict generation
     476        keys=self.GetPy_ColumnKeys()
     477        #offsets=self.GetPy_ColumnOffsets() #not needed on python level...
     478        nums=self.GetPy_ColumnNums()
     479        sizes=self.GetPy_ColumnSizes()
     480        types=self.GetPy_ColumnTypes()
     481        units=self.GetPy_ColumnUnits()
     482   
     483        # zip the values
     484        values = zip(nums,sizes,types,units)
     485        # create the columns dictionary
     486        columns = dict(zip(keys ,values))
     487        return columns
     488
     489    def stack(self, on=True):
     490        self.next()
     491        for col in self._registered_cols:
     492            if isinstance( self.dict[col], type(np.array('')) ):
     493                self.stacked_cols[col] = self.dict[col]
     494            else:
     495#            elif isinstance(self.dict[col], ctypes._SimpleCData):
     496                self.stacked_cols[col] = np.array(self.dict[col])
     497#            else:
     498#                raise TypeError("I don't know how to stack "+col+". It is of type: "+str(type(self.dict[col])))
     499   
     500    def register(self, input_str):
     501        columns = self.columns
     502        if input_str.lower() == 'all':
     503            for col in columns:
     504                self._register(col)
     505        else:
     506            #check if colname is in columns:
     507            if input_str not in columns:
     508                error_msg = 'colname:'+ input_str +' is not a column in the binary table.\n'
     509                error_msg+= 'possible colnames are\n'
     510                for key in columns:
     511                    error_msg += key+'\n'
     512                raise KeyError(error_msg)
     513            else:
     514                self._register(input_str)
     515
     516    # 'private' method, do not use
     517    def _register( self, colname):
     518        columns = self.columns
     519        local = None
     520       
     521        number_of_elements = int(columns[colname][0])
     522        size_of_elements_in_bytes = int(columns[colname][1])
     523        ctypecode_of_elements = columns[colname][2]
     524        physical_unit_of_elements = columns[colname][3]
     525       
     526        # snippet from the C++ source code, or header file to be precise:
     527        #case 'L': gLog << "bool(8)";    break;
     528        #case 'B': gLog << "byte(8)";    break;
     529        #case 'I': gLog << "short(16)";  break;
     530        #case 'J': gLog << "int(32)";    break;
     531        #case 'K': gLog << "int(64)";    break;
     532        #case 'E': gLog << "float(32)";  break;
     533        #case 'D': gLog << "double(64)"; break;
     534
     535       
     536       
     537        # the fields inside the columns can either contain single numbers,
     538        # or whole arrays of numbers as well.
     539        # we treat single elements differently...
     540        if number_of_elements == 1:
     541            # allocate some memory for a single number according to its type
     542            if ctypecode_of_elements == 'J':  # J is for a 4byte int, i.e. an unsigned long
     543                local = ctypes.c_ulong()
     544                un_c_type = long
     545            elif ctypecode_of_elements == 'I':  # I is for a 2byte int, i.e. an unsinged int
     546                local = ctypes.c_ushort()
     547                un_c_type = int
     548            elif ctypecode_of_elements == 'B':  # B is for a byte
     549                local = ctypes.c_ubyte()
     550                un_c_type = int
     551            elif ctypecode_of_elements == 'D':
     552                local = ctypes.c_double()
     553                un_c_type = float
     554            elif ctypecode_of_elements == 'E':
     555                local = ctypes.c_float()
     556                un_c_type = float
     557            elif ctypecode_of_elements == 'A':
     558                local = ctypes.c_uchar()
     559                un_c_type = chr
     560            elif ctypecode_of_elements == 'K':
     561                local = ctypes.c_ulonglong()
     562                un_c_type = long
     563            else:
     564                raise TypeError('unknown ctypecode_of_elements:',ctypecode_of_elements)
     565        else:
     566            if ctypecode_of_elements == 'B':  # B is for a byte
     567                nptype = np.int8
     568            elif ctypecode_of_elements == 'A':  # A is for a char .. but I don't know how to handle it
     569                nptype = np.int8
     570            elif ctypecode_of_elements == 'I':  # I is for a 2byte int
     571                nptype = np.int16
     572            elif ctypecode_of_elements == 'J':  # J is for a 4byte int
     573                nptype = np.int32
     574            elif ctypecode_of_elements == 'K':  # B is for a byte
     575                nptype = np.int64
     576            elif ctypecode_of_elements == 'E':  # B is for a byte
     577                nptype = np.float32
     578            elif ctypecode_of_elements == 'D':  # B is for a byte
     579                nptype = np.float64
     580            else:
     581                raise TypeError('unknown ctypecode_of_elements:',ctypecode_of_elements)
     582            local = np.zeros( number_of_elements, nptype)
     583       
     584        # Set the Pointer Address
     585        self.SetPtrAddress(colname, local)
     586        self._table_cols[colname] = local
     587        if number_of_elements > 1:
     588            self.__dict__[colname] = local
     589            self.dict[colname] = local
     590        else:
     591            # remove any traces of ctypes:
     592            self.__dict__[colname] = local.value
     593            self.dict[colname] = local.value
     594        self._registered_cols.append(colname)
     595       
     596   
     597    def treat_meta_dict(self):
     598        """make 'interesting' meta information available like normal members.
     599            non interesting are:
     600            TFORM, TUNIT, and TTYPE
     601            since these are available via the columns dict.
     602        """
     603       
     604        self.number_of_rows = self.meta['NAXIS2'][0]
     605        self.number_of_columns = self.meta['TFIELDS'][0]
     606
     607        # there are some information in the meta dict, which are alsways there:
     608        # there are regarded as not interesting:
     609        uninteresting_meta = {}
     610        uninteresting_meta['arraylike'] = {}
     611        uninteresting = ['NAXIS', 'NAXIS1', 'NAXIS2',
     612                        'TFIELDS',
     613                        'XTENSION','EXTNAME','EXTREL',
     614                        'BITPIX', 'PCOUNT', 'GCOUNT',
     615                        'ORIGIN',
     616                        'PACKAGE', 'COMPILED', 'CREATOR',
     617                        'TELESCOP','TIMESYS','TIMEUNIT','VERSION']
     618        for key in uninteresting:
     619            if key in self.meta:
     620                uninteresting_meta[key]=self.meta[key]
     621                del self.meta[key]
     622       
     623        # the table meta data contains
     624       
     625       
     626        # shortcut to access the meta dict. But this needs to
     627        # be cleaned up quickly!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     628        meta = self.meta
     629       
     630        # loop over keys:
     631        #   * try to find array-like keys
     632        arraylike = {}
     633        singlelike = []
     634        for key in self.meta:
     635            stripped = key.rstrip('1234567890')
     636            if stripped == key:
     637                singlelike.append(key)
     638            else:
     639                if stripped not in arraylike:
     640                    arraylike[stripped] = 0
     641                else:
     642                    arraylike[stripped] += 1
     643        newmeta = {}
     644        for key in singlelike:
     645            newmeta[key.lower()] = meta[key]
     646        for key in arraylike:
     647            uninteresting_meta['arraylike'][key.lower()] = []
     648            for i in range(arraylike[key]+1):
     649                if key+str(i) in meta:
     650                    uninteresting_meta['arraylike'][key.lower()].append(meta[key+str(i)])
     651        self.ui_meta = uninteresting_meta
     652        # make newmeta self
     653        for key in newmeta:
     654            self.__dict__[key]=newmeta[key]
     655       
     656        dict = self.__dict__.copy()
     657        del dict['meta']
     658        del dict['ui_meta']
     659        self.dict = dict
     660
     661    def __iter__(self):
     662        """ iterator """
     663        return self
     664
     665    def next(self):
     666        """ used by __iter__ """
     667        # Here one might check, if looping makes any sense, and if not
     668        # one could stop looping or so...
     669        # like this:
     670        #
     671        # if len(self._registered_cols) == 0:
     672        #   print 'warning: looping without any registered columns'
     673        if self._current_row < self.number_of_rows:
     674            if self.GetNextRow() == False:
     675                raise StopIteration
     676            for col in self._registered_cols:
     677                if isinstance(self._table_cols[col], ctypes._SimpleCData):
     678                    self.__dict__[col] = self._table_cols[col].value
     679                    self.dict[col] = self._table_cols[col].value
     680                   
     681            for col in self.stacked_cols:
     682                if isinstance(self.dict[col], type(np.array(''))):
     683                    self.stacked_cols[col] = np.vstack( (self.stacked_cols[col],self.dict[col]) )
     684                else:
     685                    self.stacked_cols[col] = np.vstack( (self.stacked_cols[col],np.array(self.dict[col])) )
     686            self._current_row += 1
     687        else:
     688            raise StopIteration
     689        return self
     690
     691    def show(self):
     692        pprint.pprint(self.dict)
     693
     694
     695
     696
    382697class fnames( object ):
    383698    """ organize file names of a FACT data run
     
    455770# end of class definition: fnames( object )
    456771
     772def _test_SlowData( filename ):
     773    file = FactFits(filename)
     774    print '-'*70
     775    print "opened :", filename, " as 'file'"
     776    print
     777    print '-'*70
     778    print 'type file.show() to look at its contents'
     779    print "type file.register( columnname ) or file.register('all') in order to register columns"
     780    print
     781    print "   due column-registration you declare, that you would like to retrieve the contents of one of the columns"
     782    print "   after column-registration, the 'file' has new member variables, they are named like the columns"
     783    print "   PLEASE NOTE: immediatly after registration, the members exist, but they are empty."
     784    print "   the values are assigned only, when you call file.next() or when you loop over the 'file'"
     785    print
     786    print "in order to loop over it, just go like this:"
     787    print "for row in file:"
     788    print "    print row.columnname_one, row.columnname_two"
     789    print
     790    print ""
     791    print '-'*70
     792   
     793
     794
    457795def _test_iter( nevents ):
    458796    """ test for function __iter__ """
     
    471809if __name__ == '__main__':
    472810    """ tests  """
    473 
    474     _test_iter(10)
     811    import sys
     812    if len(sys.argv) == 1:
     813        print 'showing test of iterator of RawData class'
     814        print 'in order to test the SlowData classe please use:', sys.argv[0], 'fits-file-name'
     815        _test_iter(10)
     816    else:
     817        print 'showing test of SlowData class'
     818        print 'in case you wanted to test the RawData class, please give no commandline arguments'
     819        _test_SlowData(sys.argv[1])
Note: See TracChangeset for help on using the changeset viewer.