Source code for dodal.devices.detector.detector
from enum import Enum, auto
from pathlib import Path
from pydantic import BaseModel, Field, field_serializer, field_validator
from dodal.devices.detector.det_dim_constants import (
EIGER2_X_16M_SIZE,
DetectorSize,
DetectorSizeConstants,
constants_from_type,
)
from dodal.devices.detector.det_dist_to_beam_converter import (
Axis,
DetectorDistanceToBeamXYConverter,
)
from dodal.utils import get_run_number
[docs]
class TriggerMode(Enum):
"""In set frames the number of frames is known at arm time. In free run they are
not known until the detector is unstaged."""
SET_FRAMES = auto()
FREE_RUN = auto()
[docs]
class DetectorParams(BaseModel):
"""Holds parameters for the detector. Provides access to a list of Dectris detector
sizes and a converter for distance to beam centre."""
# https://github.com/pydantic/pydantic/issues/8379
# Must use model_dump(by_alias=True) if serialising!
expected_energy_ev: float | None = None
exposure_time: float
directory: str # : Path https://github.com/DiamondLightSource/dodal/issues/774
prefix: str
detector_distance: float
omega_start: float
omega_increment: float
num_images_per_trigger: int
num_triggers: int
use_roi_mode: bool
det_dist_to_beam_converter_path: str
override_run_number: int | None = Field(default=None, alias="run_number")
trigger_mode: TriggerMode = TriggerMode.SET_FRAMES
detector_size_constants: DetectorSizeConstants = EIGER2_X_16M_SIZE
enable_dev_shm: bool = (
False # Remove in https://github.com/DiamondLightSource/hyperion/issues/1395
)
@property
def beam_xy_converter(self) -> DetectorDistanceToBeamXYConverter:
return DetectorDistanceToBeamXYConverter(self.det_dist_to_beam_converter_path)
@property
def run_number(self) -> int:
return (
get_run_number(self.directory, self.prefix)
if self.override_run_number is None
else self.override_run_number
)
@field_serializer("detector_size_constants")
def serialize_detector_size_constants(self, size: DetectorSizeConstants):
return size.det_type_string
@field_validator("detector_size_constants", mode="before")
@classmethod
def _parse_detector_size_constants(cls, det_type: str) -> DetectorSizeConstants:
return (
det_type
if isinstance(det_type, DetectorSizeConstants)
else constants_from_type(det_type)
)
@field_validator("directory", mode="before")
@classmethod
def _parse_directory(cls, directory: str | Path) -> str:
path = Path(directory)
assert path.is_dir()
return f"{path}/"
def get_beam_position_mm(self, detector_distance: float) -> tuple[float, float]:
x_beam_mm = self.beam_xy_converter.get_beam_xy_from_det_dist(
detector_distance, Axis.X_AXIS
)
y_beam_mm = self.beam_xy_converter.get_beam_xy_from_det_dist(
detector_distance, Axis.Y_AXIS
)
full_size_mm = self.detector_size_constants.det_dimension
roi_size_mm = (
self.detector_size_constants.roi_dimension
if self.use_roi_mode
else full_size_mm
)
offset_x = (full_size_mm.width - roi_size_mm.width) / 2.0
offset_y = (full_size_mm.height - roi_size_mm.height) / 2.0
return x_beam_mm - offset_x, y_beam_mm - offset_y
def get_detector_size_pizels(self) -> DetectorSize:
full_size = self.detector_size_constants.det_size_pixels
roi_size = self.detector_size_constants.roi_size_pixels
return roi_size if self.use_roi_mode else full_size
def get_beam_position_pixels(self, detector_distance: float) -> tuple[float, float]:
full_size_pixels = self.detector_size_constants.det_size_pixels
roi_size_pixels = self.get_detector_size_pizels()
x_beam_pixels = self.beam_xy_converter.get_beam_x_pixels(
detector_distance,
full_size_pixels.width,
self.detector_size_constants.det_dimension.width,
)
y_beam_pixels = self.beam_xy_converter.get_beam_y_pixels(
detector_distance,
full_size_pixels.height,
self.detector_size_constants.det_dimension.height,
)
offset_x = (full_size_pixels.width - roi_size_pixels.width) / 2.0
offset_y = (full_size_pixels.height - roi_size_pixels.height) / 2.0
return x_beam_pixels - offset_x, y_beam_pixels - offset_y
@property
def full_filename(self):
return f"{self.prefix}_{self.run_number}"
@property
def full_number_of_images(self):
return self.num_triggers * self.num_images_per_trigger