예제 #1
0
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
예제 #2
0
    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