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 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'
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
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'
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()
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 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)
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)
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)
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)
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
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")
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
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
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
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):
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
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
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")
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):