def format_object_from_data(base_dxtbx, data, distance, wavelength, timestamp, address, round_to_int=True): """ Given a preloaded dxtbx format object and raw data, assemble the tiles and set the distance. @param base_dxtbx A header only dxtbx format object @param data rayonix byte array from XTC stream @param distance Detector distance (mm) @param wavelength Shot wavelength (angstroms) @param timestamp Human readable timestamp @param address Detector address, put in CBF header """ import numpy as np from xfel.cftbx.detector import cspad_cbf_tbx cbf = cspad_cbf_tbx.copy_cbf_header(base_dxtbx._cbf_handle, skip_sections=True) rayonix_img = FormatCBFRayonixInMemory(cbf) cbf.set_datablockname((address + "_" + timestamp).encode()) if round_to_int: data = flex.double(data.astype(np.float64)).iround() else: data = flex.double(data.astype(np.float64)) n_asics = data.focus()[0] * data.focus()[1] cspad_cbf_tbx.add_frame_specific_cbf_tables( cbf, wavelength, timestamp, [(rayonix_min_trusted_value, rayonix_saturated_value)] * n_asics) # Set the distance, I.E., the length translated along the Z axis cbf.find_category(b"diffrn_scan_frame_axis") cbf.find_column(b"axis_id") cbf.find_row( b"AXIS_D0_Z") # XXX discover the Z axis somehow, don't use D0 here cbf.find_column(b"displacement") cbf.set_value(b"%f" % (-distance)) # Explicitly reset the detector object now that the distance is set correctly rayonix_img._detector_instance = rayonix_img._detector() # Explicitly set up the beam object now that the tables are all loaded correctly rayonix_img._beam_instance = rayonix_img._beam() # Get the data and add it to the cbf handle. add_data_to_cbf(cbf, data) return rayonix_img
def get_cbf_handle(self, index=None, header_only=False, detector_only=False): """ Build a cbf handle in memory """ # set up the metrology dictionary to include axis names, pixel sizes, and so forth if index is None: detector = self.imageset.get_detector() beam = self.imageset.get_beam() else: detector = self.imageset.get_detector(index) beam = self.imageset.get_beam(index) metro = self.get_metrology_dict() def panel_group_from_key(key): # Find the node that a hierarchy key refers to if len(key) == 1: assert key[0] == 0 return detector.hierarchy() node = detector.hierarchy() for i in key[1:]: node = node[i] return node def level_string(key): # Example for key (0,1,2). "L0M0_L1M1_L2M2", where L is level and M is module return "_".join(["L%dM%d" % (l, m) for l, m in enumerate(key)]) # set up the metrology dictionary to include axis names, pixel sizes, and so forth dname = None detector_axes_names = [] # save these for later panelkeys = [] panelnames = [] def recursive_setup_basis_dict(key, parent_name="", panel_id=0): # Set up CBF axis names, including equipment components and depends_on chains basis = metro[key] node = panel_group_from_key(key) nodename = level_string(key) dname = "AXIS_" + nodename if node.is_panel(): panelname = "PANEL_%d" % panel_id panelkeys.append(key) panelnames.append(panelname) panel_id += 1 if len(key) == 1: assert key == (0,) # only one detector allowed for now for a in ["_X", "_Y", "_Z", "_R"]: detector_axes_names.append(dname + a) basis.depends_on = dname + "_X" dname += "_R" else: detector_axes_names.append(dname) basis.depends_on = parent_name basis.equipment_component = "detector_level_%d" % (len(key) - 1) basis.axis_name = detector_axes_names[-1] if not node.is_panel(): for c, child in enumerate(node): panel_id = recursive_setup_basis_dict( tuple(list(key) + [c]), dname, panel_id ) return panel_id recursive_setup_basis_dict((0,)) if index is None: cbf_root = self.imageset.paths()[0] else: cbf_root = self.imageset.paths()[index] cbf_root = os.path.splitext(os.path.basename(cbf_root))[0] # the data block is the root cbf node cbf = cbf_wrapper() cbf.new_datablock(cbf_root) # Each category listed here is preceded by the imageCIF description taken from here: # http://www.iucr.org/__data/iucr/cifdic_html/2/cif_img.dic/index.html """Data items in the DIFFRN category record details about the diffraction data and their measurement.""" diffrn_id = "dxtbx_%s" % self.imageset.reader().format_class.__name__ cbf.add_category("diffrn", ["id"]) cbf.add_row([diffrn_id]) """Data items in the DIFFRN_SOURCE category record details of the source of radiation used in the diffraction experiment.""" cbf.add_category("diffrn_source", ["diffrn_id", "source", "type"]) cbf.add_row([diffrn_id, "unknown", "unknown"]) """Data items in the DIFFRN_DETECTOR category describe the detector used to measure the scattered radiation, including any analyser and post-sample collimation.""" cbf.add_category( "diffrn_detector", ["diffrn_id", "id", "type", "details", "number_of_axes"] ) detector_id = "dxtbx_detector" cbf.add_row( [ diffrn_id, detector_id, "General dxtbx detector", ".", str(len(detector_axes_names)), ] ) """Data items in the DIFFRN_DETECTOR_AXIS category associate axes with detectors.""" # Note, does not include the fast and the slow axes cbf.add_category("diffrn_detector_axis", ["detector_id", "axis_id"]) for name in detector_axes_names: cbf.add_row([detector_id, name]) """Data items in the DIFFRN_DETECTOR_ELEMENT category record the details about spatial layout and other characteristics of each element of a detector which may have multiple elements.""" cbf.add_category("diffrn_detector_element", ["id", "detector_id"]) for panelname in panelnames: cbf.add_row(["ELE_" + panelname, detector_id]) """Data items in the DIFFRN_DATA_FRAME category record the details about each frame of data.""" cbf.add_category( "diffrn_data_frame", ["id", "detector_element_id", "array_id", "binary_id"] ) for i, panelname in enumerate(panelnames): cbf.add_row( ["FRAME1", "ELE_" + panelname, "ARRAY_" + panelname, "%d" % (i + 1)] ) if not detector_only: add_frame_specific_cbf_tables( cbf, beam.get_wavelength(), "unknown", [panel.get_trusted_range() for panel in detector], diffrn_id, False, ) """Data items in the AXIS category record the information required to describe the various goniometer, detector, source and other axes needed to specify a data collection. The location of each axis is specified by two vectors: the axis itself, given as a unit vector, and an offset to the base of the unit vector. These vectors are referenced to a right-handed laboratory coordinate system with its origin in the sample or specimen""" # More detail here: http://www.iucr.org/__data/iucr/cifdic_html/2/cif_img.dic/Caxis.html # Note we also use two new columns not in the latest imageCIF dictionary: rotation and rotation_axis. # We use them to specify an translation and a rotation in a single axis to describe a change in setting # when moving from one frame (say a quadrant) to another (say a sensor) cbf.add_category( "axis", [ "id", "type", "equipment", "depends_on", "vector[1]", "vector[2]", "vector[3]", "offset[1]", "offset[2]", "offset[3]", "equipment_component", ], ) # Keep a list of rows to add to the scan frame axis table axis_settings = [] # keep a list of rows to add to the scan axis table axis_names = [] root_key = (0,) dname = metro[root_key].axis_name[:-2] eqc = metro[root_key].equipment_component # Create a series of axis describing frame shifts from each level of the detector to the next cbf.add_row( "AXIS_SOURCE general source . 0 0 1 . . . .".split() ) axis_names.append("AXIS_SOURCE") cbf.add_row( "AXIS_GRAVITY general gravity . 0 -1 0 . . . .".split() ) axis_names.append("AXIS_GRAVITY") cbf.add_row( ( "%s_Z translation detector . 0 0 1 . . . %s" % (dname, eqc) ).split() ) axis_names.append("%s_Z" % dname) cbf.add_row( ( "%s_Y translation detector %s_Z 0 1 0 . . . %s" % (dname, dname, eqc) ).split() ) axis_names.append("%s_Y" % dname) cbf.add_row( ( "%s_X translation detector %s_Y 1 0 0 . . . %s" % (dname, dname, eqc) ).split() ) axis_names.append("%s_X" % dname) axis_settings.append(["AXIS_SOURCE", "FRAME1", "0", "0"]) axis_settings.append(["AXIS_GRAVITY", "FRAME1", "0", "0"]) axis_settings.append([dname + "_X", "FRAME1", "0", "0"]) axis_settings.append([dname + "_Y", "FRAME1", "0", "0"]) axis_settings.append([dname + "_Z", "FRAME1", "0", "0"]) for key in sorted(metro): basis = metro[key] cbf.add_frame_shift(basis, axis_settings) axis_names.append(basis.axis_name) node = panel_group_from_key(key) if node.is_panel(): axis_settings[-1][ -2 ] = "0" # Drop the setting change for leaves as it's encoded below aname = level_string(key) fast = [str(v) for v in node.get_local_fast_axis()] slow = [str(v) for v in node.get_local_slow_axis()] cbf.add_row( [ "AXIS_" + aname + "_S", "translation", "detector", basis.axis_name, slow[0], slow[1], slow[2], "0", "0", "0", basis.equipment_component, ] ) cbf.add_row( [ "AXIS_" + aname + "_F", "translation", "detector", "AXIS_" + aname + "_S", fast[0], fast[1], fast[2], "0", "0", "0", basis.equipment_component, ] ) axis_names.append("AXIS_" + aname + "_F") axis_names.append("AXIS_" + aname + "_S") axis_settings.append(["AXIS_" + aname + "_F", "FRAME1", "0", "0"]) axis_settings.append(["AXIS_" + aname + "_S", "FRAME1", "0", "0"]) """Data items in the DIFFRN_SCAN_AXIS category describe the settings of axes for particular scans. Unspecified axes are assumed to be at their zero points.""" # leave all the settings zero. Levels with settings are set below. cbf.add_category( "diffrn_scan_axis", [ "axis_id", "scan_id", "angle_start", "angle_range", "angle_increment", "displacement_start", "displacement_range", "displacement_increment", ], ) for name in axis_names: cbf.add_row([name, "SCAN1", "0", "0", "0", "0", "0", "0"]) """Data items in the DIFFRN_SCAN_FRAME_AXIS category describe the settings of axes for particular frames. Unspecified axes are assumed to be at their zero points.""" cbf.add_category( "diffrn_scan_frame_axis", ["axis_id", "frame_id", "angle", "displacement"] ) for row in axis_settings: cbf.add_row(row) """Data items in the ARRAY_STRUCTURE_LIST category record the size and organization of each array dimension. The relationship to physical axes may be given.""" cbf.add_category( "array_structure_list", [ "array_id", "index", "dimension", "precedence", "direction", "axis_set_id", ], ) for panelkey, panelname in zip(panelkeys, panelnames): aname = level_string(panelkey) panel = panel_group_from_key(panelkey) fast_dim, slow_dim = panel.get_image_size() cbf.add_row( [ "ARRAY_" + panelname, "1", "%d" % fast_dim, "1", "increasing", "AXIS_" + aname + "_F", ] ) cbf.add_row( [ "ARRAY_" + panelname, "2", "%d" % slow_dim, "2", "increasing", "AXIS_" + aname + "_S", ] ) """Data items in the ARRAY_STRUCTURE_LIST_SECTION category identify the dimension-by-dimension start, end and stride of each section of an array that is to be referenced.""" # no sections here """Data items in the ARRAY_STRUCTURE_LIST_AXIS category describe the physical settings of sets of axes for the centres of pixels that correspond to data points described in the ARRAY_STRUCTURE_LIST category.""" cbf.add_category( "array_structure_list_axis", ["axis_set_id", "axis_id", "displacement", "displacement_increment"], ) for panelkey in panelkeys: aname = level_string(panelkey) panel = panel_group_from_key(panelkey) pixel_size = panel.get_pixel_size() cbf.add_row( [ "AXIS_" + aname + "_F", "AXIS_" + aname + "_F", "0.0", str(pixel_size[0]), ] ) cbf.add_row( [ "AXIS_" + aname + "_S", "AXIS_" + aname + "_S", "0.0", str(pixel_size[1]), ] ) if not header_only: self.add_data_to_cbf(cbf, index) return cbf