Example #1
0
 def geometry(self):
     geometry_string = self._fetch('geometry')
     geometry_access = None
     if geometry_string is not None:
         from psana.pscalib.geometry.GeometryAccess import GeometryAccess
         geometry_access = GeometryAccess()
         geometry_access.load_pars_from_str(geometry_string)
     return geometry_access
Example #2
0
 def _det_geo(self):
     """
     """
     if self._geo_ is None:
         geotxt, meta = self._det_geotxt_and_meta()
         if geotxt is None:
             logger.debug('_det_geo geotxt is None')
             return None            
         self._geo_ = GeometryAccess()
         self._geo_.load_pars_from_str(geotxt)
     return self._geo_
Example #3
0
 def det_geo(self):
     """
     """
     if self.geo is None:
         geotxt, meta = self.det_geotxt_and_meta()
         if geotxt is None:
             logger.warning('det_geo geotxt is None')
             return None
         self.geo = GeometryAccess()
         self.geo.load_pars_from_str(geotxt)
     return self.geo
Example #4
0
 def geo(self):
     """ return GeometryAccess() object
     """
     if self._geo is None:
         geotxt, meta = self.geotxt_and_meta()
         if geotxt is None:
             geotxt = self.geotxt_default()
             if ut.is_none(geotxt, 'geo geotxt is None'): return None
         self._geo = GeometryAccess()
         self._geo.load_pars_from_str(geotxt)
     return self._geo
Example #5
0
 def _det_geo(self):
     """
     """
     if self._geo_ is None:
         geotxt, meta = self._det_geotxt_and_meta()
         if geotxt is None:
             geotxt = self._det_geotxt_default()
             if is_none(geotxt, '_det_geo geotxt is None'): return None
         self._geo_ = GeometryAccess()
         self._geo_.load_pars_from_str(geotxt)
     return self._geo_
Example #6
0
    def data_geo(ntest):
        """Method for tests: returns test data numpy array and geometry object
    """
        from time import time
        from psana.pscalib.calib.NDArrIO import save_txt, load_txt
        from psana.pscalib.geometry.GeometryAccess import GeometryAccess

        dir = '/reg/g/psdm/detector/alignment/cspad/calib-cxi-camera2-2016-02-05'
        #fname_nda = '%s/nda-water-ring-cxij4716-r0022-e000001-CxiDs2-0-Cspad-0-ave.txt' % dir
        #fname_nda = '%s/nda-water-ring-cxij4716-r0022-e014636-CxiDs2-0-Cspad-0-ave.txt' % dir
        #fname_nda = '%s/nda-lysozyme-cxi02416-r0010-e052421-CxiDs2-0-Cspad-0-max.txt' % dir
        fname_nda = '%s/nda-lysozyme-cxi01516-r0026-e093480-CxiDs2-0-Cspad-0-max.txt'%dir if ntest in (21,28,29,30)\
                    else '%s/nda-water-ring-cxij4716-r0022-e014636-CxiDs2-0-Cspad-0-ave.txt'%dir
        fname_geo = '%s/calib/CsPad::CalibV1/CxiDs2.0:Cspad.0/geometry/geo-cxi01516-2016-02-18-Ag-behenate-tuned.data' % dir
        #fname_geo = '%s/geo-cxi02416-r0010-2016-03-11.txt' % dir
        fname_gain = '%s/calib/CsPad::CalibV1/CxiDs2.0:Cspad.0/pixel_gain/cxi01516-r0016-2016-02-18-FeKalpha.data' % dir

        # load n-d array with averaged water ring
        arr = load_txt(fname_nda)
        #arr *= load_txt(fname_gain)
        #print_ndarr(arr,'water ring')
        arr.shape = (arr.size, )  # (32*185*388,)

        # retrieve geometry
        t0_sec = time()
        geo = GeometryAccess(fname_geo)
        geo.move_geo('CSPAD:V1', 0, 1600, 0, 0)
        geo.move_geo('QUAD:V1', 2, -100, 0, 0)
        #geo.get_geo('QUAD:V1', 3).print_geo()
        print('Time to load geometry %.3f sec from file\n%s' %
              (time() - t0_sec, fname_geo))

        return arr, geo
Example #7
0
def issue_2022_03_02():
    """epix100 default geometry implementation
    """
    from psana.pscalib.geometry.GeometryAccess import GeometryAccess
    from psana.detector.NDArrUtils import info_ndarr
    from psana import DataSource

    #ds = DataSource(exp='tmox49720',run=209)
    #orun = next(ds.runs())
    #det = orun.Detector('epix100')

    ds = DataSource(exp='rixx45619',run=119)
    orun = next(ds.runs())
    det = orun.Detector('epixhr')

    for nevt,evt in enumerate(orun.events()):
        geotxt = det.raw._det_geotxt_default()
        print('_det_geotxt_default:\n%s' % geotxt)
        o = GeometryAccess()
        o.load_pars_from_str(geotxt)
        x,y,z = o.get_pixel_coords()
        print(info_ndarr(x,'x:'))
        if det.raw.image(evt) is not None: break
Example #8
0
class AreaDetector(DetectorImpl):

    def __init__(self, *args, **kwargs):
        logger.debug('AreaDetector.__init__') #  self.__class__.__name__
        DetectorImpl.__init__(self, *args, **kwargs)
        # caching
        self._geo_ = None
        self._pix_rc_ = None, None
        self._pix_xyz_ = None, None, None
        self._interpol_pars_ = None
        self._pedestals_ = None
        self._gain_ = None
        self._rms_ = None
        self._status_ = None
        self._common_mode_ = None
        self._mask_calib_ = None
        self._mask_ = None
        #logger.info('XXX dir(self):\n' + str(dir(self)))
        #logger.info('XXX self._segments:\n' + str(self._segments))


    def raw(self,evt) -> Array3d:
        """
        Returns dense 3-d numpy array of segment data
        from dict self._segments(evt)

        Parameters
        ----------
        evt: event
            psana event object, ex. run.events().next().

        Returns
        -------
        raw data: np.array, ndim=3, shape: as data
        """
        segs = self._segments(evt)
        if segs is None:
            logger.debug('self._segments(evt) is None')
            return None
        return arr3d_from_dict({k:v.raw for k,v in segs.items()})


    def _segment_numbers(self,evt):
        """ Returns dense 1-d numpy array of segment indexes.
        from dict self._segments(evt)    
        """
        segs = self._segments(evt)
        if segs is None: 
            logger.debug('self._segments(evt) is None')
            return None
        return np.array(sorted(segs.keys()), dtype=np.uint16)


    def _det_calibconst(self):
        logger.debug('AreaDetector._det_calibconst')
        cc = self._calibconst
        if cc is None:
            logger.debug('self._calibconst is None')
            return None
        return cc


    def _calibcons_and_meta_for_ctype(self, ctype='pedestals'):
        logger.debug('AreaDetector._calibcons_and_meta_for_ctype(ctype="%s")'%ctype)
        cc = self._det_calibconst()
        if cc is None: return None
        cons_and_meta = cc.get(ctype, None)
        if cons_and_meta is None:
            logger.debug('calibconst["%s"] is None'%ctype)
            return None, None
        return cons_and_meta


    def _cached_array(self, p, ctype='pedestals'):
        """cached array
        """
        if p is None: p = self._calibcons_and_meta_for_ctype(ctype)[0] # 0-data/1-metadata
        return p


    def _pedestals(self): return self._cached_array(self._pedestals_, 'pedestals')
    def _gain(self):      return self._cached_array(self._gain_, 'pixel_gain')
    def _rms(self):       return self._cached_array(self._rms_, 'pixel_rms')
    def _status(self):    return self._cached_array(self._status_, 'pixel_status')
    def _mask_calib(self):return self._cached_array(self._mask_calib_, 'pixel_mask')
    def _common_mode(self):return self._cached_array(self._common_mode_, 'common_mode')


    def _det_geotxt_and_meta(self):
        logger.debug('AreaDetector._det_geotxt_and_meta')
        cc = self._det_calibconst()
        if cc is None: return None
        geotxt_and_meta = cc.get('geometry', None)
        if geotxt_and_meta is None:
            logger.debug('calibconst[geometry] is None')
            return None, None
        return geotxt_and_meta


    def _det_geo(self):
        """
        """
        if self._geo_ is None:
            geotxt, meta = self._det_geotxt_and_meta()
            if geotxt is None:
                logger.debug('_det_geo geotxt is None')
                return None            
            self._geo_ = GeometryAccess()
            self._geo_.load_pars_from_str(geotxt)
        return self._geo_
        

    def _pixel_coord_indexes(self, **kwa):
        """
        """
        logger.debug('AreaDetector._pixel_coord_indexes')
        geo = self._det_geo()
        if geo is None:
            logger.debug('geo is None')
            return None
            
        return geo.get_pixel_coord_indexes(\
            pix_scale_size_um  = kwa.get('pix_scale_size_um',None),\
            xy0_off_pix        = kwa.get('xy0_off_pix',None),\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))


    def _pixel_coords(self, **kwa):
        """
        """
        logger.debug('AreaDetector._pixel_coords')
        geo = self._det_geo()
        if geo is None:
            logger.debug('geo is None')
            return None
        #return geo.get_pixel_xy_at_z(self, zplane=None, oname=None, oindex=0, do_tilt=True, cframe=0)
        return geo.get_pixel_coords(\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))


    def _cached_pixel_coord_indexes(self, evt, **kwa):
        """
        """
        logger.debug('AreaDetector._cached_pixel_coord_indexes')

        resp = self._pixel_coord_indexes(**kwa)
        if resp is None: return None

        # PRESERVE PIXEL INDEXES FOR USED SEGMENTS ONLY
        segs = self._segment_numbers(evt)
        if segs is None: return None
        logger.debug(info_ndarr(segs, 'preserve pixel indices for segments '))

        rows, cols = self._pix_rc_ = [reshape_to_3d(a)[segs,:,:] for a in resp]
        #self._pix_rc_ = [dict_from_arr3d(reshape_to_3d(v)) for v in resp]

        s = 'evaluate_pixel_coord_indexes:'
        for i,a in enumerate(self._pix_rc_): s += info_ndarr(a, '\n  %s '%('rows','cols')[i], last=3)
        logger.info(s)

        mapmode = kwa.get('mapmode',2)
        if mapmode <4:
          self.img_entries, self.dmulti_pix_to_img_idx, self.dmulti_imgidx_numentries=\
            statistics_of_pixel_arrays(rows, cols)

        if mapmode==4:
            rsp = self._pixel_coords(**kwa)
            if rsp is None: return None
            x,y,z = self._pix_xyz_ = [reshape_to_3d(a)[segs,:,:] for a in rsp]
            self._interpol_pars_ = init_interpolation_parameters(rows, cols, x, y)

        if mapmode <4 and kwa.get('fillholes',True):
            self.img_pix_ascend_ind, self.img_holes, self.hole_rows, self.hole_cols, self.hole_inds1d =\
               statistics_of_holes(rows, cols, **kwa)

        # TBD parameters for image interpolation
        if False:
            t0_sec = time()
            self.imgind_to_seg_row_col = image_of_pixel_seg_row_col(img_pix_ascend_ind, arr_shape)
            logger.debug('statistics_of_holes.imgind_to_seg_row_col time (sec) = %.6f' % (time()-t0_sec)) # 47ms
            logger.debug(info_ndarr(self.imgind_to_seg_row_col, ' imgind_to_seg_row_col '))
    
            if False:
                s = ' imgind_to_seg_row_col '
                # (n,352,384)
                first = (352+5)*384 + 380
                for i in range(first,first+10): s += '\n    s:%02d r:%03d c:%03d' % tuple(imgind_to_seg_row_col[i])
                logger.debug(s)


    def calib(self, evt, **kwa) -> Array3d:
        """
        """
        logger.debug('%s.calib(evt) is implemented for generic case of area detector as raw - pedestals' % self.__class__.__name__\
                      +'\n  If needed more, it needs to be re-implemented for this detector type.')
        raw = self.raw(evt)
        if raw is None:
            logger.debug('det.raw.raw(evt) is None')
            return None

        peds = self._pedestals()
        if peds is None:
            logger.debug('det.raw._pedestals() is None - return det.raw.raw(evt)')
            return raw
        return raw - peds


    def image(self, evt, nda=None, **kwa) -> Array2d:
        """
        Create 2-d image.

        Parameters
        ----------
        evt: event
            psana event object, ex. run.events().next().

        mapmode: int, optional, default: 2
            control on overlapping pixels on image map.
            0/1/2/3/4: statistics of entries / last / max / mean pixel intensity / interpolated (TBD) - ascending data index.

        fillholes: bool, optional, default: True
            control on map bins inside the panel with 0 entries from data.
            True/False: fill empty bin with minimal intensity of four neares neighbors/ do not fill.

        vbase: float, optional, default: 0
            value substituted for all image map bins without entry from data.  

        Returns
        -------
        image: np.array, ndim=2
        
        """
        logger.debug('in AreaDretector.image')
        if any(v is None for v in self._pix_rc_):
            self._cached_pixel_coord_indexes(evt, **kwa)
            if any(v is None for v in self._pix_rc_): return None

        vbase     = kwa.get('vbase',0)
        mapmode   = kwa.get('mapmode',2)
        fillholes = kwa.get('fillholes',True)

        if mapmode==0: return self.img_entries

        data = self.calib(evt) if nda is None else nda
        if data is None:
            logger.debug('AreaDetector.image calib returns None')
            return None
            
        #logger.debug(info_ndarr(data, 'data ', last=3))

        rows, cols = self._pix_rc_
        img = img_from_pixel_arrays(rows, cols, weight=data, vbase=vbase) # mapmode==1
        if   mapmode==2: img_multipixel_max(img, data, self.dmulti_pix_to_img_idx)
        elif mapmode==3: img_multipixel_mean(img, data, self.dmulti_pix_to_img_idx, self.dmulti_imgidx_numentries)

        if mapmode<4 and fillholes: fill_holes(img, self.hole_rows, self.hole_cols)

        return img if mapmode<4 else\
               img_interpolated(data, self._interpol_pars_) if mapmode==4 else\
               self.img_entries


    def _shape_as_daq(self):
        peds = self._pedestals()
        if peds is None: 
            logger.debug('In _shape_as_daq pedestals are None, can not define daq data shape')
            return None
        return peds.shape if peds.ndim<4 else peds.shape[-3:]


    def _number_of_segments_total(self):
        shape = self._shape_as_daq()
        return None if shape is None else shape[-3] # (7,n,352,384) - define through calibration constants


    def _mask_default(self, dtype=DTYPE_MASK):
        shape = self._shape_as_daq()
        return None if shape is None else np.ones(shape, dtype=dtype)


    def _mask_calib_or_default(self, dtype=DTYPE_MASK):
        mask = self._mask_calib()
        return self._mask_default(dtype) if mask is None else mask.astype(dtype)


    def _mask_from_status(self, **kwa):
        """
        For simple detectors w/o multi-gain ranges

        Parameters **kwa
        ----------------
        ##mode - int 0/1/2 masks zero/four/eight neighbors around each bad pixel

        Returns
        -------
        mask made of status: np.array, ndim=3, shape: as full detector data
        """
        status = self._status()
        if status is None:
            logger.debug('pixel_status is None')
            return None
        return np.asarray(np.select((status>0,), (0,), default=1), dtype=DTYPE_MASK)


    def _mask_edges(self, **kwa): # -> Array3d:
        mask = self._mask_default(self, dtype=DTYPE_MASK)
        return None if mask is None else\
          mask_edges(mask,\
            edge_rows=kwa.get('edge_rows', 1),\
            edge_cols=kwa.get('edge_cols', 1),\
            dtype=DTYPE_MASK) # kwa.get('dtype', DTYPE_MASK)):


#     def _mask_neighbors(self, **kwa) -> Array3d:
       #mode = kwargs.get('mode', 0)
        #if mode: smask = gu.mask_neighbors(smask, allnbrs=(True if mode>=2 else False))

        #segs = self._segments(evt)
        #if segs is None:
        #    logger.debug('self._segments(evt) is None')
        #    return None
        #return arr3d_from_dict({k:v.raw for k,v in segs.items()})


    def _mask(self, calib=False, status=False, edges=False, neighbors=False, **kwa):
        """Returns per-pixel array with mask values (per-pixel product of all requested masks).
           Parameters
           - calib    : bool - True/False = on/off mask from calib directory.
           - status   : bool - True/False = on/off mask generated from calib pixel_status. 
           - edges    : bool - True/False = on/off mask of edges. 
           - neighbors: bool - True/False = on/off mask of neighbors. 
           - kwa      : dict - additional parameters passed to low level methods (width,...) 
                        for edges: edge_rows=1, edge_cols=1, center_rows=0, center_cols=0, dtype=DTYPE_MASK
                        for status of epix10ka: grinds=(0,1,2,3,4)
           Returns
           - np.array - per-pixel mask values 1/0 for good/bad pixels.
        """
        dtype = kwa.get('dtype', DTYPE_MASK) 
        mask = self._mask_calib_or_default(dtype) if calib else self._mask_default(dtype)
        if status: mask = merge_masks(mask, self._mask_from_status(**kwa)) 
        if edges: mask = merge_masks(mask, self._mask_edges(**kwa))
        #if neighbors: mask = merge_masks(mask, self._mask_neighbors(self, **kwa))
        return mask


    def _mask_comb(self, **kwa):
        mbits=kwa.get('mbits', 1)      
        return self._mask(\
          calib     = mbits & 1,\
          status    = mbits & 2,\
          edges     = mbits & 4,\
          neighbors = mbits & 8,\
          **kwa)
Example #9
0
class AreaDetector(DetectorImpl):
    def __init__(self, *args, **kwargs):
        logger.debug('AreaDetector.__init__')  #  self.__class__.__name__
        DetectorImpl.__init__(self, *args, **kwargs)
        # caching
        self.geo = None
        self.pix_rc = None, None
        self.pix_xyz = None, None, None
        self.interpol_pars = None
        #logger.info('XXX dir(self):\n' + str(dir(self)))
        #logger.info('XXX self._segments:\n' + str(self._segments))

    # example of some possible common behavior
    def _common_mode(self, *args, **kwargs):
        """
        """
        logger.debug('in %s._common_mode' % self.__class__.__name__)
        pass

    def raw(self, evt):
        """ Returns dense 3-d numpy array of segment data
        from dict self._segments(evt)
        """
        segs = self._segments(evt)
        if segs is None:
            logger.warning('self._segments(evt) is None')
            return None
        return arr3d_from_dict({k: v.raw for k, v in segs.items()})

    def segments(self, evt):
        """ Returns dense 1-d numpy array of segment indexes.
        from dict self._segments(evt)    
        """
        segs = self._segments(evt)
        if segs is None:
            logger.warning('self._segments(evt) is None')
            return None
        return np.array(sorted(segs.keys()), dtype=np.uint16)

    def det_calibconst(self):
        logger.debug('AreaDetector.det_calibconst')
        cc = self._calibconst
        if cc is None:
            logger.warning('self._calibconst is None')
            return None
        return cc

    def det_geotxt_and_meta(self):
        logger.debug('AreaDetector.det_geometry_txt')
        cc = self.det_calibconst()
        if cc is None: return None
        geotxt_and_meta = cc.get('geometry', None)
        if geotxt_and_meta is None:
            logger.warning('calibconst[geometry] is None')
            return None, None
        return geotxt_and_meta

    def det_geo(self):
        """
        """
        if self.geo is None:
            geotxt, meta = self.det_geotxt_and_meta()
            if geotxt is None:
                logger.warning('det_geo geotxt is None')
                return None
            self.geo = GeometryAccess()
            self.geo.load_pars_from_str(geotxt)
        return self.geo

    def pixel_coord_indexes(self, **kwa):
        """
        """
        logger.debug('AreaDetector.pixel_coord_indexes')
        geo = self.det_geo()
        if geo is None:
            logger.warning('geo is None')
            return None

        return geo.get_pixel_coord_indexes(\
            pix_scale_size_um  = kwa.get('pix_scale_size_um',None),\
            xy0_off_pix        = kwa.get('xy0_off_pix',None),\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))

    def pixel_coords(self, **kwa):
        """
        """
        logger.debug('AreaDetector.pixel_coords')
        geo = self.det_geo()
        if geo is None:
            logger.warning('geo is None')
            return None
        #return geo.get_pixel_xy_at_z(self, zplane=None, oname=None, oindex=0, do_tilt=True, cframe=0)
        return geo.get_pixel_coords(\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))

    def cached_pixel_coord_indexes(self, evt, **kwa):
        """
        """
        logger.debug('AreaDetector.cached_pixel_coord_indexes')

        resp = self.pixel_coord_indexes(**kwa)
        if resp is None: return None

        # PRESERVE PIXEL INDEXES FOR USED SEGMENTS ONLY
        segs = self.segments(evt)
        if segs is None: return None
        logger.debug(info_ndarr(segs, 'preserve pixel indices for segments '))

        rows, cols = self.pix_rc = [reshape_to_3d(a)[segs, :, :] for a in resp]
        #self.pix_rc = [dict_from_arr3d(reshape_to_3d(v)) for v in resp]

        s = 'evaluate_pixel_coord_indexes:'
        for i, a in enumerate(self.pix_rc):
            s += info_ndarr(a, '\n  %s ' % ('rows', 'cols')[i], last=3)
        logger.info(s)

        mapmode = kwa.get('mapmode', 2)
        if mapmode < 4:
            self.img_entries, self.dmulti_pix_to_img_idx, self.dmulti_imgidx_numentries=\
              statistics_of_pixel_arrays(rows, cols)

        if mapmode == 4:
            rsp = self.pixel_coords(**kwa)
            if rsp is None: return None
            x, y, z = self.pix_xyz = [
                reshape_to_3d(a)[segs, :, :] for a in rsp
            ]
            self.interpol_pars = init_interpolation_parameters(
                rows, cols, x, y)

        if mapmode < 4 and kwa.get('fillholes', True):
            self.img_pix_ascend_ind, self.img_holes, self.hole_rows, self.hole_cols, self.hole_inds1d =\
               statistics_of_holes(rows, cols, **kwa)

        # TBD parameters for image interpolation
        if False:
            t0_sec = time()
            self.imgind_to_seg_row_col = image_of_pixel_seg_row_col(
                img_pix_ascend_ind, arr_shape)
            logger.debug(
                'statistics_of_holes.imgind_to_seg_row_col time (sec) = %.6f' %
                (time() - t0_sec))  # 47ms
            logger.debug(
                info_ndarr(self.imgind_to_seg_row_col,
                           ' imgind_to_seg_row_col '))

            if False:
                s = ' imgind_to_seg_row_col '
                # (n,352,384)
                first = (352 + 5) * 384 + 380
                for i in range(first, first + 10):
                    s += '\n    s:%02d r:%03d c:%03d' % tuple(
                        imgind_to_seg_row_col[i])
                logger.debug(s)

    def calib(self, evt):
        """
        """
        logger.warning('AreaDetector.calib TBD currently returns raw')
        #print('XXX dir(self):', dir(self))
        #cc = self.det_calibconst()
        #if cc is None: return None
        #peds, peds_meta = cc.get('pedestals', None)
        #...
        return self.raw(evt)

    def image(self, evt, nda=None, **kwa):
        logger.debug('in AreaDretector.image')
        if any(v is None for v in self.pix_rc):
            self.cached_pixel_coord_indexes(evt, **kwa)
            if any(v is None for v in self.pix_rc): return None

        vbase = kwa.get('vbase', 0)
        mapmode = kwa.get('mapmode', 2)
        fillholes = kwa.get('fillholes', True)

        if mapmode == 0: return self.img_entries

        data = self.calib(evt) if nda is None else nda
        if data is None:
            logger.warning('AreaDetector.image calib returns None')
            return None

        #logger.debug(info_ndarr(data, 'data ', last=3))

        rows, cols = self.pix_rc
        img = img_from_pixel_arrays(rows, cols, weight=data,
                                    vbase=vbase)  # mapmode==1
        if mapmode == 2:
            img_multipixel_max(img, data, self.dmulti_pix_to_img_idx)
        elif mapmode == 3:
            img_multipixel_mean(img, data, self.dmulti_pix_to_img_idx,
                                self.dmulti_imgidx_numentries)

        if mapmode < 4 and fillholes:
            fill_holes(img, self.hole_rows, self.hole_cols)

        return img if mapmode<4 else\
               img_interpolated(data, self.interpol_pars) if mapmode==4 else\
               self.img_entries
Example #10
0
class CalibConstants:

    def __init__(self, calibconst, **kwa):
        """
        Parameters
        ----------
        calibconst: dict - retrieved from DB by method calib_constants_all_types from psana.pscalib.calib.MDBWebUtils.
        **kwa: not used
        """
        logger.debug('__init__') #  self.__class__.__name__

        assert isinstance(calibconst, dict), 'Input parameter should be dict: %s' % str(calibconst)

        self._calibconst = calibconst
        self._geo = None
        self._pedestals = None
        self._gain = None # ADU/eV
        self._gain_factor = None # keV/ADU
        self._rms = None
        self._status = None
        self._common_mode = None
        self._mask_calib = None

        self._pix_rc = None, None
        self._pix_xyz = None, None, None
        self._interpol_pars = None


    def calibconst(self):
        logger.debug('calibconst')
        cc = self._calibconst
        if ut.is_none(cc, 'self._calibconst is None'): return None
        return cc


    def cons_and_meta_for_ctype(self, ctype='pedestals'):
        logger.debug('cons_and_meta_for_ctype(ctype="%s")'%ctype)
        cc = self.calibconst()
        if cc is None: return None
        cons_and_meta = cc.get(ctype, None)
        if ut.is_none(cons_and_meta, 'calibconst["%s"] is None'%ctype): return None, None
        return cons_and_meta


    def cached_array(self, p, ctype='pedestals'):
        """returns cached array of constants for ctype
        """
        if p is None: p = self.cons_and_meta_for_ctype(ctype)[0] # 0-data/1-metadata
        return p


    def pedestals(self):   return self.cached_array(self._pedestals, 'pedestals')
    def rms(self):         return self.cached_array(self._rms, 'pixel_rms')
    def common_mode(self): return self.cached_array(self._common_mode, 'common_mode')
    def gain(self):        return self.cached_array(self._gain, 'pixel_gain')


    def mask_calib(self):
        a = self.cached_array(self._mask_calib, 'pixel_mask')
        return a if a is None else a.astype(DTYPE_MASK)


    def status(self):
        a = self.cached_array(self._status, 'pixel_status')
        return a if a is None else a.astype(DTYPE_STATUS)


    def gain_factor(self):
        """Evaluate and return gain factor as 1/gain if gain is available in calib constants else 1."""
        if self._gain_factor is None:
            g = self.gain()
            self._gain_factor = divide_protected(np.ones_like(g), g) if isinstance(g, np.ndarray) else 1
        return self._gain_factor


    def shape_as_daq(self):
        peds = self.pedestals()
        if ut.is_none(peds, 'shape_as_daq - pedestals is None, can not define daq data shape - returns None'): return None
        return peds.shape if peds.ndim<4 else peds.shape[-3:]


    def number_of_segments_total(self):
        shape = self.shape_as_daq()
        return None if shape is None else shape[-3] # (7,n,352,384) - define through calibration constants


    def segment_numbers_total(self):
        """returns total list list of segment numbers."""
        nsegs = self.number_of_segments_total()
        segnums = None if ut.is_none(nsegs, 'number_of_segments_total is None') else\
                  list(range(nsegs))
                  #tuple(np.arange(nsegs, dtype=np.uint16))
        logger.debug('segnums: %s' % str(segnums))
        return segnums


    def geotxt_and_meta(self):
        logger.debug('geotxt_and_meta')
        cc = self.calibconst()
        if cc is None: return None
        geotxt_and_meta = cc.get('geometry', None)
        if ut.is_none(geotxt_and_meta, 'calibconst[geometry] is None'): return None, None
        return geotxt_and_meta


    def geotxt_default(self):
        logger.debug('geotxt_default should be re-implemented in specific detector subclass, othervise returns None')
        return None


    def geo(self):
        """ return GeometryAccess() object
        """
        if self._geo is None:
            geotxt, meta = self.geotxt_and_meta()
            if geotxt is None:
                geotxt = self.geotxt_default()
                if ut.is_none(geotxt, 'geo geotxt is None'): return None
            self._geo = GeometryAccess()
            self._geo.load_pars_from_str(geotxt)
        return self._geo


    def seg_geo(self):
        logger.debug('pixel_coords')
        geo = self.geo()
        if ut.is_none(geo, 'geo is None'): return None
        return None if ut.is_none(geo, 'geo is None') else\
               geo.get_seg_geo().algo


    def pixel_coords(self, **kwa):
        """ returns x, y, z - three np.ndarray
        """
        logger.debug('pixel_coords')
        geo = self.geo()
        if ut.is_none(geo, 'geo is None'): return None

        #return geo.get_pixel_xy_at_z(self, zplane=None, oname=None, oindex=0, do_tilt=True, cframe=0)
        return geo.get_pixel_coords(\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))


    def pixel_coord_indexes(self, **kwa):
        """ returns ix, iy - two np.ndarray
        """
        logger.debug('pixel_coord_indexes')
        geo = self.geo()
        if ut.is_none(geo, 'geo is None'): return None

        return geo.get_pixel_coord_indexes(\
            pix_scale_size_um  = kwa.get('pix_scale_size_um',None),\
            xy0_off_pix        = kwa.get('xy0_off_pix',None),\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))


    def cached_pixel_coord_indexes(self, segnums=None, **kwa):
        """
        """
        logger.debug('CalibConstants.cached_pixel_coord_indexes')

        resp = self.pixel_coord_indexes(**kwa)
        if resp is None: return None

        # PRESERVE PIXEL INDEXES FOR USED SEGMENTS ONLY
        if segnums is None:
            segnums = self.segment_numbers_total()

        logger.info(info_ndarr(segnums, 'preserve pixel indices for segments '))

        rows, cols = self._pix_rc = [reshape_to_3d(a)[segnums,:,:] for a in resp]
        #self._pix_rc = [dict_from_arr3d(reshape_to_3d(v)) for v in resp]

        s = 'evaluate_pixel_coord_indexes:'
        for i,a in enumerate(self._pix_rc): s += info_ndarr(a, '\n  %s '%('rows','cols')[i], last=3)
        logger.info(s)

        mapmode = kwa.get('mapmode',2)
        fillholes = kwa.get('fillholes',True)

        if mapmode <4:
          self.img_entries, self.dmulti_pix_to_img_idx, self.dmulti_imgidx_numentries=\
            statistics_of_pixel_arrays(rows, cols)

        if mapmode==4:
            rsp = self._pixel_coords(**kwa)
            if rsp is None: return None
            x,y,z = self._pix_xyz = [reshape_to_3d(a)[segnums,:,:] for a in rsp]
            self._interpol_pars = init_interpolation_parameters(rows, cols, x, y)

        if mapmode <4 and fillholes:
            self.img_pix_ascend_ind, self.img_holes, self.hole_rows, self.hole_cols, self.hole_inds1d =\
               statistics_of_holes(rows, cols, **kwa)

        # TBD parameters for image interpolation
        if False:
            t0_sec = time()
            self.imgind_to_seg_row_col = image_of_pixel_seg_row_col(img_pix_ascend_ind, arr_shape)
            logger.debug('statistics_of_holes.imgind_to_seg_row_col time (sec) = %.6f' % (time()-t0_sec)) # 47ms
            logger.debug(info_ndarr(self.imgind_to_seg_row_col, ' imgind_to_seg_row_col '))

            if False:
                s = ' imgind_to_seg_row_col '
                # (n,352,384)
                first = (352+5)*384 + 380
                for i in range(first,first+10): s += '\n    s:%02d r:%03d c:%03d' % tuple(imgind_to_seg_row_col[i])
                logger.debug(s)


    def image(self, nda, segnums=None, **kwa):
        """
        Create 2-d image.

        Parameters
        ----------
        nda: np.array, ndim=3
            array shaped as daq raw data.

        segnums: list/tuple of segment (uint) indexes

        mapmode: int, optional, default: 2
            control on overlapping pixels on image map.
            0/1/2/3/4: statistics of entries / last / max / mean pixel intensity / interpolated (TBD) - ascending data index.

        fillholes: bool, optional, default: True
            control on map bins inside the panel with 0 entries from data.
            True/False: fill empty bin with minimal intensity of four neares neighbors/ do not fill.

        vbase: float, optional, default: 0
            value substituted for all image map bins without entry from data.

        Returns
        -------
        image: np.array, ndim=2
        """
        logger.debug('in CalibConstants.image')

        if any(v is None for v in self._pix_rc):
            self.cached_pixel_coord_indexes(segnums, **kwa)
            if any(v is None for v in self._pix_rc): return None

        vbase     = kwa.get('vbase',0)
        mapmode   = kwa.get('mapmode',2)
        fillholes = kwa.get('fillholes',True)

        if mapmode==0: return self.img_entries

        if ut.is_none(nda, 'CalibConstants.image calib returns None'): return None

        logger.debug(info_ndarr(nda, 'nda ', last=3))
        rows, cols = self._pix_rc
        logger.debug(info_ndarr(rows, 'rows ', last=3))
        logger.debug(info_ndarr(cols, 'cols ', last=3))

        img = img_from_pixel_arrays(rows, cols, weight=nda, vbase=vbase) # mapmode==1
        if   mapmode==2: img_multipixel_max(img, nda, self.dmulti_pix_to_img_idx)
        elif mapmode==3: img_multipixel_mean(img, nda, self.dmulti_pix_to_img_idx, self.dmulti_imgidx_numentries)

        if mapmode<4 and fillholes: fill_holes(img, self.hole_rows, self.hole_cols)

        return img if mapmode<4 else\
               img_interpolated(nda, self._cached_interpol_pars()) if mapmode==4 else\
               self.img_entries


    def pix_rc(self): return self._pix_rc

    def pix_xyz(self): return self._pix_xyz

    def interpol_pars(self): return self._interpol_pars
Example #11
0
class AreaDetector(DetectorImpl):
    def __init__(self, *args, **kwargs):
        logger.debug('AreaDetector.__init__')  #  self.__class__.__name__
        DetectorImpl.__init__(self, *args, **kwargs)
        # caching
        self._geo_ = None
        self._pix_rc_ = None, None
        self._pix_xyz_ = None, None, None
        self._interpol_pars_ = None
        self._pedestals_ = None
        self._gain_ = None  # ADU/eV
        self._gain_factor_ = None  # keV/ADU
        self._rms_ = None
        self._status_ = None
        self._common_mode_ = None
        self._mask_calib_ = None
        self._mask_ = None
        #logger.info('XXX dir(self):\n' + str(dir(self)))
        #logger.info('XXX self._segments:\n' + str(self._segments))

    def raw(self, evt) -> Array3d:
        """
        Returns dense 3-d numpy array of segment data
        from dict self._segments(evt)

        Parameters
        ----------
        evt: event
            psana event object, ex. run.events().next().

        Returns
        -------
        raw data: np.array, ndim=3, shape: as data
        """
        if evt is None: return None
        segs = self._segments(evt)
        if is_none(segs, 'self._segments(evt) is None'): return None
        return arr3d_from_dict({k: v.raw for k, v in segs.items()})

    def _segment_numbers(self, evt):
        """ Returns dense 1-d numpy array of segment indexes.
        from dict self._segments(evt)
        """
        segs = self._segments(evt)
        if is_none(segs, 'self._segments(evt) is None'): return None
        return np.array(sorted(segs.keys()), dtype=np.uint16)

    def _det_calibconst(self):
        logger.debug('AreaDetector._det_calibconst')
        cc = self._calibconst
        if is_none(cc, 'self._calibconst is None'): return None
        return cc

    def _calibcons_and_meta_for_ctype(self, ctype='pedestals'):
        logger.debug('AreaDetector._calibcons_and_meta_for_ctype(ctype="%s")' %
                     ctype)
        cc = self._det_calibconst()
        if cc is None: return None
        cons_and_meta = cc.get(ctype, None)
        if is_none(cons_and_meta, 'calibconst["%s"] is None' % ctype):
            return None, None
        return cons_and_meta

    def _cached_array(self, p, ctype='pedestals'):
        """cached array
        """
        if p is None:
            p = self._calibcons_and_meta_for_ctype(ctype)[
                0]  # 0-data/1-metadata
        return p

    def _pedestals(self):
        return self._cached_array(self._pedestals_, 'pedestals')

    def _rms(self):
        return self._cached_array(self._rms_, 'pixel_rms')

    def _status(self):
        return self._cached_array(self._status_, 'pixel_status')

    def _mask_calib(self):
        return self._cached_array(self._mask_calib_, 'pixel_mask')

    def _common_mode(self):
        return self._cached_array(self._common_mode_, 'common_mode')

    def _gain(self):
        return self._cached_array(self._gain_, 'pixel_gain')

    def _gain_factor(self):
        """Evaluate and return gain factor as 1/gain if gain is available in calib constants else 1."""
        if self._gain_factor_ is None:
            g = self._gain()
            self._gain_factor_ = divide_protected(
                np.ones_like(g), g) if isinstance(g, np.ndarray) else 1
        return self._gain_factor_

    def _det_geotxt_and_meta(self):
        logger.debug('AreaDetector._det_geotxt_and_meta')
        cc = self._det_calibconst()
        if cc is None: return None
        geotxt_and_meta = cc.get('geometry', None)
        if is_none(geotxt_and_meta, 'calibconst[geometry] is None'):
            return None, None
        return geotxt_and_meta

    def _det_geotxt_default(self):
        logger.debug(
            '_det_geotxt_default should be re-implemented in specific detector subclass, othervise returns None'
        )
        return None

    def _det_geo(self):
        """
        """
        if self._geo_ is None:
            geotxt, meta = self._det_geotxt_and_meta()
            if geotxt is None:
                geotxt = self._det_geotxt_default()
                if is_none(geotxt, '_det_geo geotxt is None'): return None
            self._geo_ = GeometryAccess()
            self._geo_.load_pars_from_str(geotxt)
        return self._geo_

    def _pixel_coord_indexes(self, **kwa):
        """
        """
        logger.debug('AreaDetector._pixel_coord_indexes')
        geo = self._det_geo()
        if is_none(geo, 'geo is None'): return None

        return geo.get_pixel_coord_indexes(\
            pix_scale_size_um  = kwa.get('pix_scale_size_um',None),\
            xy0_off_pix        = kwa.get('xy0_off_pix',None),\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))

    def _pixel_coords(self, **kwa):
        """
        """
        logger.debug('AreaDetector._pixel_coords')
        geo = self._det_geo()
        if is_none(geo, 'geo is None'): return None

        #return geo.get_pixel_xy_at_z(self, zplane=None, oname=None, oindex=0, do_tilt=True, cframe=0)
        return geo.get_pixel_coords(\
            do_tilt            = kwa.get('do_tilt',True),\
            cframe             = kwa.get('cframe',0))

    def _cached_pixel_coord_indexes(self, evt, **kwa):
        """
        """
        logger.debug('AreaDetector._cached_pixel_coord_indexes')

        resp = self._pixel_coord_indexes(**kwa)
        if resp is None: return None

        # PRESERVE PIXEL INDEXES FOR USED SEGMENTS ONLY
        segs = self._segment_numbers(evt)
        if segs is None: return None
        logger.debug(info_ndarr(segs, 'preserve pixel indices for segments '))

        rows, cols = self._pix_rc_ = [
            reshape_to_3d(a)[segs, :, :] for a in resp
        ]
        #self._pix_rc_ = [dict_from_arr3d(reshape_to_3d(v)) for v in resp]

        s = 'evaluate_pixel_coord_indexes:'
        for i, a in enumerate(self._pix_rc_):
            s += info_ndarr(a, '\n  %s ' % ('rows', 'cols')[i], last=3)
        logger.info(s)

        mapmode = kwa.get('mapmode', 2)
        if mapmode < 4:
            self.img_entries, self.dmulti_pix_to_img_idx, self.dmulti_imgidx_numentries=\
              statistics_of_pixel_arrays(rows, cols)

        if mapmode == 4:
            rsp = self._pixel_coords(**kwa)
            if rsp is None: return None
            x, y, z = self._pix_xyz_ = [
                reshape_to_3d(a)[segs, :, :] for a in rsp
            ]
            self._interpol_pars_ = init_interpolation_parameters(
                rows, cols, x, y)

        if mapmode < 4 and kwa.get('fillholes', True):
            self.img_pix_ascend_ind, self.img_holes, self.hole_rows, self.hole_cols, self.hole_inds1d =\
               statistics_of_holes(rows, cols, **kwa)

        # TBD parameters for image interpolation
        if False:
            t0_sec = time()
            self.imgind_to_seg_row_col = image_of_pixel_seg_row_col(
                img_pix_ascend_ind, arr_shape)
            logger.debug(
                'statistics_of_holes.imgind_to_seg_row_col time (sec) = %.6f' %
                (time() - t0_sec))  # 47ms
            logger.debug(
                info_ndarr(self.imgind_to_seg_row_col,
                           ' imgind_to_seg_row_col '))

            if False:
                s = ' imgind_to_seg_row_col '
                # (n,352,384)
                first = (352 + 5) * 384 + 380
                for i in range(first, first + 10):
                    s += '\n    s:%02d r:%03d c:%03d' % tuple(
                        imgind_to_seg_row_col[i])
                logger.debug(s)

    def calib(self, evt, **kwa) -> Array3d:
        """
        """
        logger.debug('%s.calib(evt) is implemented for generic case of area detector as raw - pedestals' % self.__class__.__name__\
                      +'\n  If needed more, it needs to be re-implemented for this detector type.')
        raw = self.raw(evt)
        if is_none(raw, 'det.raw.raw(evt) is None'): return None

        peds = self._pedestals()
        if is_none(peds,
                   'det.raw._pedestals() is None - return det.raw.raw(evt)'):
            return raw

        arr = raw - peds
        gfac = self._gain_factor()

        return arr * gfac if gfac != 1 else arr

    def image(self, evt, nda=None, **kwa) -> Array2d:
        """
        Create 2-d image.

        Parameters
        ----------
        evt: event
            psana event object, ex. run.events().next().

        mapmode: int, optional, default: 2
            control on overlapping pixels on image map.
            0/1/2/3/4: statistics of entries / last / max / mean pixel intensity / interpolated (TBD) - ascending data index.

        fillholes: bool, optional, default: True
            control on map bins inside the panel with 0 entries from data.
            True/False: fill empty bin with minimal intensity of four neares neighbors/ do not fill.

        vbase: float, optional, default: 0
            value substituted for all image map bins without entry from data.

        Returns
        -------
        image: np.array, ndim=2
        """
        logger.debug('in AreaDretector.image')
        if any(v is None for v in self._pix_rc_):
            self._cached_pixel_coord_indexes(evt, **kwa)
            if any(v is None for v in self._pix_rc_): return None

        vbase = kwa.get('vbase', 0)
        mapmode = kwa.get('mapmode', 2)
        fillholes = kwa.get('fillholes', True)

        if mapmode == 0: return self.img_entries

        data = self.calib(evt) if nda is None else nda

        if is_none(data, 'AreaDetector.image calib returns None'): return None

        #logger.debug(info_ndarr(data, 'data ', last=3))

        rows, cols = self._pix_rc_
        img = img_from_pixel_arrays(rows, cols, weight=data,
                                    vbase=vbase)  # mapmode==1
        if mapmode == 2:
            img_multipixel_max(img, data, self.dmulti_pix_to_img_idx)
        elif mapmode == 3:
            img_multipixel_mean(img, data, self.dmulti_pix_to_img_idx,
                                self.dmulti_imgidx_numentries)

        if mapmode < 4 and fillholes:
            fill_holes(img, self.hole_rows, self.hole_cols)

        return img if mapmode<4 else\
               img_interpolated(data, self._interpol_pars_) if mapmode==4 else\
               self.img_entries

    def _shape_as_daq(self):
        peds = self._pedestals()
        if is_none(
                peds,
                'In _shape_as_daq pedestals is None, can not define daq data shape - returns None'
        ):
            return None
        return peds.shape if peds.ndim < 4 else peds.shape[-3:]

    def _number_of_segments_total(self):
        shape = self._shape_as_daq()
        return None if shape is None else shape[
            -3]  # (7,n,352,384) - define through calibration constants

    def _mask_default(self, dtype=DTYPE_MASK):
        shape = self._shape_as_daq()
        return None if shape is None else np.ones(shape, dtype=dtype)

    def _mask_calib_or_default(self, dtype=DTYPE_MASK):
        mask = self._mask_calib()
        return self._mask_default(dtype) if mask is None else mask.astype(
            dtype)

    def _mask_from_status(self, status_bits=0xffff, dtype=DTYPE_MASK, **kwa):
        """
        Mask made of pixel_status calibration constants.

        Parameters **kwa
        ----------------

        - status_bits : uint - bitword for mask status codes.
        - dtype : numpy.dtype - mask np.array dtype

        Returns
        -------
        np.array - mask made of pixel_status calibration constants, shapeed as full detector data
        """
        status = self._status()
        if is_none(status, 'pixel_status is None'): return None
        return um.status_as_mask(status,
                                 status_bits=status_bits,
                                 dtype=DTYPE_MASK,
                                 **kwa)

    def _mask_neighbors(self, mask, rad=9, ptrn='r'):
        """Returns 2-d or n-d mask array with increased by radial paramerer rad region around all 0-pixels in the input mask.

           Parameter

           - mask : np.array - input mask of good/bad (1/0) pixels
           - rad  : int - radisl parameter for masking region aroung each 0-pixel in mask
           - ptrn : char - pattern of the masked region, ptrn='r'-rhombus, 'c'-circle, othervise square [-rad,+rad] in rows and columns.

           Returns

           - np.array - mask with masked neighbors, shape = mask.shape
        """
        return um.mask_neighbors(mask, rad, ptrn)

    def _mask_edges(self,
                    width=0,
                    edge_rows=1,
                    edge_cols=1,
                    dtype=DTYPE_MASK,
                    **kwa):
        return um.mask_edges(self._mask_default(),\
            width=width, edge_rows=edge_rows, edge_cols=edge_cols, dtype=dtype, **kwa)

    def _mask_center(self,
                     wcenter=0,
                     center_rows=1,
                     center_cols=1,
                     dtype=DTYPE_MASK,
                     **kwa):
        """
        Parameters
        ----------
        center_rows: int number of edge rows to mask for all ASICs
        center_cols: int number of edge columns to mask for all ASICs
        dtype: numpy dtype of the output array
        **kwa: is not used
        Returns
        -------
        mask: np.ndarray, ndim=3, shaped as full detector data, mask of the panel and asic edges
        """
        logger.debug('epix_base._mask_edges')
        mask1 = self._seg_geo.pixel_mask_array(width=0, edge_rows=0, edge_cols=0,\
                  center_rows=center_rows, center_cols=center_cols, dtype=dtype, **kwa)
        nsegs = self._number_of_segments_total()
        if is_none(nsegs, '_number_of_segments_total is None'): return None
        return np.stack([mask1 for i in range(nsegs)])

    def _mask_comb(self,
                   status=True,
                   neighbors=False,
                   edges=False,
                   center=False,
                   calib=False,
                   umask=None,
                   dtype=DTYPE_MASK,
                   **kwa):
        """Returns combined mask controlled by the keyword arguments.
           Parameters
           ----------
           - status   : bool : True  - mask from pixel_status constants,
                                       kwa: status_bits=0xffff - status bits to use in mask.
                                       Status bits show why pixel is considered as bad.
                                       Content of the bitword depends on detector and code version.
                                       It is wise to exclude pixels with any bad status by setting status_bits=0xffff.
                                       kwa: gain_range_inds=(0,1,2,3,4) - list of gain range indexes to merge for epix10ka or jungfrau
           - neighbor : bool : False - mask of neighbors of all bad pixels,
                                       kwa: rad=5 - radial parameter of masked region
                                       kwa: ptrn='r'-rhombus, 'c'-circle, othervise square region around each bad pixel
           - edges    : bool : False - mask edge rows and columns of each panel,
                                       kwa: width=0 or edge_rows=1, edge_cols=1 - number of masked edge rows, columns
           - center   : bool : False - mask center rows and columns of each panel consisting of ASICS (cspad, epix, jungfrau),
                                       kwa: wcenter=0 or center_rows=1, center_cols=1 -
                                       number of masked center rows and columns in the segment,
                                       works for cspad2x1, epix100, epix10ka, jungfrau panels
           - calib    : bool : False - apply user's defined mask from pixel_mask constants
           - umask  : np.array: None - apply user's defined mask from input parameters (shaped as data)

           Returns
           -------
           np.array: dtype=np.uint8, shape as det.raw - mask array of 1 or 0 or None if all switches are False.
        """

        mask = None
        if status:
            status_bits = kwa.get('status_bits', 0xffff)
            gain_range_inds = kwa.get('gain_range_inds',
                                      (0, 1, 2, 3, 4))  # works for epix10ka
            mask = self._mask_from_status(status_bits=0xffff,
                                          gain_range_inds=gain_range_inds,
                                          dtype=dtype)


#        if unbond and (self.is_cspad2x2() or self.is_cspad()):
#            mask_unbond = self.mask_geo(par, width=0, wcenter=0, mbits=4) # mbits=4 - unbonded pixels for cspad2x1 segments
#            mask = mask_unbond if mask is None else um.merge_masks(mask, mask_unbond)

        if neighbors and mask is not None:
            rad = kwa.get('rad', 5)
            ptrn = kwa.get('ptrn', 'r')
            mask = um.mask_neighbors(mask, rad=rad, ptrn=ptrn)

        if edges:
            width = kwa.get('width', 0)
            erows = kwa.get('edge_rows', 1)
            ecols = kwa.get('edge_cols', 1)
            mask_edges = self._mask_edges(
                width=width, edge_rows=erows, edge_cols=ecols,
                dtype=dtype)  # masks each segment edges only
            mask = mask_edges if mask is None else um.merge_masks(
                mask, mask_edges, dtype=dtype)

        if center:
            wcent = kwa.get('wcenter', 0)
            crows = kwa.get('center_rows', 1)
            ccols = kwa.get('center_cols', 1)
            mask_center = self._mask_center(wcenter=wcent,
                                            center_rows=crows,
                                            center_cols=ccols,
                                            dtype=dtype)
            mask = mask_center if mask is None else um.merge_masks(
                mask, mask_center, dtype=dtype)

        if calib:
            mask_calib = self._mask_calib_or_default(dtype=dtype)
            mask = mask_calib if mask is None else um.merge_masks(
                mask, mask_calib, dtype=dtype)

        if umask is not None:
            mask = umask if mask is None else um.merge_masks(
                mask, umask, dtype=dtype)

        return mask

    def _mask(self,
              status=True,
              neighbors=False,
              edges=False,
              center=False,
              calib=False,
              umask=None,
              force_update=False,
              dtype=DTYPE_MASK,
              **kwa):
        """returns cached mask.
        """
        if self._mask_ is None or force_update:
            self._mask_ = self._mask_comb(status=status,
                                          neighbors=neighbors,
                                          edges=edges,
                                          center=center,
                                          calib=calib,
                                          umask=umask,
                                          dtype=dtype,
                                          **kwa)
        return self._mask_