Example #1
0
    def test_pnccd_vs_geometry_access(self):

        # ---- get the geometry Mikhail-style
        try:
            from PSCalib.GeometryAccess import GeometryAccess
            ga = GeometryAccess('ref_files/pnccd.data')
            xyz_old = ga.get_pixel_coords()

        except:
            # if that don't work, load a pre-saved answer
            print 'could not use GeometryAccess, loading saved xyz'
            xyz_old = np.load('ref_files/pnccd_saved.npy')

        xyz_old = np.rollaxis(np.array(xyz_old), 0, 6)  # send 0 --> end
        xyz_old = np.squeeze(xyz_old)

        geom = camera.CompoundAreaCamera.from_psana_file(
            'ref_files/pnccd.data')
        xyz_new = np.squeeze(geom.xyz)

        assert xyz_new.shape == xyz_old.shape, 'shape mismatch'

        err = np.sum(np.abs(xyz_new - xyz_old)) / float(
            np.product(xyz_new.shape))
        print 'Mean Absolute Error: %f um / px' % err
        num_more_than_1px_err = np.sum(np.abs(xyz_new - xyz_old) > 75.0)

        assert err < 10.0, 'error greater than 10um avg per px (%f)' % err
        assert num_more_than_1px_err < 7500, '>7500 pix w err > 1 px'
Example #2
0
    def test_xyz_vs_old_implementation(self):

        # ---- get the geometry Mikhail-style
        try:
            from PSCalib.GeometryAccess import GeometryAccess
            ga = GeometryAccess('ref_files/refgeom_psana.data')
            xyz_old = ga.get_pixel_coords()

        except:
            # if that don't work, load a pre-saved answer
            print 'could not use GeometryAccess, loading saved xyz'
            xyz_old = np.load('ref_files/GA_saved_1-end.npy')

        # some np-foo to move the 3-d x,y,z axis from first dim to last
        xyz_old = np.rollaxis(np.array(xyz_old), 0, 7)  # send 0 --> 7
        xyz_old = np.squeeze(xyz_old)

        geom = camera.CompoundCamera.from_psana_file(
            'ref_files/refgeom_psana.data')
        xyz_new = np.squeeze(geom.xyz)

        assert xyz_new.shape == xyz_old.shape, 'shape mismatch'

        err = np.sum(np.abs(xyz_new - xyz_old)) / float(
            np.product(xyz_new.shape))
        print 'Mean Absolute Error: %f um / px' % err
        num_more_than_1px_err = np.sum(np.abs(xyz_new - xyz_old) > 109.92)

        assert err < 10.0, 'error greater than 10um avg per px (%f)' % err
        assert num_more_than_1px_err < 7500, '>7500 pix w err > 1 px'
Example #3
0
def data_geo(ntest) :
    """Returns test data numpy array and geometry object
    """
    from time import time
    from PSCalib.NDArrIO import save_txt, load_txt
    from PSCalib.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_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 #4
0
 def test_xyz_vs_old_implementation(self):
 
     # ---- get the geometry Mikhail-style
     try:
         from PSCalib.GeometryAccess import GeometryAccess
         ga = GeometryAccess('ref_files/refgeom_psana.data')
         xyz_old = ga.get_pixel_coords()
             
     except:
         # if that don't work, load a pre-saved answer
         print('could not use GeometryAccess, loading saved xyz')
         xyz_old = np.load('ref_files/GA_saved_1-end.npy')
     
     # some np-foo to move the 3-d x,y,z axis from first dim to last
     xyz_old = np.rollaxis(np.array(xyz_old), 0, 7) # send 0 --> 7
     xyz_old = np.squeeze(xyz_old)
 
     geom = camera.CompoundCamera.from_psana_file('ref_files/refgeom_psana.data')
     xyz_new = np.squeeze(geom.xyz)
 
     assert xyz_new.shape == xyz_old.shape, 'shape mismatch'
 
     err = np.sum( np.abs(xyz_new - xyz_old) ) / float(np.product(xyz_new.shape))
     print('Mean Absolute Error: %f um / px' % err)
     num_more_than_1px_err = np.sum( np.abs(xyz_new - xyz_old) > 109.92 )
 
     assert err < 10.0, 'error greater than 10um avg per px (%f)' % err
     assert num_more_than_1px_err < 7500, '>7500 pix w err > 1 px'
Example #5
0
    def test_pnccd_vs_geometry_access(self):

        # ---- get the geometry Mikhail-style
        try:
            from PSCalib.GeometryAccess import GeometryAccess
            ga = GeometryAccess('ref_files/pnccd.data')
            xyz_old = ga.get_pixel_coords()

        except:
            # if that don't work, load a pre-saved answer
            print('could not use GeometryAccess, loading saved xyz')
            xyz_old = np.load('ref_files/pnccd_saved.npy')

        xyz_old = np.rollaxis(np.array(xyz_old), 0, 6) # send 0 --> end
        xyz_old = np.squeeze(xyz_old)

        geom = camera.CompoundAreaCamera.from_psana_file('ref_files/pnccd.data')
        xyz_new = np.squeeze(geom.xyz)

        assert xyz_new.shape == xyz_old.shape, 'shape mismatch'

        err = np.sum( np.abs(xyz_new - xyz_old) ) / float(np.product(xyz_new.shape))
        print('Mean Absolute Error: %f um / px' % err)
        num_more_than_1px_err = np.sum( np.abs(xyz_new - xyz_old) > 75.0 )

        assert err < 10.0, 'error greater than 10um avg per px (%f)' % err
        assert num_more_than_1px_err < 7500, '>7500 pix w err > 1 px'
    def __init__(self, geom_file):
        """
        Initialize instance of Detector class.
        
        :param geom_file: path to *-end.data geometry file
        """

        from PSCalib.GeometryAccess import GeometryAccess
        self._geometry = GeometryAccess(geom_file, 0)
        self._compute_pixel_index_map()
Example #7
0
 def setupRadialBackground(self):
     self.findPsanaGeometry()
     if self.calibFile is not None:
         self.geo = GeometryAccess(self.calibPath+'/'+self.calibFile)
         self.xarr, self.yarr, self.zarr = self.geo.get_pixel_coords()
         self.iX, self.iY = self.geo.get_pixel_coord_indexes()
         self.mask = self.geo.get_pixel_mask(mbits=0377)  # mask for 2x1 edges, two central columns, and unbound pixels with their neighbours
         self.rb = RadialBkgd(self.xarr, self.yarr, mask=self.mask, radedges=None, nradbins=100, phiedges=(0, 360), nphibins=1)
     else:
         self.rb = None
Example #8
0
    def on_but_roi_convert(self):

        self.setStatus(1, 'Convert image to ndarray')

        mcbits = self.sensor_mask_cbits.value()
        gfname = self.fname_geometry.value()
        ifname = self.fname_roi_mask_img.value()
        ofname = self.fname_roi_mask_nda.value()
        tfname = self.fname_roi_mask_nda_tst.value()

        msg = '\n  Convert ROI mask image: %s\n      to ndarray: %s\n    using geometry: %s' % \
             ( ifname, ofname, gfname )
        logger.info(msg, __name__)

        geometry = GeometryAccess(gfname, 0)
        iX, iY = geometry.get_pixel_coord_indexes()
        msg = 'Pixel index array iX, iY shapes: %s,  %s' % (str(
            iX.shape), str(iY.shape))
        logger.info(msg, __name__)

        ifext = os.path.splitext(ifname)[1]
        ofext = os.path.splitext(ofname)[1]

        mask_roi = np.load(ifname) if ifext == '.npy' else np.loadtxt(
            ifname, dtype=np.uint16)

        mask_nda = np.array([mask_roi[r, c]
                             for r, c in zip(iX, iY)])  # 155 msec
        if mcbits: mask_nda *= geometry.get_pixel_mask(mbits=mcbits)

        img_mask_test = img_from_pixel_arrays(iX, iY, W=mask_nda)

        if ofext == '.npy': np.save(ofname, mask_nda)
        else:
            mask_nda.shape = [iX.size / iX.shape[-1], iX.shape[-1]]
            logger.info(
                'Mask ndarray is re-shape for saving in txt to 2-d: %s' %
                str(mask_nda.shape), __name__)
            np.savetxt(ofname, mask_nda, fmt='%d', delimiter=' ')

        logger.info('Mask ndarray is saved in the file %s' % ofname, __name__)

        self.setStatus(1, 'Test: reconstruct image from mask ndarray...')

        tfext = os.path.splitext(tfname)[1]

        if tfext == '.npy': np.save(tfname, img_mask_test)
        else: np.savetxt(tfname, img_mask_test, fmt='%d', delimiter=' ')
        logger.info(
            'Test-image generated from mask ndarray is saved in file %s' %
            tfname, __name__)

        self.setStatus(0)
Example #9
0
    def on_but_roi_convert (self):

        self.setStatus(1, 'Convert image to ndarray')

        mcbits = self.sensor_mask_cbits.value() 
        gfname = self.fname_geometry.value()
        ifname = self.fname_roi_mask_img.value()
        ofname = self.fname_roi_mask_nda.value()
        tfname = self.fname_roi_mask_nda_tst.value()
        
        msg = '\n  Convert ROI mask image: %s\n      to ndarray: %s\n    using geometry: %s' % \
             ( ifname, ofname, gfname )
        logger.info(msg, __name__)

        geometry = GeometryAccess(gfname, 0)
        iX, iY = geometry.get_pixel_coord_indexes()
        msg = 'Pixel index array iX, iY shapes: %s,  %s' % (str(iX.shape), str(iY.shape))
        logger.info(msg, __name__)

        ifext = os.path.splitext(ifname)[1]
        ofext = os.path.splitext(ofname)[1]

        mask_roi = np.load(ifname) if ifext == '.npy' else np.loadtxt(ifname, dtype=np.uint16)

        mask_nda = np.array( [mask_roi[r,c] for r,c in zip(iX, iY)] ) # 155 msec
        if mcbits : mask_nda *= geometry.get_pixel_mask(mbits=mcbits)

        img_mask_test = img_from_pixel_arrays(iX, iY, W=mask_nda) 

        if ofext == '.npy' : np.save(ofname, mask_nda)
        else               :
            mask_nda.shape = [iX.size/iX.shape[-1],iX.shape[-1]]
            logger.info('Mask ndarray is re-shape for saving in txt to 2-d: %s' % str(mask_nda.shape),  __name__) 
            np.savetxt(ofname, mask_nda, fmt='%d', delimiter=' ')

        logger.info('Mask ndarray is saved in the file %s' % ofname, __name__)

        self.setStatus(1, 'Test: reconstruct image from mask ndarray...')

        tfext = os.path.splitext(tfname)[1]
        
        if tfext == '.npy' : np.save(tfname, img_mask_test)
        else               : np.savetxt(tfname, img_mask_test, fmt='%d', delimiter=' ')
        logger.info('Test-image generated from mask ndarray is saved in file %s' % tfname, __name__)

        self.setStatus(0)
Example #10
0
 def setupRadialBackground(self):
     self.findPsanaGeometry()
     if self.calibFile is not None:
         self.geo = GeometryAccess(self.calibPath+'/'+self.calibFile)
         self.xarr, self.yarr, self.zarr = self.geo.get_pixel_coords()
         self.iX, self.iY = self.geo.get_pixel_coord_indexes()
         self.mask = self.geo.get_pixel_mask(mbits=0377)  # mask for 2x1 edges, two central columns, and unbound pixels with their neighbours
         self.rb = RadialBkgd(self.xarr, self.yarr, mask=self.mask, radedges=None, nradbins=100, phiedges=(0, 360), nphibins=1)
     else:
         self.rb = None
Example #11
0
    def reco_image_from_ndarray (self, gfname, afname):

        #mcbits = self.sensor_mask_cbits.value() 

        msg = 'Reconstruct image from\n      geometry: %s\n     and ndarray: %s' % \
             ( gfname, afname )
        logger.info(msg, __name__)

        geometry = GeometryAccess(gfname, 0)
        iX, iY = geometry.get_pixel_coord_indexes()

        afext = '' if afname is None else os.path.splitext(afname)[1]

        nda = np.ones(iX.shape, dtype=np.uint16) if afname is None else \
              np.load(afname) if afext == '.npy' else \
              np.loadtxt(afname) #, dtype=np.uint16)
        nda.shape = iX.shape 

        #if mcbits :  nda *= geometry.get_pixel_mask(mbits=mcbits)
 
        return img_from_pixel_arrays(iX, iY, W=nda) 
Example #12
0
    def reco_image_from_ndarray(self, gfname, afname):

        #mcbits = self.sensor_mask_cbits.value()

        msg = 'Reconstruct image from\n      geometry: %s\n     and ndarray: %s' % \
             ( gfname, afname )
        logger.info(msg, __name__)

        geometry = GeometryAccess(gfname, 0)
        iX, iY = geometry.get_pixel_coord_indexes()

        afext = '' if afname is None else os.path.splitext(afname)[1]

        nda = np.ones(iX.shape, dtype=np.uint16) if afname is None else \
              np.load(afname) if afext == '.npy' else \
              np.loadtxt(afname) #, dtype=np.uint16)
        nda.shape = iX.shape

        #if mcbits :  nda *= geometry.get_pixel_mask(mbits=mcbits)

        return img_from_pixel_arrays(iX, iY, W=nda)
Example #13
0
def data_geo(ntest):
    """Returns test data numpy array and geometry object
    """
    from time import time
    from PSCalib.NDArrIO import save_txt, load_txt
    from PSCalib.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_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 #14
0
def read_slac_metrology(path = None, geometry = None, plot=False, include_asic_offset=False):
  if path is None and geometry is None:
    raise Sorry("Need to provide a geometry object or a path to a geometry file")

  if path is not None and geometry is not None:
    raise Sorry("Cannot provide a geometry object and a geometry file. Ambiguous")

  if geometry is None:
    try:
      from PSCalib.GeometryAccess import GeometryAccess
      geometry = GeometryAccess(path)
    except Exception, e:
      raise Sorry("Can't parse this metrology file")
Example #15
0
class psanaWhisperer():
    def __init__(self,
                 experimentName,
                 runNumber,
                 detInfo,
                 clen='',
                 aduPerPhoton=1,
                 localCalib=False):
        self.experimentName = experimentName
        self.runNumber = runNumber
        self.detInfo = detInfo
        self.clenStr = clen
        self.aduPerPhoton = aduPerPhoton
        self.localCalib = localCalib

    def setupExperiment(self):
        self.ds = psana.DataSource('exp=' + str(self.experimentName) +
                                   ':run=' + str(self.runNumber) + ':idx')
        self.run = self.ds.runs().next()
        self.times = self.run.times()
        self.eventTotal = len(self.times)
        self.env = self.ds.env()
        self.evt = self.run.event(self.times[0])
        self.det = psana.Detector(str(self.detInfo), self.env)
        self.det.do_reshape_2d_to_3d(flag=True)
        self.getDetInfoList()
        self.detAlias = self.getDetectorAlias(str(self.detInfo))
        self.updateClen()  # Get epics variable, clen

    def updateClen(self):
        if 'cspad' in self.detInfo.lower() and 'cxi' in self.experimentName:
            self.epics = self.ds.env().epicsStore()
            self.clen = self.epics.value(self.clenStr)
        elif 'rayonix' in self.detInfo.lower(
        ) and 'mfx' in self.experimentName:
            self.epics = self.ds.env().epicsStore()
            self.clen = self.epics.value(self.clenStr)
        elif 'rayonix' in self.detInfo.lower(
        ) and 'xpp' in self.experimentName:
            self.epics = self.ds.env().epicsStore()
            self.clen = self.epics.value(self.clenStr)

    def getDetectorAlias(self, srcOrAlias):
        for i in self.detInfoList:
            src, alias, _ = i
            if srcOrAlias.lower() == src.lower() or srcOrAlias.lower(
            ) == alias.lower():
                return alias

    def getDetInfoList(self):
        myAreaDetectors = []
        self.detnames = psana.DetNames()
        for k in self.detnames:
            try:
                if Detector.PyDetector.dettype(str(
                        k[0]), self.env) == Detector.AreaDetector.AreaDetector:
                    myAreaDetectors.append(k)
            except ValueError:
                continue
        self.detInfoList = list(set(myAreaDetectors))

    def getEvent(self, number):
        self.evt = self.run.event(self.times[number])

    def getImg(self, number):
        self.getEvent(number)
        img = self.det.image(self.evt, self.det.calib(self.evt))
        return img

    def getImg(self):
        if self.evt is not None:
            img = self.det.image(self.evt, self.det.calib(self.evt))
            return img
        return None

    def getCheetahImg(self, calib=None):
        """Converts seg, row, col assuming (32,185,388)
           to cheetah 2-d table row and col (8*185, 4*388)
        """
        if 'cspad2x2' in self.detInfo.lower():
            print "Not implemented yet: cspad2x2"
        elif 'cspad' in self.detInfo.lower():
            if calib is None: calib = self.det.calib(self.evt)  # (32,185,388)
            img = np.zeros((8 * 185, 4 * 388))
            counter = 0
            for quad in range(4):
                for seg in range(8):
                    img[seg * 185:(seg + 1) * 185,
                        quad * 388:(quad + 1) * 388] = calib[counter, :, :]
                    counter += 1
        elif 'rayonix' in self.detInfo.lower():
            if calib is None:
                img = np.squeeze(self.det.calib(self.evt))  # (1920,1920)
            else:
                img = np.squeeze(calib)
        return img

    def getCleanAssembledImg(self, backgroundEvent):
        """Returns psana assembled image
        """
        backgroundEvt = self.run.event(self.times[backgroundEvent])
        backgroundCalib = self.det.calib(backgroundEvt)
        calib = self.det.calib(self.evt)
        cleanCalib = calib - backgroundCalib
        img = self.det.image(self.evt, cleanCalib)
        return img

    def getAssembledImg(self):
        """Returns psana assembled image
        """
        img = self.det.image(self.evt)
        return img

    def getCalibImg(self):
        """Returns psana assembled image
        """
        img = self.det.calib(self.evt)
        return img

    def getCleanAssembledPhotons(self, backgroundEvent):
        """Returns psana assembled image in photon counts
        """
        backgroundEvt = self.run.event(self.times[backgroundEvent])
        backgroundCalib = self.det.calib(backgroundEvt)
        calib = self.det.calib(self.evt)
        cleanCalib = calib - backgroundCalib
        img = self.det.photons(self.evt,
                               nda_calib=cleanCalib,
                               adu_per_photon=self.aduPerPhoton)
        phot = self.det.image(self.evt, img)
        return phot

    def getAssembledPhotons(self):
        """Returns psana assembled image in photon counts
        """
        img = self.det.photons(self.evt, adu_per_photon=self.aduPerPhoton)
        phot = self.det.image(self.evt, img)
        return phot

    def getPsanaEvent(self, cheetahFilename):
        # Gets psana event given cheetahFilename, e.g. LCLS_2015_Jul26_r0014_035035_e820.h5
        hrsMinSec = cheetahFilename.split('_')[-2]
        fid = int(cheetahFilename.split('_')[-1].split('.')[0], 16)
        for t in self.times:
            if t.fiducial() == fid:
                localtime = time.strftime('%H:%M:%S',
                                          time.localtime(t.seconds()))
                localtime = localtime.replace(':', '')
                if localtime[0:3] == hrsMinSec[0:3]:
                    self.evt = self.run.event(t)
                else:
                    self.evt = None

    def getStartTime(self):
        self.evt = self.run.event(self.times[0])
        evtId = self.evt.get(psana.EventId)
        sec = evtId.time()[0]
        nsec = evtId.time()[1]
        fid = evtId.fiducials()
        return time.strftime('%FT%H:%M:%S-0800',
                             time.localtime(sec))  # Hard-coded pacific time

    #####################################################################
    # TODO: Functions below are not being used yet
    #####################################################################
    def findPsanaGeometry(self):
        try:
            self.source = psana.Detector.PyDetector.map_alias_to_source(
                self.detInfo, self.ds.env())  # 'DetInfo(CxiDs2.0:Cspad.0)'
            self.calibSource = self.source.split('(')[-1].split(')')[
                0]  # 'CxiDs2.0:Cspad.0'
            self.detectorType = gu.det_type_from_source(self.source)  # 1
            self.calibGroup = gu.dic_det_type_to_calib_group[
                self.detectorType]  # 'CsPad::CalibV1'
            self.detectorName = gu.dic_det_type_to_name[
                self.detectorType].upper()  # 'CSPAD'
            if self.localCalib:
                self.calibPath = "./calib/" + self.calibGroup + "/" + self.calibSource + "/geometry"
            else:
                self.calibPath = "/reg/d/psdm/" + self.parent.experimentName[0:3] + \
                                 "/" + self.parent.experimentName + "/calib/" + \
                                 self.calibGroup + "/" + self.calibSource + "/geometry"

            # Determine which calib file to use
            geometryFiles = os.listdir(self.calibPath)
            self.calibFile = None
            minDiff = -1e6
            for fname in geometryFiles:
                if fname.endswith('.data'):
                    endValid = False
                    startNum = int(fname.split('-')[0])
                    endNum = fname.split('-')[-1].split('.data')[0]
                    diff = startNum - self.parent.runNumber
                    # Make sure it's end number is valid too
                    if 'end' in endNum:
                        endValid = True
                    else:
                        try:
                            if self.parent.runNumber <= int(endNum):
                                endValid = True
                        except:
                            continue
                    if diff <= 0 and diff > minDiff and endValid is True:
                        minDiff = diff
                        self.calibFile = fname
        except:
            if self.parent.args.v >= 1: print "Couldn't find psana geometry"
            self.calibFile = None

    def setupRadialBackground(self):
        self.findPsanaGeometry()
        if self.calibFile is not None:
            self.geo = GeometryAccess(self.calibPath + '/' + self.calibFile)
            self.xarr, self.yarr, self.zarr = self.geo.get_pixel_coords()
            self.iX, self.iY = self.geo.get_pixel_coord_indexes()
            self.mask = self.geo.get_pixel_mask(
                mbits=0377
            )  # mask for 2x1 edges, two central columns, and unbound pixels with their neighbours
            self.rb = RadialBkgd(self.xarr,
                                 self.yarr,
                                 mask=self.mask,
                                 radedges=None,
                                 nradbins=100,
                                 phiedges=(0, 360),
                                 nphibins=1)
        else:
            self.rb = None

    def updatePolarizationFactor(self, detectorDistance_in_m):
        if self.rb is not None:
            self.pf = polarization_factor(self.rb.pixel_rad(),
                                          self.rb.pixel_phi(),
                                          detectorDistance_in_m *
                                          1e6)  # convert to um

    def getCalib(self, evtNumber):
        if self.run is not None:
            self.evt = self.getEvent(evtNumber)
            if self.applyCommonMode:  # play with different common mode
                if self.commonMode[0] == 5:  # Algorithm 5
                    calib = self.det.calib(self.evt,
                                           cmpars=(self.commonMode[0],
                                                   self.commonMode[1]))
                else:  # Algorithms 1 to 4
                    print "### Overriding common mode: ", self.commonMode
                    calib = self.det.calib(
                        self.evt,
                        cmpars=(self.commonMode[0], self.commonMode[1],
                                self.commonMode[2], self.commonMode[3]))
            else:
                calib = self.det.calib(self.evt)
            return calib
        else:
            return None

    def getPreprocessedImage(self, evtNumber, image_property):
        disp_medianCorrection = 19
        disp_radialCorrection = 18
        disp_gainMask = 17
        disp_coordy = 16
        disp_coordx = 15
        disp_col = 14
        disp_row = 13
        disp_seg = 12
        disp_quad = 11
        disp_gain = 10
        disp_commonMode = 9
        disp_rms = 8
        disp_status = 7
        disp_pedestal = 6
        disp_photons = 5
        disp_raw = 4
        disp_pedestalCorrected = 3
        disp_commonModeCorrected = 2
        disp_adu = 1

        if image_property == disp_medianCorrection:  # median subtraction
            print "Sorry, this feature isn't available yet"
        elif image_property == disp_radialCorrection:  # radial subtraction + polarization corrected
            self.getEvent(evtNumber)
            calib = self.getCalib(evtNumber)
            if calib:
                self.pf.shape = self.parent.calib.shape
                calib = self.rb.subtract_bkgd(calib * self.pf)
        elif image_property == disp_adu:  # gain and hybrid gain corrected
            calib = self.getCalib(evtNumber)
        elif image_property == disp_commonModeCorrected:  # common mode corrected
            calib = self.getCommonModeCorrected(evtNumber)
        elif image_property == disp_pedestalCorrected:  # pedestal corrected
            calib = self.det.raw(self.evt).astype('float32')
            if calib: calib -= self.det.pedestals(self.evt)
        elif image_property == disp_raw:  # raw
            calib = self.det.raw(self.evt)
        elif image_property == disp_photons:  # photon counts
            calib = self.det.photons(
                self.evt,
                mask=self.parent.mk.userMask,
                adu_per_photon=self.parent.exp.aduPerPhoton)
            if calib is None:
                calib = np.zeros_like(self.parent.exp.detGuaranteed,
                                      dtype='int32')
        elif image_property == disp_pedestal:  # pedestal
            calib = self.parent.det.pedestals(self.parent.evt)
        elif image_property == disp_status:  # status
            calib = self.parent.det.status(self.parent.evt)
        elif image_property == disp_rms:  # rms
            calib = self.parent.det.rms(self.parent.evt)
        elif image_property == disp_commonMode:  # common mode
            calib = self.getCommonMode(evtNumber)
        elif image_property == disp_gain:  # gain
            calib = self.parent.det.gain(self.parent.evt)
        elif image_property == disp_gainMask:  # gain_mask
            calib = self.parent.det.gain_mask(self.parent.evt)
        elif image_property == disp_coordx:  # coords_x
            calib = self.parent.det.coords_x(self.parent.evt)
        elif image_property == disp_coordy:  # coords_y
            calib = self.parent.det.coords_y(self.parent.evt)

        shape = self.parent.det.shape(self.parent.evt)
        if len(shape) == 3:
            if image_property == disp_quad:  # quad ind
                calib = np.zeros(shape)
                for i in range(shape[0]):
                    # FIXME: handle detectors properly
                    if shape[0] == 32:  # cspad
                        calib[i, :, :] = int(i) % 8
                    elif shape[0] == 2:  # cspad2x2
                        calib[i, :, :] = int(i) % 2
                    elif shape[0] == 4:  # pnccd
                        calib[i, :, :] = int(i) % 4
            elif image_property == disp_seg:  # seg ind
                calib = np.zeros(shape)
                if shape[0] == 32:  # cspad
                    for i in range(32):
                        calib[i, :, :] = int(i) / 8
                elif shape[0] == 2:  # cspad2x2
                    for i in range(2):
                        calib[i, :, :] = int(i)
                elif shape[0] == 4:  # pnccd
                    for i in range(4):
                        calib[i, :, :] = int(i)
            elif image_property == disp_row:  # row ind
                calib = np.zeros(shape)
                if shape[0] == 32:  # cspad
                    for i in range(185):
                        calib[:, i, :] = i
                elif shape[0] == 2:  # cspad2x2
                    for i in range(185):
                        calib[:, i, :] = i
                elif shape[0] == 4:  # pnccd
                    for i in range(512):
                        calib[:, i, :] = i
            elif image_property == disp_col:  # col ind
                calib = np.zeros(shape)
                if shape[0] == 32:  # cspad
                    for i in range(388):
                        calib[:, :, i] = i
                elif shape[0] == 2:  # cspad2x2
                    for i in range(388):
                        calib[:, :, i] = i
                elif shape[0] == 4:  # pnccd
                    for i in range(512):
                        calib[:, :, i] = i
Example #16
0
class psanaWhisperer():
    def __init__(self, experimentName, runNumber, detInfo, clen='', aduPerPhoton=1, localCalib=False):
        self.experimentName = experimentName
        self.runNumber = runNumber
        self.detInfo = detInfo
        self.clenStr = clen
        self.aduPerPhoton = aduPerPhoton
        self.localCalib = localCalib

    def setupExperiment(self):
        self.ds = psana.DataSource('exp=' + str(self.experimentName) + ':run=' + str(self.runNumber) + ':idx')
        self.run = self.ds.runs().next()
        self.times = self.run.times()
        self.eventTotal = len(self.times)
        self.env = self.ds.env()
        self.evt = self.run.event(self.times[0])
        self.det = psana.Detector(str(self.detInfo), self.env)
        self.det.do_reshape_2d_to_3d(flag=True)
        self.getDetInfoList()
        self.detAlias = self.getDetectorAlias(str(self.detInfo))
        self.updateClen() # Get epics variable, clen

    def updateClen(self):
        if 'cspad' in self.detInfo.lower() and 'cxi' in self.experimentName:
            self.epics = self.ds.env().epicsStore()
            self.clen = self.epics.value(self.clenStr)
        elif 'rayonix' in self.detInfo.lower() and 'mfx' in self.experimentName:
            self.epics = self.ds.env().epicsStore()
            self.clen = self.epics.value(self.clenStr)
        elif 'rayonix' in self.detInfo.lower() and 'xpp' in self.experimentName:
            self.epics = self.ds.env().epicsStore()
            self.clen = self.epics.value(self.clenStr)

    def getDetectorAlias(self, srcOrAlias):
        for i in self.detInfoList:
            src, alias, _ = i
            if srcOrAlias.lower() == src.lower() or srcOrAlias.lower() == alias.lower():
                return alias

    def getDetInfoList(self):
        myAreaDetectors = []
        self.detnames = psana.DetNames()
        for k in self.detnames:
            try:
                if Detector.PyDetector.dettype(str(k[0]), self.env) == Detector.AreaDetector.AreaDetector:
                    myAreaDetectors.append(k)
            except ValueError:
                continue
        self.detInfoList = list(set(myAreaDetectors))

    def getEvent(self, number):
        self.evt = self.run.event(self.times[number])

    def getImg(self, number):
        self.getEvent(number)
        img = self.det.image(self.evt, self.det.calib(self.evt))
        return img

    def getImg(self):
        if self.evt is not None:
            img = self.det.image(self.evt, self.det.calib(self.evt))
            return img
        return None

    def getCheetahImg(self, calib=None):
        """Converts seg, row, col assuming (32,185,388)
           to cheetah 2-d table row and col (8*185, 4*388)
        """
        if 'cspad2x2' in self.detInfo.lower():
            print "Not implemented yet: cspad2x2"
        elif 'cspad' in self.detInfo.lower():
            if calib is None: calib = self.det.calib(self.evt) # (32,185,388)
            img = np.zeros((8 * 185, 4 * 388))
            counter = 0
            for quad in range(4):
                for seg in range(8):
                    img[seg * 185:(seg + 1) * 185, quad * 388:(quad + 1) * 388] = calib[counter, :, :]
                    counter += 1
        elif 'rayonix' in self.detInfo.lower():
            if calib is None:
                img = np.squeeze(self.det.calib(self.evt))  # (1920,1920)
            else:
                img = np.squeeze(calib)
        return img

    def getCleanAssembledImg(self, backgroundEvent):
        """Returns psana assembled image
        """
        backgroundEvt = self.run.event(self.times[backgroundEvent])
        backgroundCalib = self.det.calib(backgroundEvt)
        calib = self.det.calib(self.evt)
        cleanCalib = calib - backgroundCalib
        img = self.det.image(self.evt, cleanCalib)
        return img

    def getAssembledImg(self):
        """Returns psana assembled image
        """
        img = self.det.image(self.evt)
        return img

    def getCalibImg(self):
        """Returns psana assembled image
        """
        img = self.det.calib(self.evt)
        return img

    def getCleanAssembledPhotons(self, backgroundEvent):
        """Returns psana assembled image in photon counts
        """
        backgroundEvt = self.run.event(self.times[backgroundEvent])
        backgroundCalib = self.det.calib(backgroundEvt)
        calib = self.det.calib(self.evt)
        cleanCalib = calib - backgroundCalib
        img = self.det.photons(self.evt, nda_calib=cleanCalib, adu_per_photon=self.aduPerPhoton)
        phot = self.det.image(self.evt, img)
        return phot

    def getAssembledPhotons(self):
        """Returns psana assembled image in photon counts
        """
        img = self.det.photons(self.evt, adu_per_photon=self.aduPerPhoton)
        phot = self.det.image(self.evt, img)
        return phot

    def getPsanaEvent(self, cheetahFilename):
        # Gets psana event given cheetahFilename, e.g. LCLS_2015_Jul26_r0014_035035_e820.h5
        hrsMinSec = cheetahFilename.split('_')[-2]
        fid = int(cheetahFilename.split('_')[-1].split('.')[0], 16)
        for t in self.times:
            if t.fiducial() == fid:
                localtime = time.strftime('%H:%M:%S', time.localtime(t.seconds()))
                localtime = localtime.replace(':', '')
                if localtime[0:3] == hrsMinSec[0:3]:
                    self.evt = self.run.event(t)
                else:
                    self.evt = None

    def getStartTime(self):
        self.evt = self.run.event(self.times[0])
        evtId = self.evt.get(psana.EventId)
        sec = evtId.time()[0]
        nsec = evtId.time()[1]
        fid = evtId.fiducials()
        return time.strftime('%FT%H:%M:%S-0800', time.localtime(sec))  # Hard-coded pacific time

    #####################################################################
    # TODO: Functions below are not being used yet
    #####################################################################
    def findPsanaGeometry(self):
        try:
            self.source = psana.Detector.PyDetector.map_alias_to_source(self.detInfo,
                                                                  self.ds.env())  # 'DetInfo(CxiDs2.0:Cspad.0)'
            self.calibSource = self.source.split('(')[-1].split(')')[0]  # 'CxiDs2.0:Cspad.0'
            self.detectorType = gu.det_type_from_source(self.source)  # 1
            self.calibGroup = gu.dic_det_type_to_calib_group[self.detectorType]  # 'CsPad::CalibV1'
            self.detectorName = gu.dic_det_type_to_name[self.detectorType].upper()  # 'CSPAD'
            if self.localCalib:
                self.calibPath = "./calib/" + self.calibGroup + "/" + self.calibSource + "/geometry"
            else:
                self.calibPath = "/reg/d/psdm/" + self.parent.experimentName[0:3] + \
                                 "/" + self.parent.experimentName + "/calib/" + \
                                 self.calibGroup + "/" + self.calibSource + "/geometry"

            # Determine which calib file to use
            geometryFiles = os.listdir(self.calibPath)
            self.calibFile = None
            minDiff = -1e6
            for fname in geometryFiles:
                if fname.endswith('.data'):
                    endValid = False
                    startNum = int(fname.split('-')[0])
                    endNum = fname.split('-')[-1].split('.data')[0]
                    diff = startNum - self.parent.runNumber
                    # Make sure it's end number is valid too
                    if 'end' in endNum:
                        endValid = True
                    else:
                        try:
                            if self.parent.runNumber <= int(endNum):
                                endValid = True
                        except:
                            continue
                    if diff <= 0 and diff > minDiff and endValid is True:
                        minDiff = diff
                        self.calibFile = fname
        except:
            if self.parent.args.v >= 1: print "Couldn't find psana geometry"
            self.calibFile = None

    def setupRadialBackground(self):
        self.findPsanaGeometry()
        if self.calibFile is not None:
            self.geo = GeometryAccess(self.calibPath+'/'+self.calibFile)
            self.xarr, self.yarr, self.zarr = self.geo.get_pixel_coords()
            self.iX, self.iY = self.geo.get_pixel_coord_indexes()
            self.mask = self.geo.get_pixel_mask(mbits=0377)  # mask for 2x1 edges, two central columns, and unbound pixels with their neighbours
            self.rb = RadialBkgd(self.xarr, self.yarr, mask=self.mask, radedges=None, nradbins=100, phiedges=(0, 360), nphibins=1)
        else:
            self.rb = None

    def updatePolarizationFactor(self, detectorDistance_in_m):
        if self.rb is not None:
            self.pf = polarization_factor(self.rb.pixel_rad(), self.rb.pixel_phi(), detectorDistance_in_m*1e6) # convert to um

    def getCalib(self, evtNumber):
        if self.run is not None:
            self.evt = self.getEvent(evtNumber)
            if self.applyCommonMode: # play with different common mode
                if self.commonMode[0] == 5: # Algorithm 5
                    calib = self.det.calib(self.evt,cmpars=(self.commonMode[0], self.commonMode[1]))
                else: # Algorithms 1 to 4
                    print "### Overriding common mode: ", self.commonMode
                    calib = self.det.calib(self.evt,cmpars=(self.commonMode[0], self.commonMode[1],
                                                          self.commonMode[2], self.commonMode[3]))
            else:
                calib = self.det.calib(self.evt)
            return calib
        else:
            return None

    def getPreprocessedImage(self, evtNumber, image_property):
        disp_medianCorrection = 19
        disp_radialCorrection = 18
        disp_gainMask = 17
        disp_coordy= 16
        disp_coordx= 15
        disp_col= 14
        disp_row= 13
        disp_seg= 12
        disp_quad= 11
        disp_gain= 10
        disp_commonMode= 9
        disp_rms= 8
        disp_status= 7
        disp_pedestal= 6
        disp_photons= 5
        disp_raw= 4
        disp_pedestalCorrected= 3
        disp_commonModeCorrected= 2
        disp_adu= 1

        if image_property == disp_medianCorrection:  # median subtraction
            print "Sorry, this feature isn't available yet"
        elif image_property == disp_radialCorrection:  # radial subtraction + polarization corrected
            self.getEvent(evtNumber)
            calib = self.getCalib(evtNumber)
            if calib:
                self.pf.shape = self.parent.calib.shape
                calib = self.rb.subtract_bkgd(calib * self.pf)
        elif image_property == disp_adu:  # gain and hybrid gain corrected
            calib = self.getCalib(evtNumber)
        elif image_property == disp_commonModeCorrected:  # common mode corrected
            calib = self.getCommonModeCorrected(evtNumber)
        elif image_property == disp_pedestalCorrected:  # pedestal corrected
            calib = self.det.raw(self.evt).astype('float32')
            if calib: calib -= self.det.pedestals(self.evt)
        elif image_property == disp_raw:  # raw
            calib = self.det.raw(self.evt)
        elif image_property == disp_photons:  # photon counts
            calib = self.det.photons(self.evt, mask=self.parent.mk.userMask,
                                            adu_per_photon=self.parent.exp.aduPerPhoton)
            if calib is None:
                calib = np.zeros_like(self.parent.exp.detGuaranteed, dtype='int32')
        elif image_property == disp_pedestal:  # pedestal
            calib = self.parent.det.pedestals(self.parent.evt)
        elif image_property == disp_status:  # status
            calib = self.parent.det.status(self.parent.evt)
        elif image_property == disp_rms:  # rms
            calib = self.parent.det.rms(self.parent.evt)
        elif image_property == disp_commonMode:  # common mode
            calib = self.getCommonMode(evtNumber)
        elif image_property == disp_gain:  # gain
            calib = self.parent.det.gain(self.parent.evt)
        elif image_property == disp_gainMask:  # gain_mask
            calib = self.parent.det.gain_mask(self.parent.evt)
        elif image_property == disp_coordx:  # coords_x
            calib = self.parent.det.coords_x(self.parent.evt)
        elif image_property == disp_coordy:  # coords_y
            calib = self.parent.det.coords_y(self.parent.evt)

        shape = self.parent.det.shape(self.parent.evt)
        if len(shape) == 3:
            if image_property == disp_quad:  # quad ind
                calib = np.zeros(shape)
                for i in range(shape[0]):
                    # FIXME: handle detectors properly
                    if shape[0] == 32:  # cspad
                        calib[i, :, :] = int(i) % 8
                    elif shape[0] == 2:  # cspad2x2
                        calib[i, :, :] = int(i) % 2
                    elif shape[0] == 4:  # pnccd
                        calib[i, :, :] = int(i) % 4
            elif image_property == disp_seg:  # seg ind
                calib = np.zeros(shape)
                if shape[0] == 32:  # cspad
                    for i in range(32):
                        calib[i, :, :] = int(i) / 8
                elif shape[0] == 2:  # cspad2x2
                    for i in range(2):
                        calib[i, :, :] = int(i)
                elif shape[0] == 4:  # pnccd
                    for i in range(4):
                        calib[i, :, :] = int(i)
            elif image_property == disp_row:  # row ind
                calib = np.zeros(shape)
                if shape[0] == 32:  # cspad
                    for i in range(185):
                        calib[:, i, :] = i
                elif shape[0] == 2:  # cspad2x2
                    for i in range(185):
                        calib[:, i, :] = i
                elif shape[0] == 4:  # pnccd
                    for i in range(512):
                        calib[:, i, :] = i
            elif image_property == disp_col:  # col ind
                calib = np.zeros(shape)
                if shape[0] == 32:  # cspad
                    for i in range(388):
                        calib[:, :, i] = i
                elif shape[0] == 2:  # cspad2x2
                    for i in range(388):
                        calib[:, :, i] = i
                elif shape[0] == 4:  # pnccd
                    for i in range(512):
                        calib[:, :, i] = i
Example #17
0
    def initialize(self, geom, run_num=0):
        """
        Initialize the detector as pnccd
        :param geom: The pnccd .data file which characterize the geometry profile.
        :param run_num: The run_num containing the background, rms and gain and the other
                        pixel pixel properties.
        :return:  None
        """

        # Redirect the output stream
        old_stdout = sys.stdout
        f = six.StringIO()
        # f = open('Detector_initialization.log', 'w')
        sys.stdout = f

        ###########################################################################################
        # Initialize the geometry configuration
        ############################################################################################
        self.geometry = GeometryAccess(geom, 0o377)
        self.run_num = run_num

        # Set coordinate in real space
        temp = self.geometry.get_pixel_coords()
        temp_index = self.geometry.get_pixel_coord_indexes()

        self.panel_num = temp[0].shape[1] * temp[0].shape[2]
        self.distance = temp[2][0, 0, 0, 0, 0] * 1e-6  # Convert to m

        self.pixel_position = np.zeros(
            (self.panel_num, temp[0].shape[3], temp[0].shape[4], 3))
        self.pixel_index_map = np.zeros(
            (self.panel_num, temp[0].shape[3], temp[0].shape[4], 2))

        for l in range(temp[0].shape[1]):
            for m in range(temp[0].shape[2]):
                for n in range(3):
                    self.pixel_position[m + l * temp[0].shape[2], :, :,
                                        n] = temp[n][0, l, m]
                for n in range(2):
                    self.pixel_index_map[m + l * temp[0].shape[2], :, :,
                                         n] = temp_index[n][0, l, m]

        self.pixel_index_map = self.pixel_index_map.astype(np.int64)

        # Get the range of the pixel index
        self.detector_pixel_num_x = np.max(self.pixel_index_map[:, :, :,
                                                                0]) + 1
        self.detector_pixel_num_y = np.max(self.pixel_index_map[:, :, :,
                                                                1]) + 1

        self.panel_pixel_num_x = np.array([
            self.pixel_index_map.shape[1],
        ] * self.panel_num)
        self.panel_pixel_num_y = np.array([
            self.pixel_index_map.shape[2],
        ] * self.panel_num)
        self.pixel_num_total = np.sum(
            np.multiply(self.panel_pixel_num_x, self.panel_pixel_num_y))

        tmp = float(self.geometry.get_pixel_scale_size() *
                    1e-6)  # Convert to m
        self.pixel_width = np.ones((self.panel_num, self.panel_pixel_num_x[0],
                                    self.panel_pixel_num_y[0])) * tmp
        self.pixel_height = np.ones((self.panel_num, self.panel_pixel_num_x[0],
                                     self.panel_pixel_num_y[0])) * tmp

        # Calculate the pixel area
        self.pixel_area = np.multiply(self.pixel_height, self.pixel_width)

        ###########################################################################################
        # Initialize the pixel effects
        ###########################################################################################
        # first we should parse the path
        parsed_path = geom.split('/')
        source = parsed_path[-3]

        if six.PY2:
            cbase = CalibParsBasePnccdV1()
            calibdir = '/'.join(parsed_path[:-4])
            group = parsed_path[-4]
            pbits = 255
            gcp = GenericCalibPars(cbase, calibdir, group, source, run_num,
                                   pbits)

            self._pedestals = gcp.pedestals()
            self._pixel_rms = gcp.pixel_rms()
            self._pixel_mask = gcp.pixel_mask()
            self._pixel_bkgd = gcp.pixel_bkgd()
            self._pixel_status = gcp.pixel_status()
            self._pixel_gain = gcp.pixel_gain()
        else:
            self.det = "pnccd_000" + source[-1]
            self.exp = parsed_path[-5]

            self._pedestals = None
            self._pixel_rms = None
            self._pixel_mask = None
            self._pixel_bkgd = None
            self._pixel_status = None
            self._pixel_gain = None

        # Redirect the output stream
        sys.stdout = old_stdout
Example #18
0
class PnccdDetector(DetectorBase):
    """
    Class for lcls detectors.
    """
    def __init__(self, geom, beam, run_num=0):
        """
        Initialize a pnccd detector.

        :param geom: The path to the geometry .data file.
        :param beam: The beam object.
        :param run_num: The run_num containing the background, rms and gain and the other pixel
        pixel properties.
        """
        super(PnccdDetector, self).__init__()

        # Parse the path to extract the necessary information to use psana modules
        parsed_path = geom.split('/')
        # Notify the user that the path should be as deep as the geometry profile
        if parsed_path[-2] != "geometry":
            # print parsed_path[-1]
            raise Exception(
                " Sorry, at present, the package is not very smart. Please specify "
                +
                "the path of the detector as deep as the geometry profile. \n "
                + "And example would be like:" +
                "/reg/d/psdm/amo/experiment_name/calib/group/source/geometry/0-end.data \n"
                +
                "where the '/calib/group/source/geometry/0-end.data' part is essential. \n"
                +
                "The address before that part is not essential and can be replaced with"
                + " your absolute address or relative address.\n"
                "The experiment_name is also essential in Python 3.")

        self.initialize(geom=geom, run_num=run_num)

        # Initialize the pixel effects
        self.initialize_pixels_with_beam(beam=beam)

    def initialize(self, geom, run_num=0):
        """
        Initialize the detector as pnccd
        :param geom: The pnccd .data file which characterize the geometry profile.
        :param run_num: The run_num containing the background, rms and gain and the other
                        pixel pixel properties.
        :return:  None
        """

        # Redirect the output stream
        old_stdout = sys.stdout
        f = six.StringIO()
        # f = open('Detector_initialization.log', 'w')
        sys.stdout = f

        ###########################################################################################
        # Initialize the geometry configuration
        ############################################################################################
        self.geometry = GeometryAccess(geom, 0o377)
        self.run_num = run_num

        # Set coordinate in real space
        temp = self.geometry.get_pixel_coords()
        temp_index = self.geometry.get_pixel_coord_indexes()

        self.panel_num = temp[0].shape[1] * temp[0].shape[2]
        self.distance = temp[2][0, 0, 0, 0, 0] * 1e-6  # Convert to m

        self.pixel_position = np.zeros(
            (self.panel_num, temp[0].shape[3], temp[0].shape[4], 3))
        self.pixel_index_map = np.zeros(
            (self.panel_num, temp[0].shape[3], temp[0].shape[4], 2))

        for l in range(temp[0].shape[1]):
            for m in range(temp[0].shape[2]):
                for n in range(3):
                    self.pixel_position[m + l * temp[0].shape[2], :, :,
                                        n] = temp[n][0, l, m]
                for n in range(2):
                    self.pixel_index_map[m + l * temp[0].shape[2], :, :,
                                         n] = temp_index[n][0, l, m]

        self.pixel_index_map = self.pixel_index_map.astype(np.int64)

        # Get the range of the pixel index
        self.detector_pixel_num_x = np.max(self.pixel_index_map[:, :, :,
                                                                0]) + 1
        self.detector_pixel_num_y = np.max(self.pixel_index_map[:, :, :,
                                                                1]) + 1

        self.panel_pixel_num_x = np.array([
            self.pixel_index_map.shape[1],
        ] * self.panel_num)
        self.panel_pixel_num_y = np.array([
            self.pixel_index_map.shape[2],
        ] * self.panel_num)
        self.pixel_num_total = np.sum(
            np.multiply(self.panel_pixel_num_x, self.panel_pixel_num_y))

        tmp = float(self.geometry.get_pixel_scale_size() *
                    1e-6)  # Convert to m
        self.pixel_width = np.ones((self.panel_num, self.panel_pixel_num_x[0],
                                    self.panel_pixel_num_y[0])) * tmp
        self.pixel_height = np.ones((self.panel_num, self.panel_pixel_num_x[0],
                                     self.panel_pixel_num_y[0])) * tmp

        # Calculate the pixel area
        self.pixel_area = np.multiply(self.pixel_height, self.pixel_width)

        ###########################################################################################
        # Initialize the pixel effects
        ###########################################################################################
        # first we should parse the path
        parsed_path = geom.split('/')
        source = parsed_path[-3]

        if six.PY2:
            cbase = CalibParsBasePnccdV1()
            calibdir = '/'.join(parsed_path[:-4])
            group = parsed_path[-4]
            pbits = 255
            gcp = GenericCalibPars(cbase, calibdir, group, source, run_num,
                                   pbits)

            self._pedestals = gcp.pedestals()
            self._pixel_rms = gcp.pixel_rms()
            self._pixel_mask = gcp.pixel_mask()
            self._pixel_bkgd = gcp.pixel_bkgd()
            self._pixel_status = gcp.pixel_status()
            self._pixel_gain = gcp.pixel_gain()
        else:
            self.det = "pnccd_000" + source[-1]
            self.exp = parsed_path[-5]

            self._pedestals = None
            self._pixel_rms = None
            self._pixel_mask = None
            self._pixel_bkgd = None
            self._pixel_status = None
            self._pixel_gain = None

        # Redirect the output stream
        sys.stdout = old_stdout
        # f.close()
        # os.remove('./Detector_initialization.log')

    @property
    def pedestals(self):
        if six.PY3 and not self._pedestals:
            self._pedestals = calib_constants(self.det,
                                              exp=self.exp,
                                              ctype="pedestals",
                                              run=self.run_num)[0]
        return self._pedestals

    @property
    def pixel_rms(self):
        if six.PY3 and not self._pixel_rms:
            self._pixel_rms = calib_constants(self.det,
                                              exp=self.exp,
                                              ctype="pixel_rms",
                                              run=self.run_num)[0]
        return self._pixel_rms

    @property
    def pixel_mask(self):
        if six.PY3 and not self._pixel_mask:
            self._pixel_mask = calib_constants(self.det,
                                               exp=self.exp,
                                               ctype="pixel_mask",
                                               run=self.run_num)[0]
        return self._pixel_mask

    @property
    def pixel_bkgd(self):
        if six.PY3 and not self._pixel_bkgd:
            self._pixel_bkgd = calib_constants(self.det,
                                               exp=self.exp,
                                               ctype="pixel_bkgd",
                                               run=self.run_num)[0]
        return self._pixel_bkgd

    @property
    def pixel_status(self):
        if six.PY3 and not self._pixel_status:
            self._pixel_status = calib_constants(self.det,
                                                 exp=self.exp,
                                                 ctype="pixel_status",
                                                 run=self.run_num)[0]
        return self._pixel_status

    @property
    def pixel_gain(self):
        if six.PY3 and not self._pixel_gain:
            self._pixel_gain = calib_constants(self.det,
                                               exp=self.exp,
                                               ctype="pixel_gain",
                                               run=self.run_num)[0]
        return self._pixel_gain

    def assemble_image_stack(self, image_stack):
        """
        Assemble the image stack into a 2D diffraction pattern.
        For this specific object, since it only has one panel, the result is to remove the
        first dimension.

        :param image_stack: The [1, num_x, num_y] numpy array.
        :return: The [num_x, num_y] numpy array.
        """
        # construct the image holder:
        image = np.zeros(
            (self.detector_pixel_num_x, self.detector_pixel_num_y))
        for l in range(self.panel_num):
            image[self.pixel_index_map[l, :, :, 0],
                  self.pixel_index_map[l, :, :, 1]] = image_stack[l, :, :]

        return image

    def assemble_image_stack_batch(self, image_stack_batch):
        """
        Assemble the image stack batch into a stack of 2D diffraction patterns.
        For this specific object, since it has only one panel, the result is a simple reshape.

        :param image_stack_batch: The [stack_num, 1, num_x, num_y] numpy array
        :return: The [stack_num, num_x, num_y] numpy array
        """
        stack_num = image_stack_batch.shape[0]

        # construct the image holder:
        image = np.zeros(
            (stack_num, self.detector_pixel_num_x, self.detector_pixel_num_y))
        for l in range(self.panel_num):
            idx_map_1 = self.pixel_index_map[l, :, :, 0]
            idx_map_2 = self.pixel_index_map[l, :, :, 1]
            image[:, idx_map_1, idx_map_2] = image_stack_batch[:, l]

        return image
    except IOError:
      # See if it's a json file
      from dxtbx.model.experiment_list import ExperimentListFactory
      try:
        experiments = ExperimentListFactory.from_json_file(params.metrology)
        assert len(experiments) == 1
        detector = experiments[0].detector
      except Exception as e:
        detector = None

    if detector is None:
      # see if it's a SLAC geometry file
      from scitbx import matrix
      try:
        from PSCalib.GeometryAccess import GeometryAccess
        geometry = GeometryAccess(params.metrology)
      except Exception as e:
        geometry = None

      if geometry is None:
        # see if this is a Ginn metrology file (see Helen Ginn et. al. (2015), Acta D Cryst)
        panels = []
        for line in open(params.metrology).readlines():
          if len(line) > 0 and line[0] != '#' and len(line.strip().split()) == 10:
            panels.append(line.strip())

        if len(panels) != 64:
          raise Sorry("Can't parse this metrology file")

        print "Ginn metrology file"
        for panel_id, panel in enumerate(panels):
Example #20
0
    def initialize(self, geom, run_num=0, cframe=0):
        """
        Initialize the detector
        :param geom: The *-end.data file which characterizes the geometry profile.
        :param run_num: The run_num containing the background, rms and gain and the other
                        pixel pixel properties.
        :param cframe: The desired coordinate frame, 0 for psana and 1 for lab conventions.
        :return:  None
        """
        # Redirect the output stream
        old_stdout = sys.stdout
        f = six.StringIO()
        # f = open('Detector_initialization.log', 'w')
        sys.stdout = f

        ###########################################################################################
        # Initialize the geometry configuration
        ############################################################################################
        self.geometry = GeometryAccess(geom, cframe=cframe)
        self.run_num = run_num

        # Set coordinate in real space (convert to m)
        temp = [
            xp.asarray(t) * 1e-6
            for t in self.geometry.get_pixel_coords(cframe=cframe)
        ]
        temp_index = [
            xp.asarray(t)
            for t in self.geometry.get_pixel_coord_indexes(cframe=cframe)
        ]

        self.panel_num = np.prod(temp[0].shape[:-2])
        self._distance = float(temp[2].mean())

        self._shape = (self.panel_num, temp[0].shape[-2], temp[0].shape[-1])
        self.pixel_position = xp.zeros(self._shape + (3, ))
        self.pixel_index_map = xp.zeros(self._shape + (2, ))

        for n in range(3):
            self.pixel_position[..., n] = temp[n].reshape(self._shape)
        for n in range(2):
            self.pixel_index_map[..., n] = temp_index[n].reshape(self._shape)

        self.pixel_index_map = self.pixel_index_map.astype(xp.int64)

        # Get the range of the pixel index
        self.detector_pixel_num_x = asnumpy(
            xp.max(self.pixel_index_map[..., 0]) + 1)
        self.detector_pixel_num_y = asnumpy(
            xp.max(self.pixel_index_map[..., 1]) + 1)

        self.panel_pixel_num_x = np.array([
            self.pixel_index_map.shape[1],
        ] * self.panel_num)
        self.panel_pixel_num_y = np.array([
            self.pixel_index_map.shape[2],
        ] * self.panel_num)
        self.pixel_num_total = np.sum(
            np.multiply(self.panel_pixel_num_x, self.panel_pixel_num_y))

        tmp = float(self.geometry.get_pixel_scale_size() *
                    1e-6)  # Convert to m
        self.pixel_width = xp.ones((self.panel_num, self.panel_pixel_num_x[0],
                                    self.panel_pixel_num_y[0])) * tmp
        self.pixel_height = xp.ones((self.panel_num, self.panel_pixel_num_x[0],
                                     self.panel_pixel_num_y[0])) * tmp

        # Calculate the pixel area
        self.pixel_area = xp.multiply(self.pixel_height, self.pixel_width)

        ###########################################################################################
        # Initialize the pixel effects
        ###########################################################################################
        # first we should parse the path
        parsed_path = geom.split('/')
        self.exp = parsed_path[-5]
        if self.exp == 'calib':
            self.exp = parsed_path[-6]
        self.group = parsed_path[-4]
        self.source = parsed_path[-3]

        self._pedestals = None
        self._pixel_rms = None
        self._pixel_mask = None
        self._pixel_bkgd = None
        self._pixel_status = None
        self._pixel_gain = None

        if six.PY2:
            try:
                cbase = self._get_cbase()
                self.calibdir = '/'.join(parsed_path[:-4])
                pbits = 255
                gcp = GenericCalibPars(cbase, self.calibdir, self.group,
                                       self.source, run_num, pbits)

                self._pedestals = gcp.pedestals()
                self._pixel_rms = gcp.pixel_rms()
                self._pixel_mask = gcp.pixel_mask()
                self._pixel_bkgd = gcp.pixel_bkgd()
                self._pixel_status = gcp.pixel_status()
                self._pixel_gain = gcp.pixel_gain()
            except NotImplementedError:
                # No GenericCalibPars information.
                pass
        else:
            try:
                self.det = self._get_det_id(self.group)
            except NotImplementedError:
                # No GenericCalibPars information.
                self.det = None

        # Redirect the output stream
        sys.stdout = old_stdout
Example #21
0
class LCLSDetector(DetectorBase):
    """
    Class for LCLS detectors.
    """
    def __init__(self, geom, beam=None, run_num=0, cframe=0):
        """
        Initialize a LCLS detector.

        :param geom: The path to the geometry .data file.
        :param beam: The beam object.
        :param run_num: The run_num containing the background, rms and gain and the other pixel
        pixel properties.
        :param cframe: The desired coordinate frame, 0 for psana and 1 for lab conventions. The
        default (psana) matches the convention of non-LCLS detectors. Lab frame yields the transpose.
        """
        super(LCLSDetector, self).__init__()

        # Parse the path to extract the necessary information to use psana modules
        parsed_path = geom.split('/')
        # Notify the user that the path should be as deep as the geometry profile
        if parsed_path[-2] != "geometry":
            # print parsed_path[-1]
            raise Exception(
                " Sorry, at present, the package is not very smart. Please specify "
                +
                "the path of the detector as deep as the geometry profile. \n "
                + "And example would be like:" +
                "/reg/d/psdm/amo/experiment_name/calib/group/source/geometry/0-end.data \n"
                +
                "where the '/calib/group/source/geometry/0-end.data' part is essential. \n"
                +
                "The address before that part is not essential and can be replaced with"
                + " your absolute address or relative address.\n"
                "The experiment_name is also essential in Python 3.")

        self.initialize(geom=geom, run_num=run_num, cframe=cframe)
        # Initialize the pixel effects, enforcing detector distance to be positive
        if self.distance < 0:
            self.distance *= -1
        self.initialize_pixels_with_beam(beam=beam)

    def initialize(self, geom, run_num=0, cframe=0):
        """
        Initialize the detector
        :param geom: The *-end.data file which characterizes the geometry profile.
        :param run_num: The run_num containing the background, rms and gain and the other
                        pixel pixel properties.
        :param cframe: The desired coordinate frame, 0 for psana and 1 for lab conventions.
        :return:  None
        """
        # Redirect the output stream
        old_stdout = sys.stdout
        f = six.StringIO()
        # f = open('Detector_initialization.log', 'w')
        sys.stdout = f

        ###########################################################################################
        # Initialize the geometry configuration
        ############################################################################################
        self.geometry = GeometryAccess(geom, cframe=cframe)
        self.run_num = run_num

        # Set coordinate in real space (convert to m)
        temp = [
            xp.asarray(t) * 1e-6
            for t in self.geometry.get_pixel_coords(cframe=cframe)
        ]
        temp_index = [
            xp.asarray(t)
            for t in self.geometry.get_pixel_coord_indexes(cframe=cframe)
        ]

        self.panel_num = np.prod(temp[0].shape[:-2])
        self._distance = float(temp[2].mean())

        self._shape = (self.panel_num, temp[0].shape[-2], temp[0].shape[-1])
        self.pixel_position = xp.zeros(self._shape + (3, ))
        self.pixel_index_map = xp.zeros(self._shape + (2, ))

        for n in range(3):
            self.pixel_position[..., n] = temp[n].reshape(self._shape)
        for n in range(2):
            self.pixel_index_map[..., n] = temp_index[n].reshape(self._shape)

        self.pixel_index_map = self.pixel_index_map.astype(xp.int64)

        # Get the range of the pixel index
        self.detector_pixel_num_x = asnumpy(
            xp.max(self.pixel_index_map[..., 0]) + 1)
        self.detector_pixel_num_y = asnumpy(
            xp.max(self.pixel_index_map[..., 1]) + 1)

        self.panel_pixel_num_x = np.array([
            self.pixel_index_map.shape[1],
        ] * self.panel_num)
        self.panel_pixel_num_y = np.array([
            self.pixel_index_map.shape[2],
        ] * self.panel_num)
        self.pixel_num_total = np.sum(
            np.multiply(self.panel_pixel_num_x, self.panel_pixel_num_y))

        tmp = float(self.geometry.get_pixel_scale_size() *
                    1e-6)  # Convert to m
        self.pixel_width = xp.ones((self.panel_num, self.panel_pixel_num_x[0],
                                    self.panel_pixel_num_y[0])) * tmp
        self.pixel_height = xp.ones((self.panel_num, self.panel_pixel_num_x[0],
                                     self.panel_pixel_num_y[0])) * tmp

        # Calculate the pixel area
        self.pixel_area = xp.multiply(self.pixel_height, self.pixel_width)

        ###########################################################################################
        # Initialize the pixel effects
        ###########################################################################################
        # first we should parse the path
        parsed_path = geom.split('/')
        self.exp = parsed_path[-5]
        if self.exp == 'calib':
            self.exp = parsed_path[-6]
        self.group = parsed_path[-4]
        self.source = parsed_path[-3]

        self._pedestals = None
        self._pixel_rms = None
        self._pixel_mask = None
        self._pixel_bkgd = None
        self._pixel_status = None
        self._pixel_gain = None

        if six.PY2:
            try:
                cbase = self._get_cbase()
                self.calibdir = '/'.join(parsed_path[:-4])
                pbits = 255
                gcp = GenericCalibPars(cbase, self.calibdir, self.group,
                                       self.source, run_num, pbits)

                self._pedestals = gcp.pedestals()
                self._pixel_rms = gcp.pixel_rms()
                self._pixel_mask = gcp.pixel_mask()
                self._pixel_bkgd = gcp.pixel_bkgd()
                self._pixel_status = gcp.pixel_status()
                self._pixel_gain = gcp.pixel_gain()
            except NotImplementedError:
                # No GenericCalibPars information.
                pass
        else:
            try:
                self.det = self._get_det_id(self.group)
            except NotImplementedError:
                # No GenericCalibPars information.
                self.det = None

        # Redirect the output stream
        sys.stdout = old_stdout
        # f.close()
        # os.remove('./Detector_initialization.log')

    def _get_cbase(self):
        """Get detector calibration base object.

        Psana 1 only.
        """
        raise NotImplementedError()

    def _get_det_id(self, group):
        """Get detector ID form source.

        Psana 2 only.
        """
        raise NotImplementedError()

    def _get_calib_constants(self, name):
        _name = "_" + name
        attribute = getattr(self, _name)
        if six.PY3 and attribute is None and self.det is not None:
            # We haven't tried to get the calib_constant yet.
            attribute = calib_constants(self.det,
                                        exp=self.exp,
                                        ctype=name,
                                        run=self.run_num)[0]
        if attribute is None:
            # We still don't have it
            raise RuntimeError("No {} available for this detector"
                               "".format(name))
        setattr(self, _name, attribute)
        return attribute

    @property
    def pedestals(self):
        return self._get_calib_constants("pedestals")

    @property
    def pixel_rms(self):
        return self._get_calib_constants("pixel_rms")

    @property
    def pixel_mask(self):
        return self._get_calib_constants("pixel_mask")

    @property
    def pixel_bkgd(self):
        return self._get_calib_constants("pixel_bkgd")

    @property
    def pixel_status(self):
        return self._get_calib_constants("pixel_status")

    @property
    def pixel_gain(self):
        return self._get_calib_constants("pixel_gain")

    @pedestals.setter
    def pedestals(self, value):
        self._pedestals = value

    @pixel_rms.setter
    def pixel_rms(self, value):
        self._pixel_rms = value

    @pixel_mask.setter
    def pixel_mask(self, value):
        self._pixel_mask = value

    @pixel_bkgd.setter
    def pixel_bkgd(self, value):
        self._pixel_bkgd = value

    @pixel_status.setter
    def pixel_status(self, value):
        self._pixel_status = value

    @pixel_gain.setter
    def pixel_gain(self, value):
        self._pixel_gain = value

    def reset_calib(self, run_num):
        """
        Update calibration pixel effects based on new run number.
        """
        old_stdout = sys.stdout
        f = six.StringIO()
        sys.stdout = f

        self.run_num = run_num

        if six.PY2:
            try:
                pbits = 255
                gcp = GenericCalibPars(self._get_cbase(), self.calibdir,
                                       self.group, self.source, self.run_num,
                                       pbits)
                self._pedestals = gcp.pedestals()
                self._pixel_rms = gcp.pixel_rms()
                self._pixel_mask = gcp.pixel_mask()
                self._pixel_bkgd = gcp.pixel_bkgd()
                self._pixel_status = gcp.pixel_status()
                self._pixel_gain = gcp.pixel_gain()
            except NotImplementedError:
                pass
        else:
            self._pedestals = calib_constants(self.det,
                                              exp=self.exp,
                                              ctype='pedestals',
                                              run=self.run_num)[0]
            self._pixel_rms = calib_constants(self.det,
                                              exp=self.exp,
                                              ctype='pixel_rms',
                                              run=self.run_num)[0]
            self._pixel_mask = calib_constants(self.det,
                                               exp=self.exp,
                                               ctype='pixel_mask',
                                               run=self.run_num)[0]
            self._pixel_bkgd = calib_constants(self.det,
                                               exp=self.exp,
                                               ctype='pixel_bkgd',
                                               run=self.run_num)[0]
            self._pixel_status = calib_constants(self.det,
                                                 exp=self.exp,
                                                 ctype='pixel_status',
                                                 run=self.run_num)[0]
            self._pixel_gain = calib_constants(self.det,
                                               exp=self.exp,
                                               ctype='pixel_gain',
                                               run=self.run_num)[0]

        sys.stdout = old_stdout

        return

    ###########################################################################################
    # Functionality for adding dark noise
    ###########################################################################################
    def _calibrate_evt(self, evt):
        """
        Retrieve calibrated data from psana event object. Applied corrections are 
        pedestal, common mode, gain mask, gain, and pixel status mask, performed
        by the psana.Detector class.
    
        :param evt: psana event object
        :return data: calibrated image
        """
        import psana

        # retrieve psana.Source alias
        det_type = self.__class__.__name__.split("Detector")[0].lower()
        alias = None

        for key in evt.keys():
            if det_type in key.alias().lower():
                alias = key.alias()
                break
            else:
                srcname = key.src()
                if srcname.__class__.__name__ == 'DetInfo':
                    if det_type in srcname.devName().lower():
                        alias = str(srcname)
                        break

        # retrieve calibrated shot
        det = psana.Detector(alias)
        return det.calib(evt)

    def _retrieve_batch_evt(self, num_shots):
        """
        Retrieve num_shots patterns from a run of the experiment.
        
        :param num_shots: number of patterns to retrieve
        :return data: array of patterns in shape (num_shots, n_pedestals, ped_x, ped_y)
        """
        # set up psana1 DataSource object
        from psana import DataSource
        ds = DataSource('exp=%s:run=%i' % (self.exp, self.run_num))

        # set up storage array
        if self.pedestals.ndim == 4:
            pshape = self.pedestals.shape[1:]
        else:
            pshape = self.pedestals.shape
        data = np.zeros((num_shots, pshape[0], pshape[1], pshape[2]))

        # retrieve multiple events (shots)
        counter = 0
        for num, evt in enumerate(ds.events()):
            if counter < num_shots:
                data[counter] = np.array(self._calibrate_evt(evt))
                counter += 1
            else:
                break

        # if run is shorter than num_shots, fill in remainder by linear combination
        if counter < num_shots:
            for i in range(counter, num_shots):
                indices = np.random.randint(0, high=counter, size=2)
                weights = np.random.dirichlet(np.ones(2))
                data[i] = weights[0] * data[indices[0]] + weights[1] * data[
                    indices[1]]

        return data

    def _random_dark_index(self):
        """
        Return the run index of random dark run, assuming that the indices of dark
        runs can be inferred from the pedestal nomenclature.
        
        :return dark_idx: index of random dark run, -1 if no dark runs available
        """
        import glob

        # list of available pedestals
        pnames = glob.glob(
            "/reg/d/psdm/%s/%s/calib/%s/%s/pedestals/*-end.data" %
            (self.exp[:3].upper(), self.exp, self.group, self.source))

        # add run indices from pedestals list if associated XTC files exist
        dark_indices = list()
        for pn in pnames:
            temp_str = pn.split("/")[-1]
            temp_idx = int(temp_str.split("-")[0])
            fnames = glob.glob("/reg/d/psdm/%s/%s/xtc/*-r%04d-*.xtc" %
                               (self.exp[:3].upper(), self.exp, temp_idx))
            if len(fnames) > 0:
                dark_indices.append(temp_idx)

        # return random dark run or -1 if none available
        if len(dark_indices) != 0:
            return np.random.choice(np.array(dark_indices))
        else:
            return -1

    def add_dark_noise(self,
                       num_shots,
                       det_shape=True,
                       dark_idx=None,
                       mask_neg=True):
        """
        Retrieve calibrated images from dark runs.
    
        :param num_shots: number of calibrated dark shots to retreive
        :param det_shape: boolean, if True reassemble panels into detector's shape
        :param dark_idx: index of dark run; if None, a run number will be chosen randomly
        :param mask_neg: boolean, if True set negative-valued pixels to zero
        :return dark_data: array of calibrated dark shots with shape 
           (num_shots, det_x, det_y) if det_shape is True 
           (num_shots, n_panels, panel_x, panel_y) if det_shape is False
           None if pedestals and/or XTC files for a dark run are unavailable
        """
        if six.PY3:
            raise NotImplementedError(
                'Currently only implemented for psana2/python3.')
            return

        # grab index of random dark run and reset calibration attributes to match
        if dark_idx == None:
            dark_idx = self._random_dark_index()
            if dark_idx == -1:
                print("Pedestals and/or XTC data are unavailable.")
                return
        self.reset_calib(dark_idx)

        # retrieve dark data
        dark_data = self._retrieve_batch_evt(num_shots)

        # floor: set negative intensities to zero
        if mask_neg:
            dark_data[dark_data < 0] = 0

        # optionally reshape to match detector's shape
        if det_shape:
            dark_data = self.assemble_image_stack_batch(dark_data)

        return dark_data
Example #22
0
class LCLSDetector(DetectorBase):
    """
    Class for lcls detectors.
    """

    def __init__(self, geom, beam=None, run_num=0):
        """
        Initialize a pnccd detector.

        :param geom: The path to the geometry .data file.
        :param beam: The beam object.
        :param run_num: The run_num containing the background, rms and gain and the other pixel
        pixel properties.
        """
        super(LCLSDetector, self).__init__()

        # Parse the path to extract the necessary information to use psana modules
        parsed_path = geom.split('/')
        # Notify the user that the path should be as deep as the geometry profile
        if parsed_path[-2] != "geometry":
            # print parsed_path[-1]
            raise Exception(
                " Sorry, at present, the package is not very smart. Please specify " +

                "the path of the detector as deep as the geometry profile. \n " +
                "And example would be like:" +
                "/reg/d/psdm/amo/experiment_name/calib/group/source/geometry/0-end.data \n" +
                "where the '/calib/group/source/geometry/0-end.data' part is essential. \n" +
                "The address before that part is not essential and can be replaced with" +
                " your absolute address or relative address.\n"
                "The experiment_name is also essential in Python 3.")

        self.initialize(geom=geom, run_num=run_num)
        # Initialize the pixel effects
        self.initialize_pixels_with_beam(beam=beam)

    def initialize(self, geom, run_num=0):
        """
        Initialize the detector as pnccd
        :param geom: The pnccd .data file which characterize the geometry profile.
        :param run_num: The run_num containing the background, rms and gain and the other
                        pixel pixel properties.
        :return:  None
        """
        # Redirect the output stream
        old_stdout = sys.stdout
        f = six.StringIO()
        # f = open('Detector_initialization.log', 'w')
        sys.stdout = f

        ###########################################################################################
        # Initialize the geometry configuration
        ############################################################################################
        self.geometry = GeometryAccess(geom, 0)
        self.run_num = run_num

        # Set coordinate in real space (convert to m)
        temp = [xp.asarray(t) * 1e-6 for t in self.geometry.get_pixel_coords()]
        temp_index = [xp.asarray(t)
                      for t in self.geometry.get_pixel_coord_indexes()]

        self.panel_num = np.prod(temp[0].shape[:-2])
        self._distance = float(temp[2].mean())

        self._shape = (self.panel_num, temp[0].shape[-2], temp[0].shape[-1])
        self.pixel_position = xp.zeros(self._shape + (3,))
        self.pixel_index_map = xp.zeros(self._shape + (2,))

        for n in range(3):
            self.pixel_position[..., n] = temp[n].reshape(self._shape)
        for n in range(2):
            self.pixel_index_map[..., n] = temp_index[n].reshape(self._shape)

        self.pixel_index_map = self.pixel_index_map.astype(xp.int64)

        # Get the range of the pixel index
        self.detector_pixel_num_x = asnumpy(
            xp.max(self.pixel_index_map[..., 0]) + 1)
        self.detector_pixel_num_y = asnumpy(
            xp.max(self.pixel_index_map[..., 1]) + 1)

        self.panel_pixel_num_x = np.array([self.pixel_index_map.shape[1], ] * self.panel_num)
        self.panel_pixel_num_y = np.array([self.pixel_index_map.shape[2], ] * self.panel_num)
        self.pixel_num_total = np.sum(np.multiply(self.panel_pixel_num_x, self.panel_pixel_num_y))

        tmp = float(self.geometry.get_pixel_scale_size() * 1e-6)  # Convert to m
        self.pixel_width = xp.ones(
            (self.panel_num, self.panel_pixel_num_x[0], self.panel_pixel_num_y[0])) * tmp
        self.pixel_height = xp.ones(
            (self.panel_num, self.panel_pixel_num_x[0], self.panel_pixel_num_y[0])) * tmp

        # Calculate the pixel area
        self.pixel_area = xp.multiply(self.pixel_height, self.pixel_width)

        ###########################################################################################
        # Initialize the pixel effects
        ###########################################################################################
        # first we should parse the path
        parsed_path = geom.split('/')
        group = parsed_path[-4]
        source = parsed_path[-3]

        self._pedestals = None
        self._pixel_rms = None
        self._pixel_mask = None
        self._pixel_bkgd = None
        self._pixel_status = None
        self._pixel_gain = None

        if six.PY2:
            try:
                cbase = self._get_cbase()
                calibdir = '/'.join(parsed_path[:-4])
                pbits = 255
                gcp = GenericCalibPars(cbase, calibdir, group, source, run_num, pbits)

                self._pedestals = gcp.pedestals()
                self._pixel_rms = gcp.pixel_rms()
                self._pixel_mask = gcp.pixel_mask()
                self._pixel_bkgd = gcp.pixel_bkgd()
                self._pixel_status = gcp.pixel_status()
                self._pixel_gain = gcp.pixel_gain()
            except NotImplementedError:
                # No GenericCalibPars information.
                pass
        else:
            try:
                self.det = self._get_det_id(source)
            except NotImplementedError:
                # No GenericCalibPars information.
                self.det = None
            self.exp = parsed_path[-5]

        # Redirect the output stream
        sys.stdout = old_stdout
        # f.close()
        # os.remove('./Detector_initialization.log')

    def _get_cbase(self):
        """Get detector calibration base object.

        Psana 1 only.
        """
        raise NotImplementedError()

    def _get_det_id(self, source):
        """Get detector ID form source.

        Psana 2 only.
        """
        raise NotImplementedError()

    def _get_calib_constants(self, name):
        _name = "_" + name
        attribute = getattr(self, _name)
        if six.PY3 and attribute is None and self.det is not None:
            # We haven't tried to get the calib_constant yet.
            attribute = calib_constants(
                self.det, exp=self.exp, ctype=name,
                run=self.run_num)[0]
        if attribute is None:
            # We still don't have it
            raise RuntimeError("No {} available for this detector"
                               "".format(name))
        setattr(self, _name, attribute)
        return attribute

    @property
    def pedestals(self):
        return self._get_calib_constants("pedestals")

    @property
    def pixel_rms(self):
        return self._get_calib_constants("pixel_rms")

    @property
    def pixel_mask(self):
        return self._get_calib_constants("pixel_mask")

    @property
    def pixel_bkgd(self):
        return self._get_calib_constants("pixel_bkgd")

    @property
    def pixel_status(self):
        return self._get_calib_constants("pixel_status")

    @property
    def pixel_gain(self):
        return self._get_calib_constants("pixel_gain")
Example #23
0
def read_slac_metrology(path = None, geometry = None, plot=False, include_asic_offset=False):
  if path is None and geometry is None:
    raise Sorry("Need to provide a geometry object or a path to a geometry file")

  if path is not None and geometry is not None:
    raise Sorry("Cannot provide a geometry object and a geometry file. Ambiguous")

  if geometry is None:
    try:
      from PSCalib.GeometryAccess import GeometryAccess
      geometry = GeometryAccess(path)
    except Exception as e:
      raise Sorry("Can't parse this metrology file")

  metro = {}
  pixel_size = geometry.get_pixel_scale_size()/1000
  null_ori = matrix.col((0,0,1)).axis_and_angle_as_unit_quaternion(0, deg=True)

  # collapse any transformations above those of the quadrants into one X/Y offset,
  # but don't keep Z transformations, as those come from the XTC stream
  root = geometry.get_top_geo()
  root_basis = basis_from_geo(root, use_z=False)
  while len(root.get_list_of_children()) != 4 and len(root.get_list_of_children()) != 32:
    assert len(root.get_list_of_children()) == 1
    root = root.get_list_of_children()[0]
    root_basis *= basis_from_geo(root, use_z=False)

  metro[(0,)] = root_basis

  def add_sensor(quad_id, sensor_id, sensor):
    metro[(0,quad_id,sensor_id)] = basis_from_geo(sensor)

    x, y, z = sensor.get_pixel_coords()
    x/=1000; y/=1000; z/=1000
    assert x.shape == y.shape == z.shape
    sensor_px_slow = x.shape[0]
    sensor_px_fast = x.shape[1]
    assert sensor_px_fast % 2 == 0

    a0ul = sul = matrix.col((x[0,0],y[0,0],z[0,0]))
    a1ur = sur = matrix.col((x[0,sensor_px_fast-1],y[0,sensor_px_fast-1],z[0,sensor_px_fast-1]))
    a1lr = slr = matrix.col((x[sensor_px_slow-1,sensor_px_fast-1],y[sensor_px_slow-1,sensor_px_fast-1],z[sensor_px_slow-1,sensor_px_fast-1]))
    a0ll = sll = matrix.col((x[sensor_px_slow-1,0],y[sensor_px_slow-1,0],z[sensor_px_slow-1,0]))

    a0ur = matrix.col((x[0,sensor_px_fast//2-1],y[0,sensor_px_fast//2-1],z[0,sensor_px_fast//2-1]))
    a0lr = matrix.col((x[sensor_px_slow-1,sensor_px_fast//2-1],y[sensor_px_slow-1,sensor_px_fast//2-1],z[sensor_px_slow-1,sensor_px_fast//2-1]))

    a1ul = matrix.col((x[0,sensor_px_fast//2],y[0,sensor_px_fast//2],z[0,sensor_px_fast//2]))
    a1ll = matrix.col((x[sensor_px_slow-1,sensor_px_fast//2],y[sensor_px_slow-1,sensor_px_fast//2],z[sensor_px_slow-1,sensor_px_fast//2]))

    sensor_center = center([sul,sur,slr,sll])
    asic0_center = center([a0ul,a0ur,a0lr,a0ll])
    asic1_center = center([a1ul,a1ur,a1lr,a1ll])

    asic_trans0 = (asic0_center-sensor_center).length()
    asic_trans1 = (asic1_center-sensor_center).length()

    if include_asic_offset:
      rotated_ori = matrix.col((1,0,0)).axis_and_angle_as_unit_quaternion(180.0, deg=True)
      offset_fast = -pixel_size*((sensor_px_fast) / 4) # 4 because sensor_px_fast is for sensor
      offset_slow = +pixel_size*((sensor_px_slow) / 2) # Sensor is divided into 2 only in fast direction
      metro[(0,quad_id,sensor_id,0)] = basis(orientation=rotated_ori,translation=matrix.col((-asic_trans0,0,0)))
      metro[(0,quad_id,sensor_id,1)] = basis(orientation=rotated_ori,translation=matrix.col((+asic_trans1,0,0)))
      metro[(0,quad_id,sensor_id,0)].translation += matrix.col((offset_fast, offset_slow, 0))
      metro[(0,quad_id,sensor_id,1)].translation += matrix.col((offset_fast, offset_slow, 0))
    else:
      metro[(0,quad_id,sensor_id,0)] = basis(orientation=null_ori,translation=matrix.col((-asic_trans0,0,0)))
      metro[(0,quad_id,sensor_id,1)] = basis(orientation=null_ori,translation=matrix.col((+asic_trans1,0,0)))

  if len(root.get_list_of_children()) == 4:
    for quad_id, quad in enumerate(root.get_list_of_children()):
      metro[(0,quad_id)] = basis_from_geo(quad)
      for sensor_id, sensor in enumerate(quad.get_list_of_children()):
        add_sensor(quad_id, sensor_id, sensor)
  elif len(root.get_list_of_children()) == 32:
    for quad_id in range(4):
      metro[(0,quad_id)] = basis(orientation = null_ori, translation = matrix.col((0,0,0)))
      sensors = root.get_list_of_children()
      for sensor_id in range(8):
        add_sensor(quad_id, sensor_id, sensors[quad_id*4+sensor_id])
  else:
    assert False

  return metro
class Detector:
    """
    Detector object useful for reshaping data from XTC into detector shape. Stripped
    down version of Detector class from pysingfel when that library isn't available.
    """
    def __init__(self, geom_file):
        """
        Initialize instance of Detector class.
        
        :param geom_file: path to *-end.data geometry file
        """

        from PSCalib.GeometryAccess import GeometryAccess
        self._geometry = GeometryAccess(geom_file, 0)
        self._compute_pixel_index_map()

    def _compute_pixel_index_map(self):
        """
        Compute various parameters related to detector dimensions and pixel indices
        and store as class variables.
        """

        # Set coordinate in real space (convert to m)
        temp = [
            np.asarray(t) * 1e-6 for t in self._geometry.get_pixel_coords()
        ]
        temp_index = [
            np.asarray(t) for t in self._geometry.get_pixel_coord_indexes()
        ]
        self._panel_num = np.prod(temp[0].shape[:-2])
        self._shape = (self._panel_num, temp[0].shape[-2], temp[0].shape[-1])

        pixel_index_map = np.zeros(self._shape + (2, ))
        for n in range(2):
            pixel_index_map[..., n] = temp_index[n].reshape(self._shape)
        self._pixel_index_map = pixel_index_map.astype(np.int64)

        self._detector_pixel_num_x = int(
            np.max(self._pixel_index_map[..., 0]) + 1)
        self._detector_pixel_num_y = int(
            np.max(self._pixel_index_map[..., 1]) + 1)

        self._panel_index = np.zeros(
            (self._detector_pixel_num_x, self._detector_pixel_num_y))
        for l in range(self._panel_num):
            self._panel_index[self._pixel_index_map[l, :, :, 0],
                              self._pixel_index_map[l, :, :, 1]] = l + 1

        return

    def assemble_image_stack(self, data, dtype=np.int64):
        """
        Reassemble data retrieved from XTC file into shape of detector.
        
        :param data: list of stacked data from each quad of panels
        :param dtype: type of the returned image
        :return image: data in shape of detector
        """
        data = np.array(data).reshape(self._shape)
        image = np.zeros((self._detector_pixel_num_x,
                          self._detector_pixel_num_y)).astype(dtype)

        for l in range(self._panel_num):
            image[self._pixel_index_map[l, :, :, 0],
                  self._pixel_index_map[l, :, :, 1]] = data[l, :, :]

        return image
  ax = fig.add_subplot(111, aspect='equal')

  if os.path.isfile(params.metrology):
    # Try dxtbx first to see if this is a regular diffraction image
    from dxtbx.format.Registry import Registry
    try:
      reader = Registry.find(params.metrology)
    except IOError:
      reader = None

    if reader is None:
      # see if it's a SLAC geometry file
      from PSCalib.GeometryAccess import GeometryAccess
      from scitbx import matrix
      try:
        geometry = GeometryAccess(params.metrology)
      except Exception, e:
        geometry = None

      if geometry is None:
        # see if this is a Ginn metrology file (see Helen Ginn et. al. (2015), Acta D Cryst)
        panels = []
        for line in open(params.metrology).readlines():
          if len(line) > 0 and line[0] != '#' and len(line.strip().split()) == 10:
            panels.append(line.strip())

        if len(panels) != 64:
          raise Sorry("Can't parse this metrology file")

        print "Ginn metrology file"
        for panel_id, panel in enumerate(panels):