Skip to content

plotting

agipd_single_module_geometry()

Initialize AGIPD single module detector geometry.

Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def agipd_single_module_geometry():
    """Initialize AGIPD single module detector geometry."""
    # TODO: use extra_geom implementation when ready. This has no module name
    # tiles text is different from other AGIPD detectors.
    pixel_size = 2e-4
    simple_config = {
        'pixel_size': pixel_size,
        'slow_pixels': 64,
        'fast_pixels': 128,
        'n_tiles_per_module': 8,
        'corner_coordinates': [np.array([-256, -64, 0]) * pixel_size],
    }
    return GenericGeometry.from_simple_description(**simple_config)

create_constant_overview(constant, name, cells, vmin=None, vmax=None, entries=3, badpixels=None, gmap=None, marker=None)

Create a step plot for constant data across memory cells for requested gain entries

:param constant: dict with constants for each module. :param name: Name to be used for the x-axis :param cells: Number of memory cells :param vmin: plot minumim value boundaries :param vmax: plot maximum value boundaries :param entries: (int)number of gain entries. A tuple specifying the range can also be used. TODO: remove unused inputs from notebooks. :param out_folder: out_folder for showing .md table statistics :param infix: infix for the output png image :param badpixels: A list of 2 elements. badpixels[0] has the dict with badpixels constant for each module and badpixels[1] has the value to apply for bad pixels. :param gmap: A list with len equal to number of gain entires. if not supported, a default would be used for 3 entries. ['High gain', 'Medium gain', 'Low gain'] :param marker: A list of line markers for each gain entry. default: ['']*entries :return:

Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def create_constant_overview(constant, name, cells, vmin=None, vmax=None,
                             entries=3, badpixels=None, gmap=None,
                             marker=None):
    """
    Create a step plot for constant data across memory cells for requested
    gain entries

    :param constant: dict with constants for each module.
    :param name: Name to be used for the x-axis
    :param cells: Number of memory cells
    :param vmin: plot minumim value boundaries
    :param vmax: plot maximum value boundaries
    :param entries: (int)number of gain entries.
        A tuple specifying the range can also be used.
    TODO: remove unused inputs from notebooks.
    :param out_folder: out_folder for showing .md table statistics
    :param infix: infix for the output png image
    :param badpixels: A list of 2 elements.
        badpixels[0] has the dict with badpixels constant for each module
        and badpixels[1] has the value to apply for bad pixels.
    :param gmap: A list with len equal to number of gain entires.
        if not supported, a default would be used for 3 entries.
        ['High gain', 'Medium gain', 'Low gain']
    :param marker: A list of line markers for each gain entry.
        default: ['']*entries
    :return:
    """
    if gmap is None:
        gmap = ['High gain', 'Medium gain', 'Low gain']
    if marker is None:
        marker = [''] * entries
    fig = plt.figure(figsize=(10, 5))
    ax = fig.add_subplot(111)
    for g in range(entries):
        table = []
        dbp = None

        for qm in constant.keys():
            if len(constant[qm].shape) == 4:
                d = constant[qm][..., g]
                if badpixels is not None and isinstance(badpixels, list):
                    dbp = np.copy(d)
                    dbp[badpixels[0][qm][..., g] > 0] = badpixels[1]

            else:
                # e.g. DSSC
                d = constant[qm]
                if badpixels is not None and isinstance(badpixels, list):
                    dbp = np.copy(d)
                    dbp[badpixels[0][qm] > 0] = badpixels[1]
            # TODO: check if not used to remove.
            table.append([name, qm, gmap[g], np.nanmean(d), np.nanmedian(d),
                          np.nanstd(d)])

            ax.step(np.arange(cells), np.nanmean(d, axis=(0, 1)),
                    label=gmap[g], color=f'C{g}', marker=marker[g])
            # Plotting good pixels only if bad-pixels were given
            if dbp is not None:
                ax.step(np.arange(cells), np.nanmean(dbp, axis=(0, 1)),
                        label=f'Good pixels {gmap[g]}', color=f'C{g}',
                        linestyle='--')
    ax.set_xlabel("Memory cell")
    ax.set_ylabel(name)
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
    ax.set_title(f"{name} Median per Cell")
    if vmin and vmax:
        ax.set_ylim(vmin, vmax)

init_jungfrau_geom(karabo_id, karabo_da)

Initiate JUNGFRAUGeometry object based on the selected detector (JF4M, JF1M, or JF500K detectors).

:param karabo_id: the detector identifer of an expected multimodular detector or a single module detector. :param karabo_da: the data aggregator names that defines the module name. This is more relevant for the single module detector to display the correct module name. :return: List of expected module names, JUNGFRAUGeometry object.

Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def init_jungfrau_geom(
    karabo_id: str,
    karabo_da: List[str]
    ) -> Tuple[List[str], JUNGFRAUGeometry]:
    """ Initiate JUNGFRAUGeometry object based on the selected detector
    (JF4M, JF1M, or JF500K detectors).

    :param karabo_id: the detector identifer of an expected multimodular
        detector or a single module detector.
    :param karabo_da: the data aggregator names that defines the module name.
        This is more relevant for the single module detector to display the
        correct module name.
    :return: List of expected module names, JUNGFRAUGeometry object.

    """
    # Positions are given in pixels
    mod_width = (256 * 4) + (2 * 3)  # inc. 2px gaps between tiles
    mod_height = (256 * 2) + 2

    if "JF4M" in karabo_id:
        nmods = 8
        expected_modules = [f"JNGFR{i:02d}" for i in range(1, nmods+1)]
        # The first 4 modules are rotated 180 degrees relative to the others.
        # We pass the bottom, beam-right corner of the module regardless
        # of its orientation, requiring a subtraction from the
        # symmetric positions we'd otherwise calculate.
        x_start, y_start = 1125, 1078
        module_pos = [
            (x_start - mod_width, y_start - mod_height - (i*(mod_height+33)))
            for i in range(4)
        ] + [
            (-x_start, -y_start + (i*(mod_height + 33))) for i in range(4)
        ]
        orientations = [
            (-1, -1) for _ in range(4)] + [(1, 1) for _ in range(4)]
    elif "JF1M" in karabo_id:
        nmods = 2
        st_modno = 1
        # TODO: This is a temporary workaround. A proper solution is needed.
        if karabo_id == "SPB_CFEL_JF1M":
            st_modno = 9
        expected_modules = [f"JNGFR{i:02d}" for i in range(st_modno, st_modno+nmods)]
        module_pos = ((-mod_width//2, 33), (-mod_width//2, -mod_height-33))
        orientations = [(-1,-1), (1,1)]
    else:  # e.g. HED_IA1_JF500K1, FXE_XAD_JF500K, FXE_XAD_JFHZ
        nmods = 1
        expected_modules = karabo_da
        module_pos = ((-mod_width//2, -mod_height//2),)
        orientations = None

    return expected_modules, JUNGFRAUGeometry.from_module_positions(
        module_pos, orientations=orientations, asic_gap=6)

rebin(a, *args)

rebin ndarray data into a smaller ndarray of the same rank whose dimensions are factors of the original dimensions. eg. An array with 6 columns and 4 rows can be reduced to have 6,3,2 or 1 columns and 4,2 or 1 rows. example usages: https://scipy-cookbook.readthedocs.io/items/Rebinning.html

a=rand(6,4); b=rebin(a,3,2) a=rand(6); b=rebin(a,2)

Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def rebin(a, *args):
    '''rebin ndarray data into a smaller ndarray of the same rank whose
    dimensions are factors of the original dimensions. eg. An array with 6
    columns and 4 rows can be reduced to have 6,3,2 or 1 columns and 4,2 or 1
    rows. example usages:
    https://scipy-cookbook.readthedocs.io/items/Rebinning.html
    >>> a=rand(6,4); b=rebin(a,3,2)
    >>> a=rand(6); b=rebin(a,2)
    '''
    shape = a.shape
    lenShape = len(shape)
    factor = np.asarray(shape) // np.asarray(args)
    evList = ['a.reshape('] + \
             ['args[%d],factor[%d],' % (i, i) for i in range(lenShape)] + \
             [')'] + ['.sum(%d)' % (i + 1) for i in range(lenShape)] + \
             ['/factor[%d]' % i for i in range(lenShape - 1)]
    ta = eval(''.join(evList))
    return ta.astype(np.uint32), np.indices([s + 1 for s in ta.shape])

show_overview(d, cell_to_preview, gain_to_preview, out_folder=None, infix=None)

Show an overview :param d: A dict with the number of modules and a dict for the constant names and their data. :param cell_to_preview: Integer number of memory cells to preview. :param gain_to_preview: Integer number for the gain stages to preview. :param out_folder: Output folder for saving the plotted.png image. :param infix: infix to include in the png file name. :return:

Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def show_overview(
        d, cell_to_preview, gain_to_preview, out_folder=None, infix=None
):
    """
    Show an overview
    :param d: A dict with the number of modules and
        a dict for the constant names and their data.
    :param cell_to_preview: Integer number of memory cells to preview.
    :param gain_to_preview: Integer number for the gain stages to preview.
    :param out_folder: Output folder for saving the plotted.png image.
    :param infix: infix to include in the png file name.
    :return:
    """
    for module, data in d.items():
        # Adapt number of columns to number of constants in d.
        ncols = (len(d[module]) + (2 - 1)) // 2
        fig = plt.figure(figsize=(20, 20))
        grid = AxesGrid(fig, 111,
                        nrows_ncols=(2, ncols),
                        axes_pad=(0.9, 0.15),
                        label_mode="1",
                        share_all=True,
                        cbar_location="right",
                        cbar_mode="each",
                        cbar_size="7%",
                        cbar_pad="2%",
                        )

        items = list(data.items())
        for ax, cbar_ax, (key, item) in zip(grid, grid.cbar_axes, items):
            cf = 0
            if "ThresholdsDark" in key:
                cf = -1
            if len(item.shape) == 4:
                med = np.nanmedian(item[..., cell_to_preview,
                                        gain_to_preview + cf])
            else:
                med = np.nanmedian(item[..., cell_to_preview])
            medscale = med
            if med == 0:
                medscale = 0.1

            bound = 0.2
            while (np.count_nonzero((item[..., cell_to_preview, gain_to_preview + cf] < med - np.abs(bound * medscale)) |  # noqa
                                    (item[..., cell_to_preview, gain_to_preview + cf] > med + np.abs(bound * medscale))) /  # noqa
                   item[..., cell_to_preview, gain_to_preview + cf].size > 0.01):  # noqa
                bound *= 2

            is_badpixels = "BadPixels" in key

            if is_badpixels:
                im = ax.imshow(
                    item[..., cell_to_preview, gain_to_preview + cf] != 0,
                    cmap=plt.cm.colors.ListedColormap(["w", "k"]),
                    aspect="auto",
                )
            else:

                if len(item.shape) == 4:
                    im_prev = item[..., cell_to_preview, gain_to_preview + cf]
                    vmax = np.abs(med + bound * med)
                else:
                    im_prev = item[..., cell_to_preview]
                    # move the axis of the image to show horizontally
                    # on the output report.
                    if im_prev.shape[0] > im_prev.shape[1]:
                        im_prev = np.moveaxis(item[..., cell_to_preview], 0, 1)
                    vmax = med + np.abs(bound * medscale)

                im = ax.imshow(im_prev, interpolation="nearest",
                               vmin=med - np.abs(bound * medscale),
                               vmax=vmax, aspect='auto')

            cb = cbar_ax.colorbar(im)
            if is_badpixels:
                cb.set_ticks([0.25, 0.75])
                cb.set_ticklabels(["good", "bad"])
            else:
                cb.set_label("ADU")

            ax.text(
                5, 20, key, color="k" if is_badpixels else "w", fontsize=20
            )

        grid[0].text(5, 50, module, color="k" if "BadPixels" in items[0][0] else "r", fontsize=20)  # noqa

        if out_folder and infix:
            fig.savefig(f"{out_folder}/"
                        f"dark_analysis_{infix}_module_{module}.png")

show_processed_modules(detector, constants, mnames, mode)

Show the status of the processed modules. Green: Processed. Gray: Not Processed. Red: No data available.

Parameters:

Name Type Description Default
detector str

The detector name (karabo_id) (e.g. MID_DET_AGIPD1M-1 or FXE_DET_LPD1M-1)

required
constants Optional[Dict[str, Any]]

A dict of the plotted constants data. {"const_name":constant_data}. Can be None in case of position mode.

required
mnames str

A list of available module names.

required
mode str

String selecting on of the two modes of operation. "position": To just show the position of the processed modules. "processed": To show the modules successfully processed.

required
Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def show_processed_modules(
    detector: str,
    constants: Optional[Dict[str, Any]],
    mnames: str,
    mode: str
):
    """
    Show the status of the processed modules.
    Green: Processed. Gray: Not Processed. Red: No data available.
    Args:
        detector: The detector name (karabo_id) (e.g. MID_DET_AGIPD1M-1 or FXE_DET_LPD1M-1)
        constants: A dict of the plotted constants data.
            {"const_name":constant_data}. Can be None in case of position mode.
        mnames: A list of available module names.
        mode: String selecting on of the two modes of operation.
            "position": To just show the position of the processed modules.
            "processed": To show the modules successfully processed.
    """

    # Create the geometry figure for each detector

    if "AGIPD1M" in detector:
        quadrants = 4
        modules = 4
        tiles = 8
        quad_pos = [(-525, 625), (-550, -10), (520, -160), (542.5, 475)]
        geom = AGIPD_1MGeometry.from_quad_positions(quad_pos)

    elif "AGIPD500K" in detector:
        quadrants = 2
        modules = 4
        tiles = 8
        geom = AGIPD_500K2GGeometry.from_origin()

    elif "AGIPD65K" in detector:
        quadrants = 1
        modules = 1
        tiles = 8
        geom = agipd_single_module_geometry()

    elif 'LPD' in detector:
        quadrants = 4
        modules = 4
        tiles = 16
        quad_pos = [(11.4, 299), (-11.5, 8), (254.5, -16), (278.5, 275)]
        geom = LPD_1MGeometry.from_quad_positions(quad_pos)

    elif 'DSSC' in detector:
        quadrants = 4
        modules = 4
        tiles = 2
        quad_pos = [(-130, 5), (-130, -125), (5, -125), (5, 5)]

        geom = DSSC_1MGeometry.from_h5_file_and_quad_positions(
            Path(eg_tests.__file__).parent / 'dssc_geo_june19.h5',
            quad_pos)

    else:
        raise ValueError(f'{detector} detector is not available for plotting.')

    # Create a dict that contains the range of tiles, in the figure,
    # that belong to a module.
    ranges = {}
    tile_count = 0
    for quadrant in range(1, quadrants+1):
        for module in range(1, modules+1):
            ranges[f'Q{quadrant}M{module}'] = [tile_count, tile_count + tiles]
            tile_count += tiles

    # Create the figure
    ax = geom.inspect()
    ax.set_title('')  # Cannot remove title
    ax.set_axis_off()
    ax.get_legend().set_visible(False)

    # Remove non-tiles markings from figure
    tiles, = ax.collections = ax.collections[:1]

    # Set each tile colour individually, extra_geom provides a single color
    # for all tiles.
    facecolors = np.repeat(tiles.get_facecolor(), tile_count, axis=0)

    # Set module name fonts
    for text in ax.texts:
        text.set_fontweight('regular')

    texts = [t for t in ax.texts if t.get_text() in mnames]
    for text in texts:
        text.set_fontweight('extra bold')
        text.set_fontsize(14)

    if mode == 'position':  # Highlight selected modules
        for module in mnames:
            start, stop = ranges[module]
            facecolors[start:stop] = colors.to_rgba('pink')

    else:  # mode == 'processed': Highlight processed modules
        counter = 0  # Used as index within the `Noise` matrix
        for module, (start, stop) in ranges.items():
            color = 'grey'  # Unprocessed modules are grey

            if module in mnames:
                color = 'green'
                if ('Noise' not in constants.keys() or
                        np.nanmean(constants['Noise'][counter, ..., 0]) == 0):  # noqa
                    color = 'red'
                counter += 1

            for idx in range(start, stop):  # Set the colours
                facecolors[idx] = colors.to_rgba(color)

    tiles.set_facecolors(facecolors)  # Update colours in figure

    if mode == "processed":
        _ = ax.legend(handles=[Patch(facecolor='red', label='No data'),
                               Patch(facecolor='gray', label='Not processed'),
                               Patch(facecolor='green', label='Processed')],
                      loc='best', ncol=3, bbox_to_anchor=(0.1, 0.25, 0.7, 0.8))
    plt.show()

show_processed_modules_jungfrau(jungfrau_geom, constants, processed_modules, expected_modules=None, display_module_names=None)

Show the status of the processed modules. Green: Processed. Gray: Not Processed. Red: No data available. :param geom: JUNGFRAUGeometry object to change the colors of it based on the processed modules. :param constants: A dict of the plotted constants data. {"const_name":constant_data}. Can be None in case of position mode. :param processed_modules: A list of processed module names. :param expected_modules: A list of full module names expected for this detector. :param display_module_names: A list of the module names to display. :return

Source code in /usr/src/app/checkouts/readthedocs.org/user_builds/european-xfel-offline-calibration/envs/latest/lib/python3.8/site-packages/cal_tools/plotting.py
def show_processed_modules_jungfrau(
    jungfrau_geom: JUNGFRAUGeometry,
    constants: Optional[Dict[str, Any]],
    processed_modules: List[str],
    expected_modules: Optional[List[str]]=None,
    display_module_names: Optional[List[str]] = None,
    ):
    """ Show the status of the processed modules.
    `Green`: Processed. `Gray`: Not Processed. `Red`: No data available.
    :param geom: JUNGFRAUGeometry object to change the colors of
    it based on the processed modules.
    :param constants: A dict of the plotted constants data.
        {"const_name":constant_data}. Can be None in case of position mode.
    :param processed_modules: A list of processed module names.
    :param expected_modules: A list of full module names expected
        for this detector.
    :param display_module_names: A list of the module names to display.
    :return
    """
    # Estimate expected modules from number of modules in extra_geom object.
    # This will work fine as long as the related modules start from JNGFR01
    # incrementaly for the number of modules available.
    if expected_modules is None:
        expected_modules = [
            f"JNGFR{i:02d}" for i in len(jungfrau_geom.modules)]

    # Create the figure
    ax = jungfrau_geom.inspect(
        module_names=[
            f'{mod}\n{pdu}' for mod, pdu in zip(
                processed_modules, display_module_names)])
    ax.set_title('')  # Cannot remove title
    ax.set_axis_off()
    ax.get_legend().set_visible(False)

    # Set each module colour individually,
    # extra_geom provides a single color for all modules.
    modules, = ax.collections = ax.collections[:1]
    facecolors = np.repeat(modules.get_facecolor(), len(expected_modules), axis=0)

    counter = 0  # Used as index within the `Noise` matrix
    for idx, module in enumerate(expected_modules):
        color = 'grey'  # Unprocessed modules are grey
        if module in processed_modules:
            color = 'green'
            if (
                'Noise' not in constants.keys() or
                np.nanmean(constants['Noise'][counter, ..., 0]) == 0
                ):
                color = 'red'
            counter += 1

        # Set the colours
        facecolors[idx] = colors.to_rgba(color)

    modules.set_facecolors(facecolors)  # Update colours in figure

    ax.legend(
        handles=[
            Patch(facecolor='red', label='No data'),
            Patch(facecolor='gray', label='Not processed'),
            Patch(facecolor='green', label='Processed'),],
        loc='best', ncol=3,
        bbox_to_anchor=(0.1, 0.25, 0.7, 0.8))
    plt.show()