def metrology2phil(calib_dir, verbose): """XXX Should really review the SLAC progress since last time around! XXX Note that this is all SLAC-specific (as is the whole thing, I guess). """ # XXX Can this fail? How? sections = calib2sections(calib_dir) if (sections is None): return (None) return sections2phil(sections, verbose)
def average(argv=None): if argv == None: argv = sys.argv[1:] try: from mpi4py import MPI except ImportError: raise Sorry("MPI not found") command_line = (libtbx.option_parser.option_parser(usage=""" %s [-p] -c config -x experiment -a address -r run -d detz_offset [-o outputdir] [-A averagepath] [-S stddevpath] [-M maxpath] [-n numevents] [-s skipnevents] [-v] [-m] [-b bin_size] [-X override_beam_x] [-Y override_beam_y] [-D xtc_dir] [-f] [-g gain_mask_value] [--min] [--minpath minpath] To write image pickles use -p, otherwise the program writes CSPAD CBFs. Writing CBFs requires the geometry to be already deployed. Examples: cxi.mpi_average -c cxi49812/average.cfg -x cxi49812 -a CxiDs1.0:Cspad.0 -r 25 -d 571 Use one process on the current node to process all the events from run 25 of experiment cxi49812, using a detz_offset of 571. mpirun -n 16 cxi.mpi_average -c cxi49812/average.cfg -x cxi49812 -a CxiDs1.0:Cspad.0 -r 25 -d 571 As above, using 16 cores on the current node. bsub -a mympi -n 100 -o average.out -q psanaq cxi.mpi_average -c cxi49812/average.cfg -x cxi49812 -a CxiDs1.0:Cspad.0 -r 25 -d 571 -o cxi49812 As above, using the psanaq and 100 cores, putting the log in average.out and the output images in the folder cxi49812. """ % libtbx.env.dispatcher_name).option( None, "--as_pickle", "-p", action="store_true", default=False, dest="as_pickle", help="Write results as image pickle files instead of cbf files" ).option( None, "--raw_data", "-R", action="store_true", default=False, dest="raw_data", help= "Disable psana corrections such as dark pedestal subtraction or common mode (cbf only)" ).option( None, "--background_pickle", "-B", default=None, dest="background_pickle", help="" ).option( None, "--config", "-c", type="string", default=None, dest="config", metavar="PATH", help="psana config file" ).option( None, "--experiment", "-x", type="string", default=None, dest="experiment", help="experiment name (eg cxi84914)" ).option( None, "--run", "-r", type="int", default=None, dest="run", help="run number" ).option( None, "--address", "-a", type="string", default="CxiDs2.0:Cspad.0", dest="address", help="detector address name (eg CxiDs2.0:Cspad.0)" ).option( None, "--detz_offset", "-d", type="float", default=None, dest="detz_offset", help= "offset (in mm) from sample interaction region to back of CSPAD detector rail (CXI), or detector distance (XPP)" ).option( None, "--outputdir", "-o", type="string", default=".", dest="outputdir", metavar="PATH", help="Optional path to output directory for output files" ).option( None, "--averagebase", "-A", type="string", default="{experiment!l}_avg-r{run:04d}", dest="averagepath", metavar="PATH", help= "Path to output average image without extension. String substitution allowed" ).option( None, "--stddevbase", "-S", type="string", default="{experiment!l}_stddev-r{run:04d}", dest="stddevpath", metavar="PATH", help= "Path to output standard deviation image without extension. String substitution allowed" ).option( None, "--maxbase", "-M", type="string", default="{experiment!l}_max-r{run:04d}", dest="maxpath", metavar="PATH", help= "Path to output maximum projection image without extension. String substitution allowed" ).option( None, "--numevents", "-n", type="int", default=None, dest="numevents", help="Maximum number of events to process. Default: all" ).option( None, "--skipevents", "-s", type="int", default=0, dest="skipevents", help="Number of events in the beginning of the run to skip. Default: 0" ).option( None, "--verbose", "-v", action="store_true", default=False, dest="verbose", help="Print more information about progress" ).option( None, "--pickle-optical-metrology", "-m", action="store_true", default=False, dest="pickle_optical_metrology", help= "If writing pickle files, use the optical metrology in the experiment's calib directory" ).option( None, "--bin_size", "-b", type="int", default=None, dest="bin_size", help="Rayonix detector bin size" ).option( None, "--override_beam_x", "-X", type="float", default=None, dest="override_beam_x", help="Rayonix detector beam center x coordinate" ).option( None, "--override_beam_y", "-Y", type="float", default=None, dest="override_beam_y", help="Rayonix detector beam center y coordinate" ).option( None, "--calib_dir", "-C", type="string", default=None, dest="calib_dir", metavar="PATH", help="calibration directory" ).option( None, "--pickle_calib_dir", "-P", type="string", default=None, dest="pickle_calib_dir", metavar="PATH", help= "pickle calibration directory specification. Replaces --calib_dir functionality." ).option( None, "--xtc_dir", "-D", type="string", default=None, dest="xtc_dir", metavar="PATH", help="xtc stream directory" ).option( None, "--use_ffb", "-f", action="store_true", default=False, dest="use_ffb", help= "Use the fast feedback filesystem at LCLS. Only for the active experiment!" ).option( None, "--gain_mask_value", "-g", type="float", default=None, dest="gain_mask_value", help= "Ratio between low and high gain pixels, if CSPAD in mixed-gain mode. Only used in CBF averaging mode." ).option( None, "--min", None, action="store_true", default=False, dest="do_minimum_projection", help="Output a minimum projection" ).option( None, "--minpath", None, type="string", default="{experiment!l}_min-r{run:04d}", dest="minpath", metavar="PATH", help= "Path to output minimum image without extension. String substitution allowed" )).process(args=argv) if len(command_line.args) > 0 or \ command_line.options.as_pickle is None or \ command_line.options.experiment is None or \ command_line.options.run is None or \ command_line.options.address is None or \ command_line.options.detz_offset is None or \ command_line.options.averagepath is None or \ command_line.options.stddevpath is None or \ command_line.options.maxpath is None or \ command_line.options.pickle_optical_metrology is None: command_line.parser.show_help() return # set this to sys.maxint to analyze all events if command_line.options.numevents is None: maxevents = sys.maxsize else: maxevents = command_line.options.numevents comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() if command_line.options.config is not None: psana.setConfigFile(command_line.options.config) dataset_name = "exp=%s:run=%d:smd" % (command_line.options.experiment, command_line.options.run) if command_line.options.xtc_dir is not None: if command_line.options.use_ffb: raise Sorry("Cannot specify the xtc_dir and use SLAC's ffb system") dataset_name += ":dir=%s" % command_line.options.xtc_dir elif command_line.options.use_ffb: # as ffb is only at SLAC, ok to hardcode /reg/d here dataset_name += ":dir=/reg/d/ffb/%s/%s/xtc" % ( command_line.options.experiment[0:3], command_line.options.experiment) if command_line.options.calib_dir is not None: psana.setOption('psana.calib-dir', command_line.options.calib_dir) ds = psana.DataSource(dataset_name) address = command_line.options.address src = psana.Source('DetInfo(%s)' % address) nevent = np.array([0.]) if command_line.options.background_pickle is not None: background = easy_pickle.load( command_line.options.background_pickle)['DATA'].as_numpy_array() for run in ds.runs(): runnumber = run.run() if not command_line.options.as_pickle: psana_det = psana.Detector(address, ds.env()) # list of all events if command_line.options.skipevents > 0: print("Skipping first %d events" % command_line.options.skipevents) elif "Rayonix" in command_line.options.address: print("Skipping first image in the Rayonix detector" ) # Shuttering issue command_line.options.skipevents = 1 for i, evt in enumerate(run.events()): if i % size != rank: continue if i < command_line.options.skipevents: continue if i >= maxevents: break if i % 10 == 0: print('Rank', rank, 'processing event', i) #print "Event #",rank*mylength+i," has id:",evt.get(EventId) if 'Rayonix' in command_line.options.address or 'FeeHxSpectrometer' in command_line.options.address or 'XrayTransportDiagnostic' in command_line.options.address: data = evt.get(psana.Camera.FrameV1, src) if data is None: print("No data") continue data = data.data16().astype(np.float64) elif command_line.options.as_pickle: data = evt.get(psana.ndarray_float64_3, src, 'image0') else: # get numpy array, 32x185x388 from xfel.cftbx.detector.cspad_cbf_tbx import get_psana_corrected_data if command_line.options.raw_data: data = get_psana_corrected_data(psana_det, evt, use_default=False, dark=False, common_mode=None, apply_gain_mask=False, per_pixel_gain=False) else: if command_line.options.gain_mask_value is None: data = get_psana_corrected_data(psana_det, evt, use_default=True) else: data = get_psana_corrected_data( psana_det, evt, use_default=False, dark=True, common_mode=None, apply_gain_mask=True, gain_mask_value=command_line.options. gain_mask_value, per_pixel_gain=False) if data is None: print("No data") continue if command_line.options.background_pickle is not None: data -= background if 'FeeHxSpectrometer' in command_line.options.address or 'XrayTransportDiagnostic' in command_line.options.address: distance = np.array([0.0]) wavelength = np.array([1.0]) else: d = cspad_tbx.env_distance(address, run.env(), command_line.options.detz_offset) if d is None: print("No distance, using distance", command_line.options.detz_offset) assert command_line.options.detz_offset is not None if 'distance' not in locals(): distance = np.array([command_line.options.detz_offset]) else: distance += command_line.options.detz_offset else: if 'distance' in locals(): distance += d else: distance = np.array([float(d)]) w = cspad_tbx.evt_wavelength(evt) if w is None: print("No wavelength") if 'wavelength' not in locals(): wavelength = np.array([1.0]) else: if 'wavelength' in locals(): wavelength += w else: wavelength = np.array([w]) t = cspad_tbx.evt_time(evt) if t is None: print("No timestamp, skipping shot") continue if 'timestamp' in locals(): timestamp += t[0] + (t[1] / 1000) else: timestamp = np.array([t[0] + (t[1] / 1000)]) if 'sum' in locals(): sum += data else: sum = np.array(data, copy=True) if 'sumsq' in locals(): sumsq += data * data else: sumsq = data * data if 'maximum' in locals(): maximum = np.maximum(maximum, data) else: maximum = np.array(data, copy=True) if command_line.options.do_minimum_projection: if 'minimum' in locals(): minimum = np.minimum(minimum, data) else: minimum = np.array(data, copy=True) nevent += 1 #sum the images across mpi cores if size > 1: print("Synchronizing rank", rank) totevent = np.zeros(nevent.shape) comm.Reduce(nevent, totevent) if rank == 0 and totevent[0] == 0: raise Sorry("No events found in the run") sumall = np.zeros(sum.shape).astype(sum.dtype) comm.Reduce(sum, sumall) sumsqall = np.zeros(sumsq.shape).astype(sumsq.dtype) comm.Reduce(sumsq, sumsqall) maxall = np.zeros(maximum.shape).astype(maximum.dtype) comm.Reduce(maximum, maxall, op=MPI.MAX) if command_line.options.do_minimum_projection: minall = np.zeros(maximum.shape).astype(minimum.dtype) comm.Reduce(minimum, minall, op=MPI.MIN) waveall = np.zeros(wavelength.shape).astype(wavelength.dtype) comm.Reduce(wavelength, waveall) distall = np.zeros(distance.shape).astype(distance.dtype) comm.Reduce(distance, distall) timeall = np.zeros(timestamp.shape).astype(timestamp.dtype) comm.Reduce(timestamp, timeall) if rank == 0: if size > 1: print("Synchronized") # Accumulating floating-point numbers introduces errors, # which may cause negative variances. Since a two-pass # approach is unacceptable, the standard deviation is # clamped at zero. mean = sumall / float(totevent[0]) variance = (sumsqall / float(totevent[0])) - (mean**2) variance[variance < 0] = 0 stddev = np.sqrt(variance) wavelength = waveall[0] / totevent[0] distance = distall[0] / totevent[0] pixel_size = cspad_tbx.pixel_size saturated_value = cspad_tbx.cspad_saturated_value timestamp = timeall[0] / totevent[0] timestamp = (int(timestamp), timestamp % int(timestamp) * 1000) timestamp = cspad_tbx.evt_timestamp(timestamp) if command_line.options.as_pickle: extension = ".pickle" else: extension = ".cbf" dest_paths = [ cspad_tbx.pathsubst(command_line.options.averagepath + extension, evt, ds.env()), cspad_tbx.pathsubst(command_line.options.stddevpath + extension, evt, ds.env()), cspad_tbx.pathsubst(command_line.options.maxpath + extension, evt, ds.env()) ] if command_line.options.do_minimum_projection: dest_paths.append( cspad_tbx.pathsubst(command_line.options.minpath + extension, evt, ds.env())) dest_paths = [ os.path.join(command_line.options.outputdir, path) for path in dest_paths ] if 'Rayonix' in command_line.options.address: all_data = [mean, stddev, maxall] if command_line.options.do_minimum_projection: all_data.append(minall) from xfel.cxi.cspad_ana import rayonix_tbx pixel_size = rayonix_tbx.get_rayonix_pixel_size( command_line.options.bin_size) beam_center = [ command_line.options.override_beam_x, command_line.options.override_beam_y ] active_areas = flex.int([0, 0, mean.shape[1], mean.shape[0]]) split_address = cspad_tbx.address_split(address) old_style_address = split_address[0] + "-" + split_address[ 1] + "|" + split_address[2] + "-" + split_address[3] for data, path in zip(all_data, dest_paths): print("Saving", path) d = cspad_tbx.dpack( active_areas=active_areas, address=old_style_address, beam_center_x=pixel_size * beam_center[0], beam_center_y=pixel_size * beam_center[1], data=flex.double(data), distance=distance, pixel_size=pixel_size, saturated_value=rayonix_tbx.rayonix_saturated_value, timestamp=timestamp, wavelength=wavelength) easy_pickle.dump(path, d) elif 'FeeHxSpectrometer' in command_line.options.address or 'XrayTransportDiagnostic' in command_line.options.address: all_data = [mean, stddev, maxall] split_address = cspad_tbx.address_split(address) old_style_address = split_address[0] + "-" + split_address[ 1] + "|" + split_address[2] + "-" + split_address[3] if command_line.options.do_minimum_projection: all_data.append(minall) for data, path in zip(all_data, dest_paths): d = cspad_tbx.dpack(address=old_style_address, data=flex.double(data), distance=distance, pixel_size=0.1, timestamp=timestamp, wavelength=wavelength) print("Saving", path) easy_pickle.dump(path, d) elif command_line.options.as_pickle: split_address = cspad_tbx.address_split(address) old_style_address = split_address[0] + "-" + split_address[ 1] + "|" + split_address[2] + "-" + split_address[3] xpp = 'xpp' in address.lower() if xpp: evt_time = cspad_tbx.evt_time( evt) # tuple of seconds, milliseconds timestamp = cspad_tbx.evt_timestamp( evt_time) # human readable format from iotbx.detectors.cspad_detector_formats import detector_format_version, reverse_timestamp from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas version_lookup = detector_format_version( old_style_address, reverse_timestamp(timestamp)[0]) assert version_lookup is not None active_areas = xpp_active_areas[version_lookup]['active_areas'] beam_center = [1765 // 2, 1765 // 2] else: if command_line.options.pickle_calib_dir is not None: metro_path = command_line.options.pickle_calib_dir elif command_line.options.pickle_optical_metrology: from xfel.cftbx.detector.cspad_cbf_tbx import get_calib_file_path metro_path = get_calib_file_path(run.env(), address, run) else: metro_path = libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = parse_calib.calib2sections(metro_path) beam_center, active_areas = cspad_tbx.cbcaa( cspad_tbx.getConfig(address, ds.env()), sections) class fake_quad(object): def __init__(self, q, d): self.q = q self.d = d def quad(self): return self.q def data(self): return self.d if xpp: quads = [ fake_quad(i, mean[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] mean = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads=quads) mean = flex.double(mean.astype(np.float64)) quads = [ fake_quad(i, stddev[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] stddev = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads=quads) stddev = flex.double(stddev.astype(np.float64)) quads = [ fake_quad(i, maxall[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] maxall = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads=quads) maxall = flex.double(maxall.astype(np.float64)) if command_line.options.do_minimum_projection: quads = [ fake_quad(i, minall[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] minall = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads=quads) minall = flex.double(minall.astype(np.float64)) else: quads = [ fake_quad(i, mean[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] mean = cspad_tbx.CsPadDetector(address, evt, ds.env(), sections, quads=quads) mean = flex.double(mean.astype(np.float64)) quads = [ fake_quad(i, stddev[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] stddev = cspad_tbx.CsPadDetector(address, evt, ds.env(), sections, quads=quads) stddev = flex.double(stddev.astype(np.float64)) quads = [ fake_quad(i, maxall[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] maxall = cspad_tbx.CsPadDetector(address, evt, ds.env(), sections, quads=quads) maxall = flex.double(maxall.astype(np.float64)) if command_line.options.do_minimum_projection: quads = [ fake_quad(i, minall[i * 8:(i + 1) * 8, :, :]) for i in range(4) ] minall = cspad_tbx.CsPadDetector(address, evt, ds.env(), sections, quads=quads) minall = flex.double(minall.astype(np.float64)) all_data = [mean, stddev, maxall] if command_line.options.do_minimum_projection: all_data.append(minall) for data, path in zip(all_data, dest_paths): print("Saving", path) d = cspad_tbx.dpack(active_areas=active_areas, address=old_style_address, beam_center_x=pixel_size * beam_center[0], beam_center_y=pixel_size * beam_center[1], data=data, distance=distance, pixel_size=pixel_size, saturated_value=saturated_value, timestamp=timestamp, wavelength=wavelength) easy_pickle.dump(path, d) else: # load a header only cspad cbf from the slac metrology from xfel.cftbx.detector import cspad_cbf_tbx import pycbf base_dxtbx = cspad_cbf_tbx.env_dxtbx_from_slac_metrology( run, address) if base_dxtbx is None: raise Sorry("Couldn't load calibration file for run %d" % run.run()) all_data = [mean, stddev, maxall] if command_line.options.do_minimum_projection: all_data.append(minall) for data, path in zip(all_data, dest_paths): print("Saving", path) cspad_img = cspad_cbf_tbx.format_object_from_data( base_dxtbx, data, distance, wavelength, timestamp, address, round_to_int=False) cspad_img._cbf_handle.write_widefile(path, pycbf.CBF,\ pycbf.MIME_HEADERS|pycbf.MSG_DIGEST|pycbf.PAD_4K, 0)
active_areas = xpp_active_areas[params.metrology]['active_areas'] for asic_number, (y1, x1, y2, x2) in enumerate([(active_areas[(i*4)+0]+1, active_areas[(i*4)+1]+1, active_areas[(i*4)+2]-1, active_areas[(i*4)+3]-1) for i in xrange(len(active_areas)//4)]): ax.add_patch(Rectangle((x1,y1), x2-x1, y2-y1, color="grey")) ax.annotate(asic_number, (x1+(x2-x1)/2,y1+(y2-y1)/2)) ax.set_xlim((0, 2000)) ax.set_ylim((0, 2000)) ax.set_ylim(ax.get_ylim()[::-1]) else: # Read the metrology from an LCLS calibration directory from xfel.cxi.cspad_ana.parse_calib import calib2sections sections = calib2sections(params.metrology) for q_id, quad in enumerate(sections): for s_id, sensor in enumerate(quad): for a_id, asic in enumerate(sensor.corners_asic()): y1, x1, y2, x2 = asic p0 = col((x1,y1)) p1 = col((x2,y1)) p2 = col((x2,y2)) p3 = col((x1,y2)) v1 = p1-p0 v2 = p3-p0 vcen = ((v2/2) + (v1/2)) + p0 ax.add_patch(Polygon((p0[0:2],p1[0:2],p2[0:2],p3[0:2]), closed=True, color='green', fill=False, hatch='/')) ax.annotate(q_id*16+s_id*2+a_id, vcen[0:2])
params = master_phil.fetch(sources=user_phil).extract() assert params.pickle_file is not None if len(params.pickle_file) == 0: master_phil.show() raise Usage( "pickle_file must be defined (either pickle_file=XXX, or the file path(s) alone)." ) assert params.detector is not None assert params.plot is not None if params.old_metrology is None: params.old_metrology = libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") if os.path.isdir(params.old_metrology): sections = calib2sections(params.old_metrology) else: from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas assert params.old_metrology in xpp_active_areas if params.new_metrology is None: params.new_metrology = libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") assert os.path.isdir(params.new_metrology) or os.path.isfile( params.new_metrology) # Read the new metrology, either from a calibration directory or an optical metrology # flat file if os.path.isdir(params.new_metrology): metro_style = "calibdir"
address, timestamp = address_and_timestamp_from_detector_format_version( params.detector_format_version) timestamp = evt_timestamp((timestamp, 0)) raw_data = numpy.zeros((11840, 194)) if params.detector_format_version is not None and "XPP" in params.detector_format_version: from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas active_areas = xpp_active_areas[ params.detector_format_version]['active_areas'] data = flex.int(flex.grid((1765, 1765))) beam_center = (1765 // 2, 1765 // 2) else: if params.optical_metrology_path is None: calib_dir = libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = calib2sections(calib_dir) else: sections = calib2sections(params.optical_metrology_path) asic_start = 0 data3d = [] for i_quad in range(4): asic_size = 185 * 194 section_size = asic_size * 4 quad_start = i_quad * section_size * 4 quad_asics = [] for i_2x2 in range(4): for i_asic in range(2): asic_end = asic_start + 185 a = raw_data[asic_start:asic_end, :] asic_start = asic_end
def convert_detector(raw_data, detector_format_version, address, optical_metrology_path=None): # https://confluence.slac.stanford.edu/display/PCDS/CSPad+metrology+and+calibration+files%2C+links data3d = [] if raw_data.shape == (5920,388): asic_start = 0 if optical_metrology_path is None: calib_dir = libtbx.env.find_in_repositories("xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = parse_calib.calib2sections(calib_dir) else: sections = parse_calib.calib2sections(optical_metrology_path) for i_quad in range(4): asic_size = 185 * 388 section_size = asic_size * 2 quad_start = i_quad * section_size * 4 quad_asics = [] for i_2x2 in range(4): for i_asic in range(2): asic_end = asic_start + 185 quad_asics.append(raw_data[asic_start:asic_end, :]) asic_start = asic_end quad_data = numpy.dstack(quad_asics) quad_data = numpy.rollaxis(quad_data, 2,0) data3d.append(fake_cspad_ElementV2(quad_data, i_quad)) env = fake_env(fake_config()) evt = fake_evt(data3d) return flex.double(cspad_tbx.CsPadDetector(address, evt, env, sections).astype(numpy.float64)), None else: asic_start = 0 if detector_format_version is not None and 'XPP' in detector_format_version: from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas rotations = xpp_active_areas[detector_format_version]['rotations'] active_areas = xpp_active_areas[detector_format_version]['active_areas'] det = flex.double([0]*(1765*1765)) det.reshape(flex.grid((1765,1765))) for i in xrange(64): row = active_areas[i*4] col = active_areas[i*4 + 1] block = flex.double(raw_data[i * 185:(i+1)*185, :]) det.matrix_paste_block_in_place(block.matrix_rot90(rotations[i]), row, col) return det, active_areas else: if optical_metrology_path is None: calib_dir = libtbx.env.find_in_repositories("xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = parse_calib.calib2sections(calib_dir) else: sections = parse_calib.calib2sections(optical_metrology_path) for i_quad in range(4): asic_size = 185 * 194 section_size = asic_size * 4 quad_start = i_quad * section_size * 4 quad_asics = [] for i_2x2 in range(4): for i_asic in range(2): asic_end = asic_start + 185 a = raw_data[asic_start:asic_end, :] asic_start = asic_end asic_end = asic_start + 185 b = raw_data[asic_start:asic_end, :] asic_start = asic_end quad_asics.append(numpy.concatenate((a,b),axis=1)) quad_data = numpy.dstack(quad_asics) quad_data = numpy.rollaxis(quad_data, 2,0) data3d.append(fake_cspad_ElementV2(quad_data, i_quad)) env = fake_env(fake_config()) evt = fake_evt(data3d) beam_center, active_areas = cspad_tbx.cbcaa(fake_config(),sections) return flex.double(cspad_tbx.CsPadDetector(address, evt, env, sections).astype(numpy.float64)), active_areas
def display_calib(dirname, right, verbose): """XXX Docstring, in fact revise all the documentation @param dirname Directory with calibration information @param right @c True to restrict rotations to right angles @param verbose @c True to print ASIC coordinates """ fig = plt.figure(figsize=(10, 10)) ax = plt.axes([0, 0, 1, 1]) plt.axis([0, 1765, 1765, 0]) colours = [] patches = [] sections = calib2sections(dirname) for q in xrange(len(sections)): for s in xrange(len(sections[q])): # Get the vertices of the section s in quadrant q, and round # rotation angles to integer multiples of 90 degrees by default. # Change from matrix-coordinate system to screen coordinate # system, where origin is in the top left corner, the first # coordinate increases to the right, and the second coordinate # increases downwards. Ensure that the eight sections within # the quadrants are coloured consistently. vertices = sections[q][s].corners(right) for i in xrange(len(vertices)): vertices[i] = [vertices[i][1], vertices[i][0]] art = mpatches.Circle(vertices[0], 10) patches.append(art) colours.append(s) polygon = mpatches.Polygon(vertices) patches.append(polygon) colours.append(s) plt.text(sections[q][s].center[1], sections[q][s].center[0], "(%1d, %1d)" % (q, s), family="sans-serif", size=14, ha="center", va="center") # Assuming that rotations are integer multiples of 90 degrees, # print the ASIC coordinates in "spotfinder" format, ordered by # quadrant, section, and ASIC. XXX This only makes sense for # right = True. if (verbose): vertices = sections[q][s].corners_asic() print "(%4d, %4d, %4d, %4d)" % \ (vertices[0][0], vertices[0][1], vertices[0][2], vertices[0][3]) print "(%4d, %4d, %4d, %4d)" % \ (vertices[1][0], vertices[1][1], vertices[1][2], vertices[1][3]) collection = PatchCollection(patches, cmap=matplotlib.cm.jet, alpha=0.4) collection.set_array(numpy.array(colours)) ax.add_collection(collection) ax.set_xticks([]) ax.set_yticks([]) plt.show() return (0)
def average(argv=None): if argv == None: argv = sys.argv[1:] try: from mpi4py import MPI except ImportError: raise Sorry("MPI not found") command_line = (libtbx.option_parser.option_parser( usage=""" %s [-p] -c config -x experiment -a address -r run -d detz_offset [-o outputdir] [-A averagepath] [-S stddevpath] [-M maxpath] [-n numevents] [-s skipnevents] [-v] [-m] [-b bin_size] [-X override_beam_x] [-Y override_beam_y] [-D xtc_dir] [-f] To write image pickles use -p, otherwise the program writes CSPAD CBFs. Writing CBFs requires the geometry to be already deployed. Examples: cxi.mpi_average -c cxi49812/average.cfg -x cxi49812 -a CxiDs1.0:Cspad.0 -r 25 -d 571 Use one process on the current node to process all the events from run 25 of experiment cxi49812, using a detz_offset of 571. mpirun -n 16 cxi.mpi_average -c cxi49812/average.cfg -x cxi49812 -a CxiDs1.0:Cspad.0 -r 25 -d 571 As above, using 16 cores on the current node. bsub -a mympi -n 100 -o average.out -q psanaq cxi.mpi_average -c cxi49812/average.cfg -x cxi49812 -a CxiDs1.0:Cspad.0 -r 25 -d 571 -o cxi49812 As above, using the psanaq and 100 cores, putting the log in average.out and the output images in the folder cxi49812. """ % libtbx.env.dispatcher_name) .option(None, "--as_pickle", "-p", action="store_true", default=False, dest="as_pickle", help="Write results as image pickle files instead of cbf files") .option(None, "--config", "-c", type="string", default=None, dest="config", metavar="PATH", help="psana config file") .option(None, "--experiment", "-x", type="string", default=None, dest="experiment", help="experiment name (eg cxi84914)") .option(None, "--run", "-r", type="int", default=None, dest="run", help="run number") .option(None, "--address", "-a", type="string", default="CxiDs2.0:Cspad.0", dest="address", help="detector address name (eg CxiDs2.0:Cspad.0)") .option(None, "--detz_offset", "-d", type="float", default=None, dest="detz_offset", help="offset (in mm) from sample interaction region to back of CSPAD detector rail (CXI), or detector distance (XPP)") .option(None, "--outputdir", "-o", type="string", default=".", dest="outputdir", metavar="PATH", help="Optional path to output directory for output files") .option(None, "--averagebase", "-A", type="string", default="{experiment!l}_avg-r{run:04d}", dest="averagepath", metavar="PATH", help="Path to output average image without extension. String substitution allowed") .option(None, "--stddevbase", "-S", type="string", default="{experiment!l}_stddev-r{run:04d}", dest="stddevpath", metavar="PATH", help="Path to output standard deviation image without extension. String substitution allowed") .option(None, "--maxbase", "-M", type="string", default="{experiment!l}_max-r{run:04d}", dest="maxpath", metavar="PATH", help="Path to output maximum projection image without extension. String substitution allowed") .option(None, "--numevents", "-n", type="int", default=None, dest="numevents", help="Maximum number of events to process. Default: all") .option(None, "--skipevents", "-s", type="int", default=0, dest="skipevents", help="Number of events in the beginning of the run to skip. Default: 0") .option(None, "--verbose", "-v", action="store_true", default=False, dest="verbose", help="Print more information about progress") .option(None, "--pickle-optical-metrology", "-m", action="store_true", default=False, dest="pickle_optical_metrology", help="If writing pickle files, use the optical metrology in the experiment's calib directory") .option(None, "--bin_size", "-b", type="int", default=None, dest="bin_size", help="Rayonix detector bin size") .option(None, "--override_beam_x", "-X", type="float", default=None, dest="override_beam_x", help="Rayonix detector beam center x coordinate") .option(None, "--override_beam_y", "-Y", type="float", default=None, dest="override_beam_y", help="Rayonix detector beam center y coordinate") .option(None, "--calib_dir", "-C", type="string", default=None, dest="calib_dir", metavar="PATH", help="calibration directory") .option(None, "--xtc_dir", "-D", type="string", default=None, dest="xtc_dir", metavar="PATH", help="xtc stream directory") .option(None, "--use_ffb", "-f", action="store_true", default=False, dest="use_ffb", help="Use the fast feedback filesystem at LCLS. Only for the active experiment!") ).process(args=argv) if len(command_line.args) > 0 or \ command_line.options.as_pickle is None or \ command_line.options.experiment is None or \ command_line.options.run is None or \ command_line.options.address is None or \ command_line.options.detz_offset is None or \ command_line.options.averagepath is None or \ command_line.options.stddevpath is None or \ command_line.options.maxpath is None or \ command_line.options.pickle_optical_metrology is None: command_line.parser.show_help() return # set this to sys.maxint to analyze all events if command_line.options.numevents is None: maxevents = sys.maxint else: maxevents = command_line.options.numevents comm = MPI.COMM_WORLD rank = comm.Get_rank() size = comm.Get_size() if command_line.options.config is not None: psana.setConfigFile(command_line.options.config) dataset_name = "exp=%s:run=%d:idx"%(command_line.options.experiment, command_line.options.run) if command_line.options.xtc_dir is not None: if command_line.options.use_ffb: raise Sorry("Cannot specify the xtc_dir and use SLAC's ffb system") dataset_name += ":dir=%s"%command_line.options.xtc_dir elif command_line.options.use_ffb: # as ffb is only at SLAC, ok to hardcode /reg/d here dataset_name += ":dir=/reg/d/ffb/%s/%s/xtc"%(command_line.options.experiment[0:3],command_line.options.experiment) ds = psana.DataSource(dataset_name) address = command_line.options.address src = psana.Source('DetInfo(%s)'%address) if not command_line.options.as_pickle: psana_det = psana.Detector(address, ds.env()) nevent = np.array([0.]) for run in ds.runs(): runnumber = run.run() # list of all events if command_line.options.skipevents > 0: print "Skipping first %d events"%command_line.options.skipevents times = run.times()[command_line.options.skipevents:] nevents = min(len(times),maxevents) # chop the list into pieces, depending on rank. This assigns each process # events such that the get every Nth event where N is the number of processes mytimes = [times[i] for i in xrange(nevents) if (i+rank)%size == 0] for i in xrange(len(mytimes)): if i%10==0: print 'Rank',rank,'processing event',rank*len(mytimes)+i,', ',i,'of',len(mytimes) evt = run.event(mytimes[i]) #print "Event #",rank*mylength+i," has id:",evt.get(EventId) if 'Rayonix' in command_line.options.address: data = evt.get(Camera.FrameV1,src) if data is None: print "No data" continue data=data.data16().astype(np.float64) elif command_line.options.as_pickle: data = evt.get(psana.ndarray_float64_3, src, 'image0') else: # get numpy array, 32x185x388 data = psana_det.calib(evt) # applies psana's complex run-dependent calibrations if data is None: print "No data" continue d = cspad_tbx.env_distance(address, run.env(), command_line.options.detz_offset) if d is None: print "No distance, skipping shot" continue if 'distance' in locals(): distance += d else: distance = np.array([float(d)]) w = cspad_tbx.evt_wavelength(evt) if w is None: print "No wavelength, skipping shot" continue if 'wavelength' in locals(): wavelength += w else: wavelength = np.array([w]) t = cspad_tbx.evt_time(evt) if t is None: print "No timestamp, skipping shot" continue if 'timestamp' in locals(): timestamp += t[0] + (t[1]/1000) else: timestamp = np.array([t[0] + (t[1]/1000)]) if 'sum' in locals(): sum+=data else: sum=np.array(data, copy=True) if 'sumsq' in locals(): sumsq+=data*data else: sumsq=data*data if 'maximum' in locals(): maximum=np.maximum(maximum,data) else: maximum=np.array(data, copy=True) nevent += 1 #sum the images across mpi cores if size > 1: print "Synchronizing rank", rank totevent = np.zeros(nevent.shape) comm.Reduce(nevent,totevent) if rank == 0 and totevent[0] == 0: raise Sorry("No events found in the run") sumall = np.zeros(sum.shape).astype(sum.dtype) comm.Reduce(sum,sumall) sumsqall = np.zeros(sumsq.shape).astype(sumsq.dtype) comm.Reduce(sumsq,sumsqall) maxall = np.zeros(maximum.shape).astype(maximum.dtype) comm.Reduce(maximum,maxall, op=MPI.MAX) waveall = np.zeros(wavelength.shape).astype(wavelength.dtype) comm.Reduce(wavelength,waveall) distall = np.zeros(distance.shape).astype(distance.dtype) comm.Reduce(distance,distall) timeall = np.zeros(timestamp.shape).astype(timestamp.dtype) comm.Reduce(timestamp,timeall) if rank==0: if size > 1: print "Synchronized" # Accumulating floating-point numbers introduces errors, # which may cause negative variances. Since a two-pass # approach is unacceptable, the standard deviation is # clamped at zero. mean = sumall / float(totevent[0]) variance = (sumsqall / float(totevent[0])) - (mean**2) variance[variance < 0] = 0 stddev = np.sqrt(variance) wavelength = waveall[0] / totevent[0] distance = distall[0] / totevent[0] pixel_size = cspad_tbx.pixel_size saturated_value = cspad_tbx.cspad_saturated_value timestamp = timeall[0] / totevent[0] timestamp = (int(timestamp), timestamp % int(timestamp) * 1000) timestamp = cspad_tbx.evt_timestamp(timestamp) if command_line.options.as_pickle: extension = ".pickle" else: extension = ".cbf" dest_paths = [cspad_tbx.pathsubst(command_line.options.averagepath + extension, evt, ds.env()), cspad_tbx.pathsubst(command_line.options.stddevpath + extension, evt, ds.env()), cspad_tbx.pathsubst(command_line.options.maxpath + extension, evt, ds.env())] dest_paths = [os.path.join(command_line.options.outputdir, path) for path in dest_paths] if 'Rayonix' in command_line.options.address: from xfel.cxi.cspad_ana import rayonix_tbx pixel_size = rayonix_tbx.get_rayonix_pixel_size(command_line.options.bin_size) beam_center = [command_line.options.override_beam_x,command_line.options.override_beam_y] detector_dimensions = rayonix_tbx.get_rayonix_detector_dimensions(command_line.options.bin_size) active_areas = flex.int([0,0,detector_dimensions[0],detector_dimensions[1]]) split_address = cspad_tbx.address_split(address) old_style_address = split_address[0] + "-" + split_address[1] + "|" + split_address[2] + "-" + split_address[3] for data, path in zip([mean, stddev, maxall], dest_paths): print "Saving", path d = cspad_tbx.dpack( active_areas=active_areas, address=old_style_address, beam_center_x=pixel_size * beam_center[0], beam_center_y=pixel_size * beam_center[1], data=flex.double(data), distance=distance, pixel_size=pixel_size, saturated_value=rayonix_tbx.rayonix_saturated_value, timestamp=timestamp, wavelength=wavelength) easy_pickle.dump(path, d) elif command_line.options.as_pickle: split_address = cspad_tbx.address_split(address) old_style_address = split_address[0] + "-" + split_address[1] + "|" + split_address[2] + "-" + split_address[3] xpp = 'xpp' in address.lower() if xpp: evt_time = cspad_tbx.evt_time(evt) # tuple of seconds, milliseconds timestamp = cspad_tbx.evt_timestamp(evt_time) # human readable format from xfel.detector_formats import detector_format_version, reverse_timestamp from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas version_lookup = detector_format_version(old_style_address, reverse_timestamp(timestamp)[0]) assert version_lookup is not None active_areas = xpp_active_areas[version_lookup]['active_areas'] beam_center = [1765 // 2, 1765 // 2] else: if command_line.options.calib_dir is not None: metro_path = command_line.options.calib_dir elif command_line.options.pickle_optical_metrology: from xfel.cftbx.detector.cspad_cbf_tbx import get_calib_file_path metro_path = get_calib_file_path(run.env(), address, run) else: metro_path = libtbx.env.find_in_repositories("xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = parse_calib.calib2sections(metro_path) beam_center, active_areas = cspad_tbx.cbcaa( cspad_tbx.getConfig(address, ds.env()), sections) class fake_quad(object): def __init__(self, q, d): self.q = q self.d = d def quad(self): return self.q def data(self): return self.d if xpp: quads = [fake_quad(i, mean[i*8:(i+1)*8,:,:]) for i in xrange(4)] mean = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads = quads) mean = flex.double(mean.astype(np.float64)) quads = [fake_quad(i, stddev[i*8:(i+1)*8,:,:]) for i in xrange(4)] stddev = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads = quads) stddev = flex.double(stddev.astype(np.float64)) quads = [fake_quad(i, maxall[i*8:(i+1)*8,:,:]) for i in xrange(4)] maxall = cspad_tbx.image_xpp(old_style_address, None, ds.env(), active_areas, quads = quads) maxall = flex.double(maxall.astype(np.float64)) else: quads = [fake_quad(i, mean[i*8:(i+1)*8,:,:]) for i in xrange(4)] mean = cspad_tbx.CsPadDetector( address, evt, ds.env(), sections, quads=quads) mean = flex.double(mean.astype(np.float64)) quads = [fake_quad(i, stddev[i*8:(i+1)*8,:,:]) for i in xrange(4)] stddev = cspad_tbx.CsPadDetector( address, evt, ds.env(), sections, quads=quads) stddev = flex.double(stddev.astype(np.float64)) quads = [fake_quad(i, maxall[i*8:(i+1)*8,:,:]) for i in xrange(4)] maxall = cspad_tbx.CsPadDetector( address, evt, ds.env(), sections, quads=quads) maxall = flex.double(maxall.astype(np.float64)) for data, path in zip([mean, stddev, maxall], dest_paths): print "Saving", path d = cspad_tbx.dpack( active_areas=active_areas, address=old_style_address, beam_center_x=pixel_size * beam_center[0], beam_center_y=pixel_size * beam_center[1], data=data, distance=distance, pixel_size=pixel_size, saturated_value=saturated_value, timestamp=timestamp, wavelength=wavelength) easy_pickle.dump(path, d) else: # load a header only cspad cbf from the slac metrology from xfel.cftbx.detector import cspad_cbf_tbx import pycbf base_dxtbx = cspad_cbf_tbx.env_dxtbx_from_slac_metrology(run, address) if base_dxtbx is None: raise Sorry("Couldn't load calibration file for run %d"%run.run()) for data, path in zip([mean, stddev, maxall], dest_paths): print "Saving", path cspad_img = cspad_cbf_tbx.format_object_from_data(base_dxtbx, data, distance, wavelength, timestamp, address) cspad_img._cbf_handle.write_widefile(path, pycbf.CBF,\ pycbf.MIME_HEADERS|pycbf.MSG_DIGEST|pycbf.PAD_4K, 0)
def display_calib(dirname, right, verbose): """XXX Docstring, in fact revise all the documentation @param dirname Directory with calibration information @param right @c True to restrict rotations to right angles @param verbose @c True to print ASIC coordinates """ fig = plt.figure(figsize=(10, 10)) ax = plt.axes([0, 0, 1, 1]) plt.axis([0, 1765, 1765, 0]) colours = [] patches = [] sections = calib2sections(dirname) for q in xrange(len(sections)): for s in xrange(len(sections[q])): # Get the vertices of the section s in quadrant q, and round # rotation angles to integer multiples of 90 degrees by default. # Change from matrix-coordinate system to screen coordinate # system, where origin is in the top left corner, the first # coordinate increases to the right, and the second coordinate # increases downwards. Ensure that the eight sections within # the quadrants are coloured consistently. vertices = sections[q][s].corners(right) for i in xrange(len(vertices)): vertices[i] = [vertices[i][1], vertices[i][0]] art = mpatches.Circle(vertices[0], 10) patches.append(art) colours.append(s) polygon = mpatches.Polygon(vertices) patches.append(polygon) colours.append(s) plt.text( sections[q][s].center[1], sections[q][s].center[0], "(%1d, %1d)" % (q, s), family="sans-serif", size=14, ha="center", va="center", ) # Assuming that rotations are integer multiples of 90 degrees, # print the ASIC coordinates in "spotfinder" format, ordered by # quadrant, section, and ASIC. XXX This only makes sense for # right = True. if verbose: vertices = sections[q][s].corners_asic() print "(%4d, %4d, %4d, %4d)" % (vertices[0][0], vertices[0][1], vertices[0][2], vertices[0][3]) print "(%4d, %4d, %4d, %4d)" % (vertices[1][0], vertices[1][1], vertices[1][2], vertices[1][3]) collection = PatchCollection(patches, cmap=matplotlib.cm.jet, alpha=0.4) collection.set_array(numpy.array(colours)) ax.add_collection(collection) ax.set_xticks([]) ax.set_yticks([]) plt.show() return 0
def convert_detector(raw_data, detector_format_version, address, optical_metrology_path=None): # https://confluence.slac.stanford.edu/display/PCDS/CSPad+metrology+and+calibration+files%2C+links data3d = [] if raw_data.shape == (5920, 388): asic_start = 0 if optical_metrology_path is None: calib_dir = libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = parse_calib.calib2sections(calib_dir) else: sections = parse_calib.calib2sections(optical_metrology_path) for i_quad in range(4): asic_size = 185 * 388 section_size = asic_size * 2 quad_start = i_quad * section_size * 4 quad_asics = [] for i_2x2 in range(4): for i_asic in range(2): asic_end = asic_start + 185 quad_asics.append(raw_data[asic_start:asic_end, :]) asic_start = asic_end quad_data = numpy.dstack(quad_asics) quad_data = numpy.rollaxis(quad_data, 2, 0) data3d.append(fake_cspad_ElementV2(quad_data, i_quad)) env = fake_env(fake_config()) evt = fake_evt(data3d) return flex.double( cspad_tbx.CsPadDetector(address, evt, env, sections).astype(numpy.float64)), None else: asic_start = 0 if detector_format_version is not None and 'XPP' in detector_format_version: from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas rotations = xpp_active_areas[detector_format_version]['rotations'] active_areas = xpp_active_areas[detector_format_version][ 'active_areas'] det = flex.double([0] * (1765 * 1765)) det.reshape(flex.grid((1765, 1765))) for i in range(64): row = active_areas[i * 4] col = active_areas[i * 4 + 1] block = flex.double(raw_data[i * 185:(i + 1) * 185, :]) det.matrix_paste_block_in_place( block.matrix_rot90(rotations[i]), row, col) return det, active_areas else: if optical_metrology_path is None: calib_dir = libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = parse_calib.calib2sections(calib_dir) else: sections = parse_calib.calib2sections(optical_metrology_path) for i_quad in range(4): asic_size = 185 * 194 section_size = asic_size * 4 quad_start = i_quad * section_size * 4 quad_asics = [] for i_2x2 in range(4): for i_asic in range(2): asic_end = asic_start + 185 a = raw_data[asic_start:asic_end, :] asic_start = asic_end asic_end = asic_start + 185 b = raw_data[asic_start:asic_end, :] asic_start = asic_end quad_asics.append(numpy.concatenate((a, b), axis=1)) quad_data = numpy.dstack(quad_asics) quad_data = numpy.rollaxis(quad_data, 2, 0) data3d.append(fake_cspad_ElementV2(quad_data, i_quad)) env = fake_env(fake_config()) evt = fake_evt(data3d) beam_center, active_areas = cspad_tbx.cbcaa( fake_config(), sections) return flex.double( cspad_tbx.CsPadDetector(address, evt, env, sections).astype( numpy.float64)), active_areas
from xfel.cxi.cspad_ana.cspad_tbx import dpack, evt_timestamp, cbcaa, pixel_size, CsPadDetector from iotbx.detectors.cspad_detector_formats import address_and_timestamp_from_detector_format_version from convert_gain_map import fake_env, fake_config, fake_evt, fake_cspad_ElementV2 address, timestamp = address_and_timestamp_from_detector_format_version(params.detector_format_version) timestamp = evt_timestamp((timestamp,0)) raw_data = numpy.zeros((11840,194)) if params.detector_format_version is not None and "XPP" in params.detector_format_version: from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas active_areas = xpp_active_areas[params.detector_format_version]['active_areas'] data = flex.int(flex.grid((1765,1765))) beam_center = (1765 // 2, 1765 // 2) else: if params.optical_metrology_path is None: calib_dir = libtbx.env.find_in_repositories("xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") sections = calib2sections(calib_dir) else: sections = calib2sections(params.optical_metrology_path) asic_start = 0 data3d = [] for i_quad in range(4): asic_size = 185 * 194 section_size = asic_size * 4 quad_start = i_quad * section_size * 4 quad_asics = [] for i_2x2 in range(4): for i_asic in range(2): asic_end = asic_start + 185 a = raw_data[asic_start:asic_end, :] asic_start = asic_end
def metrology2phil(calib_dir, verbose): """XXX Should really review the SLAC progress since last time around! XXX Note that this is all SLAC-specific (as is the whole thing, I guess). """ from xfel.cftbx.detector.metrology import master_phil # XXX Can this fail? How? sections = calib2sections(calib_dir) if (sections is None): return (None) # Properties of CSPad pixels (Philipp et al., 2007). The counters # are 14 bits wide, and the pixels are square with a side length of # 110 um. Because cspad_tbx depends on pyana, it may fail to import # in which case a hardcoded fallback is provided. try: from xfel.cxi.cspad_ana.cspad_tbx import cspad_saturated_value as sv from xfel.cxi.cspad_ana.cspad_tbx import pixel_size as ps saturated_value = sv pixel_size = ps * 1e-3 except ImportError: saturated_value = 90000 pixel_size = 110e-6 # Build the Phil object. XXX Should have det-z? Probably not, # because then it aint't just metrology anymore. In fact, under # orthographic projection the whole translation/orientation thing # can be scrapped for the detector. XXX look up include, # include_scope for phil XXX Hardcoded address for now. address = "CxiDs1-0|Cspad-0" metrology_str = "detector { serial = %d\n" % 0 metrology_str += "label = %s\n" % address # The center of the detector is defined as the average of all the # sections it contains. The first coordinate of the matrix-oriented # coordinate system of the Section class maps to -y, the second to # x, and the third to z. While the detector still sits on the # origin (zero translation), the mapping from the origin in Section # coordinates is still needed. t_d = [0, 0, 0] nmemb = 0 for p in xrange(len(sections)): for s in xrange(len(sections[p])): t_d[0] += +sections[p][s].center[1] t_d[1] += -sections[p][s].center[0] t_d[2] += 0 nmemb += 1 if (nmemb > 0): for i in xrange(3): t_d[i] /= nmemb metrology_str += "translation = 0, 0, 0\n" o_d = matrix.col([0, 0, 1]).axis_and_angle_as_unit_quaternion( angle=0, deg=True) metrology_str += "orientation = %s, %s, %s, %s\n" % \ tuple(repr(c) for c in o_d) for p in xrange(len(sections)): # Loop over quadrants (panels). XXX Translation of panels is # wrongly set to to centre of sections in the quadrant # w.r.t. center of the detector. metrology_str += "panel { serial = %d\n" % p metrology_str += "translation = " t_p = [0, 0, 0] for s in xrange(len(sections[p])): t_p[0] += +sections[p][s].center[1] - t_d[0] t_p[1] += -sections[p][s].center[0] - t_d[1] t_p[2] += 0 - t_d[2] for i in xrange(3): t_p[i] /= len(sections[p]) metrology_str += "%s" % repr(t_p[i] * pixel_size) if (i < 2): metrology_str += ", " else: metrology_str += "\n" # XXX Orientation wrongly set to zero. o_p = matrix.col([0, 0, 1]).axis_and_angle_as_unit_quaternion( angle=0, deg=True) metrology_str += "orientation = %s, %s, %s, %s\n" % \ tuple(repr(c) for c in o_p) for s in xrange(len(sections[p])): # Loop over sensors (sections or two-by-one:s). Note that # sensors are rotated by -90 degrees in the SLAC metrology # convention w.r.t. their appearance in the XTC stream. s_t = [0, 0, 0] s_t[0] = +sections[p][s].center[1] - t_p[0] - t_d[0] s_t[1] = -sections[p][s].center[0] - t_p[1] - t_d[1] s_t[2] = 0 - t_p[2] - t_d[2] metrology_str += "sensor { serial = %d\n" % s metrology_str += "translation = " for i in xrange(3): metrology_str += "%s" % repr(s_t[i] * pixel_size) if (i < 2): metrology_str += ", " else: metrology_str += "\n" s_o = matrix.col([0, 0, 1]).axis_and_angle_as_unit_quaternion( angle=sections[p][s].angle - 90, deg=True) metrology_str += "orientation = %s, %s, %s, %s\n" % \ tuple(repr(c) for c in s_o) for a in xrange(2): # The ASIC:s of the CSPad are 185 rows by 194 columns. The # ASIC:s are horizontally aligned within a section, with a # three-column gap between them. metrology_str += "asic { serial = %d\n" % a if (a == 0): metrology_str += "translation = %s, %s, %s\n" % ( repr(-(194 + 3) / 2 * pixel_size), "0", "0") # XXX hardcoded! else: metrology_str += "translation = %s, %s, %s\n" % ( repr(+(194 + 3) / 2 * pixel_size), "0", "0") # XXX hardcoded! metrology_str += "orientation = 1, 0, 0, 0\n" metrology_str += "pixel_size = %s, %s\n" % ( repr(pixel_size), repr(pixel_size)) metrology_str += "dimension = %d, %d\n" % (194, 185) # XXX hardcoded! metrology_str += "saturation = %s\n" % repr(float(saturated_value)) metrology_str += "}\n" metrology_str += "}\n" metrology_str += "}\n" metrology_str += "}\n" metrology_phil = master_phil.fetch( sources=[phil.parse(metrology_str)]) return (metrology_phil)
except RuntimeError, e : raise Sorry("Unrecognized argument '%s' (error: %s)" % (arg, str(e))) params = master_phil.fetch(sources=user_phil).extract() assert params.pickle_file is not None if len(params.pickle_file) == 0 : master_phil.show() raise Usage("pickle_file must be defined (either pickle_file=XXX, or the file path(s) alone).") assert params.detector is not None assert params.plot is not None if params.old_metrology is None: params.old_metrology=libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") if os.path.isdir(params.old_metrology): sections = calib2sections(params.old_metrology) else: from xfel.cxi.cspad_ana.cspad_tbx import xpp_active_areas assert params.old_metrology in xpp_active_areas if params.new_metrology is None: params.new_metrology=libtbx.env.find_in_repositories( "xfel/metrology/CSPad/run4/CxiDs1.0_Cspad.0") assert os.path.isdir(params.new_metrology) or os.path.isfile(params.new_metrology) # Read the new metrology, either from a calibration directory or an optical metrology # flat file if os.path.isdir(params.new_metrology): metro_style = "calibdir" from xfel.cftbx.detector.metrology2phil import metrology2phil