Source code for dodal.devices.detector.det_resolution

from math import atan, sin

from dodal.devices.detector import DetectorParams
from dodal.devices.detector.det_dist_to_beam_converter import (
    Axis,
)


def _get_detector_radius_mm(detector_params: DetectorParams):
    return 0.5 * _get_detector_max_size_mm(detector_params)


def _get_detector_max_size_mm(detector_params):
    return max(
        detector_params.detector_size_constants.det_dimension.width,
        detector_params.detector_size_constants.det_dimension.height,
    )


def _get_beam_xy_accounting_for_roi(detector_params, det_distance_mm):
    beam_x = detector_params.beam_xy_converter.get_beam_xy_from_det_dist(
        det_distance_mm, Axis.X_AXIS
    )
    beam_y = detector_params.beam_xy_converter.get_beam_xy_from_det_dist(
        det_distance_mm, Axis.Y_AXIS
    )
    if detector_params.use_roi_mode:
        size_constants = detector_params.detector_size_constants
        offset_x = 0.5 * (
            size_constants.det_dimension.width - size_constants.roi_dimension.width
        )
        offset_y = 0.5 * (
            size_constants.det_dimension.height - size_constants.roi_dimension.height
        )
        beam_x = beam_x - offset_x
        beam_y = beam_y - offset_y

    return beam_x, beam_y


def _calc_useful_radius(detector_radius_mm, beam_x_mm, beam_y_mm):
    beam_x_from_centre = abs(beam_x_mm - detector_radius_mm)
    beam_y_from_centre = abs(beam_y_mm - detector_radius_mm)
    return detector_radius_mm - max(beam_x_from_centre, beam_y_from_centre)


def _calc_res_at_angle(wavelength_angstroms, angular_shift_radians):
    """Base definition of maximum resolution (from Bragg's Law with n=1)"""
    return 0.5 * wavelength_angstroms / sin(angular_shift_radians)


def _calc_res_off_axis_detector(
    wavelength_angstroms, usable_radius, det_distance_mm, two_theta_radians
):
    """
    Calculate maximum resolution given detector distance and extent (radius) that detector face extends
    No correction for position of beam centre on detector face at twoTheta=0
    Here radius and distance parameters can be in any length unit, provided it is the same, millimetres is the convention
    """
    angular_shift_radians = atan(usable_radius / det_distance_mm)
    return _calc_res_at_angle(
        wavelength_angstroms, 0.5 * (angular_shift_radians + two_theta_radians)
    )


def _max_res_for_mx(
    wavelength_angstroms, detector_radius_mm, det_distance_mm, beam_x_mm, beam_y_mm
):
    """
    Calculate maximum resolution given MX Use Case (detector at twoTheta=0) and beam centre on detector face
    Correct the radius for position of beam centre on detector face at twoTheta=0
    """
    usable_radius = _calc_useful_radius(detector_radius_mm, beam_x_mm, beam_y_mm)
    return _calc_res_off_axis_detector(
        wavelength_angstroms, usable_radius, det_distance_mm, 0
    )


[docs] def resolution( detector_params: DetectorParams, wavelength_angstroms: float, det_distance_mm: float ): detector_radius_mm = _get_detector_radius_mm(detector_params) (beam_x_mm, beam_y_mm) = _get_beam_xy_accounting_for_roi( detector_params, det_distance_mm ) return _max_res_for_mx( wavelength_angstroms, detector_radius_mm, det_distance_mm, beam_x_mm, beam_y_mm )