diff --git a/common/src/aaredaqlib/find_xtal.py b/common/src/aaredaqlib/find_xtal.py index 72afcd38..cac88e4f 100644 --- a/common/src/aaredaqlib/find_xtal.py +++ b/common/src/aaredaqlib/find_xtal.py @@ -4,8 +4,8 @@ import numpy as np from scipy import ndimage from aaredaqlib.coordinate import Coordinate -from aaredaqlib.models import CrystalSize, CenterOfMassModel -from aaredaqlib.raster_grid import RasterGridRequest +from aaredaqlib.models import CrystalSize +from aaredaqlib.raster_grid import RasterGridRequest, CenterOfMassModel from aaredaqlib.logger_config import setup_logger logger = setup_logger('aareDAQ') diff --git a/common/src/aaredaqlib/models.py b/common/src/aaredaqlib/models.py index 486ad7c2..1eb4e2d6 100644 --- a/common/src/aaredaqlib/models.py +++ b/common/src/aaredaqlib/models.py @@ -9,7 +9,6 @@ from pydantic import BaseModel, Field, field_validator, AfterValidator from aaredaqlib.coordinate import Coordinate, positive_coords from aaredaqlib.diffraction_geometry import DiffractionGeometry -from aaredaqlib.raster_grid import RasterGridRequest from aaredaqlib.sample_geometry import SampleGeometryModel from jfjoch_client.models.scan_result import ScanResult @@ -409,16 +408,6 @@ class SampleShortInfo(BaseModel): class SampleShortInfoList(BaseModel): s: List[SampleShortInfo] -class SimpleScanParameters(BaseModel): - filename: Optional[str] = None - dtz: float = 150 - exp_time_s: float = 0.02 - start_omega_deg:float = 0 - incr_omega_deg: float = 0.2 - steps: int = 1800 - transmission: Annotated[float, Field(ge=0.0, le=1.0)] = 1.0 - - class BeamMarkCoeffModel(BaseModel): """ Model to calculate beam center for a given zoom level based on a quadratic approximation @@ -780,41 +769,4 @@ class SimpleScanParameters(BaseModel): beam_size_y_um: float | None = None dose_rate_MGy_s: float | None = None d_vis: float | None = None - d_tar: float | None = None - -class CenterOfMassModel(BaseModel): - n_x: float - n_y: float - max_image: int | None = None - - @classmethod - def model_validate_maybe(cls, n_x: float, n_y: float) -> "CenterOfMassModel | None": - # Return None if either coordinate is NaN - if np.isnan(n_x) or np.isnan(n_y): - return None - return cls(n_x=float(n_x), n_y=float(n_y)) - - def get_com_mm(self, r:RasterGridRequest) -> Coordinate: - #defines COM in mm at beam position, relative to the top left corner of grid - return Coordinate(x=(self.n_x+0.5)*r.grid_size_mm.x, y=(self.n_y+0.5)*r.grid_size_mm.y) - - def get_com_pxl(self, r:RasterGridRequest, geom:SampleGeometryModel) -> Coordinate: - #defines COM in pixels at beam position, relative to the top left corner of grid - com_mm = self.get_com_mm(r) - return Coordinate(x=com_mm.x/geom.pixel_in_mm, y=com_mm.y/geom.pixel_in_mm) - - def com_nan_check(self): - # True if both values are finite (not NaN) - return not (np.isnan(self.n_x) or np.isnan(self.n_y)) - -class RasterPayloadModel(BaseModel): - request: RasterGridRequest - result: ScanResult - sample_id: int - attach_image: bool = True - centre_of_mass: CenterOfMassModel | None = None - center_pxl: Coordinate | None - start_pxl: Coordinate - cell_size_pxl: Annotated[Coordinate, AfterValidator(positive_coords)] - beam_mark_pxl: tuple[float, float] - beam_size_mm: Annotated[Coordinate, AfterValidator(positive_coords)] \ No newline at end of file + d_tar: float | None = None \ No newline at end of file diff --git a/common/src/aaredaqlib/raster_grid.py b/common/src/aaredaqlib/raster_grid.py index 7fc93c41..05d32d80 100644 --- a/common/src/aaredaqlib/raster_grid.py +++ b/common/src/aaredaqlib/raster_grid.py @@ -1,5 +1,7 @@ from typing import Annotated, List +import numpy as np + from jfjoch_client.models.scan_result import ScanResult from pydantic import Field, AfterValidator, BaseModel @@ -24,6 +26,7 @@ class RasterGridRequest(BaseModel): # If None, measure from the current position. smargon_top_left: SmargonCoordinate | None + # omega angle omega_deg: float = 0.0 @@ -40,9 +43,48 @@ class RasterGridRequest(BaseModel): return geom.smargon_to_beamline(geom.smargon.sh_mm) return geom.smargon_to_beamline(self.smargon_top_left.sh_mm) +class CenterOfMassModel(BaseModel): + n_x: float + n_y: float + max_image: int | None = None + + @classmethod + def model_validate_maybe(cls, n_x: float, n_y: float) -> "CenterOfMassModel | None": + # Return None if either coordinate is NaN + if np.isnan(n_x) or np.isnan(n_y): + return None + return cls(n_x=float(n_x), n_y=float(n_y)) + + def get_com_mm(self, r:RasterGridRequest) -> Coordinate: + #defines COM in mm at beam position, relative to the top left corner of grid + return Coordinate(x=(self.n_x+0.5)*r.grid_size_mm.x, y=(self.n_y+0.5)*r.grid_size_mm.y) + + def get_com_pxl(self, r:RasterGridRequest, geom:SampleGeometryModel) -> Coordinate: + #defines COM in pixels at beam position, relative to the top left corner of grid + com_mm = self.get_com_mm(r) + return Coordinate(x=com_mm.x/geom.pixel_in_mm, y=com_mm.y/geom.pixel_in_mm) + + def com_nan_check(self): + # True if both values are finite (not NaN) + return not (np.isnan(self.n_x) or np.isnan(self.n_y)) + class CompletedRasterGridElem(BaseModel): request: RasterGridRequest result: ScanResult + centre_of_mass: CenterOfMassModel | None class CompletedRasterGrid(BaseModel): r: List[CompletedRasterGridElem] + +class RasterPayloadModel(BaseModel): + request: RasterGridRequest + result: ScanResult + sample_id: int + attach_image: bool = True + centre_of_mass: CenterOfMassModel | None = None + center_pxl: Coordinate | None + start_pxl: Coordinate + cell_size_pxl: Annotated[Coordinate, AfterValidator(positive_coords)] + beam_mark_pxl: tuple[float, float] + beam_size_mm: Annotated[Coordinate, AfterValidator(positive_coords)] + diff --git a/daq/src/aaredaq/aaredb.py b/daq/src/aaredaq/aaredb.py index f208dd8e..9ff01df2 100644 --- a/daq/src/aaredaq/aaredb.py +++ b/daq/src/aaredaq/aaredb.py @@ -29,11 +29,11 @@ from aaredaqlib.models import ( PuckLoadedInfo, DewarAddress, SampleShortInfoList, - DAQStatusModel, SessionStatus, RasterPayloadModel, CenterOfMassModel, + DAQStatusModel, SessionStatus, ) from aaredaqlib.beamline import MXBeamline -from aaredaqlib.raster_grid import RasterGridRequest, CompletedRasterGrid +from aaredaqlib.raster_grid import RasterGridRequest, CompletedRasterGrid, RasterPayloadModel, CenterOfMassModel from aaredaqlib.rotation_scan import RotationScanRequest from aaredaqlib.sample_geometry import SampleGeometryModel diff --git a/daq/src/aaredaq/daq.py b/daq/src/aaredaq/daq.py index 492e7ae2..e43a48fa 100644 --- a/daq/src/aaredaq/daq.py +++ b/daq/src/aaredaq/daq.py @@ -603,7 +603,7 @@ class AareDAQ: except Exception as e: logger.error(f"Exception ingesting grid scan: {e}") self.__devs.reflector_up = False - return CompletedRasterGridElem(request=copy.deepcopy(r), result=result) + return CompletedRasterGridElem(request=copy.deepcopy(r), result=result, centre_of_mass=com) def measure_raster(self, r: RasterGridRequest, auto: bool) -> CompletedRasterGrid: self.__cfg.try_set_busy(timeout=ceil(360)) diff --git a/gui/src/aaregui/scan_logic/raster_grid_manager.py b/gui/src/aaregui/scan_logic/raster_grid_manager.py index 85392b4b..986cd0bc 100644 --- a/gui/src/aaregui/scan_logic/raster_grid_manager.py +++ b/gui/src/aaregui/scan_logic/raster_grid_manager.py @@ -235,7 +235,8 @@ class RasterGridManager(QObject): if 0 <= cell_x < n_x and 0 <= cell_y < n_y: cell = cell_y * n_x + cell_x - if self.__loaded_image_prefix != grid.result.file_prefix or self.__loaded_image_index != grid.result.images[cell].number: + if (self.__loaded_image_prefix != grid.result.file_prefix or + self.__loaded_image_index != grid.result.images[cell].number): self.__loaded_image_prefix = grid.result.file_prefix self.__loaded_image_index = grid.result.images[cell].number print(f"Load {grid.result.file_prefix} {grid.result.images[cell].number}") @@ -454,7 +455,12 @@ class RasterGridManager(QObject): self.__active_grid.n_x = 0 self.__active_grid.n_y = 0 if last_raster is not None and last_raster.result.file_prefix is not None: - self.image_selected.emit("http://sls-gpu-001:8080",0) #last_raster.result.file_prefix, 0) + com = last_raster.centre_of_mass + if com: + cell = com.max_image + else: + cell = 0 + self.image_selected.emit("http://sls-gpu-001:8080",cell) #last_raster.result.file_prefix, 0) self.completed_grid_updated.emit() @Slot(int, bool)