diff --git a/setup.cfg b/setup.cfg index ff8900e54..9a9ac56de 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,7 @@ install_requires = xarray doct databroker - dodal @ git+https://github.com/DiamondLightSource/python-dodal.git + dodal @ git+https://github.com/DiamondLightSource/python-dodal.git@0570e5e6e5e134fd0697cadccc5bd6d2c6ed77d1 [options.extras_require] diff --git a/src/artemis/experiment_plans/fast_grid_scan_plan.py b/src/artemis/experiment_plans/fast_grid_scan_plan.py index 0cc05faca..9e632e263 100755 --- a/src/artemis/experiment_plans/fast_grid_scan_plan.py +++ b/src/artemis/experiment_plans/fast_grid_scan_plan.py @@ -5,6 +5,7 @@ import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp +import numpy as np from bluesky import RunEngine from bluesky.utils import ProgressBarManager from dodal.beamlines import i03 @@ -36,7 +37,6 @@ ) from artemis.parameters.constants import ISPYB_PLAN_NAME, SIM_BEAMLINE from artemis.tracing import TRACER -from artemis.utils.utils import Point3D if TYPE_CHECKING: from artemis.external_interaction.callbacks.fgs.fgs_callback_collection import ( @@ -146,7 +146,7 @@ def read_hardware_for_ispyb( @bpp.run_decorator(md={"subplan_name": "move_xyz"}) def move_xyz( sample_motors, - xray_centre_motor_position: Point3D, + xray_centre_motor_position: np.ndarray, md={ "plan_name": "move_xyz", }, @@ -156,11 +156,11 @@ def move_xyz( artemis.log.LOGGER.info(f"Moving Smargon x, y, z to: {xray_centre_motor_position}") yield from bps.mv( sample_motors.x, - xray_centre_motor_position.x, + xray_centre_motor_position[0], sample_motors.y, - xray_centre_motor_position.y, + xray_centre_motor_position[1], sample_motors.z, - xray_centre_motor_position.z, + xray_centre_motor_position[2], ) @@ -242,10 +242,12 @@ def run_gridscan_and_move( and moves to the centre of mass determined by zocalo""" # We get the initial motor positions so we can return to them on zocalo failure - initial_xyz = Point3D( - (yield from bps.rd(fgs_composite.sample_motors.x)), - (yield from bps.rd(fgs_composite.sample_motors.y)), - (yield from bps.rd(fgs_composite.sample_motors.z)), + initial_xyz = np.array( + [ + (yield from bps.rd(fgs_composite.sample_motors.x)), + (yield from bps.rd(fgs_composite.sample_motors.y)), + (yield from bps.rd(fgs_composite.sample_motors.z)), + ] ) yield from setup_zebra_for_fgs(fgs_composite.zebra) diff --git a/src/artemis/experiment_plans/full_grid_scan.py b/src/artemis/experiment_plans/full_grid_scan.py index f5b82e128..a4785c014 100644 --- a/src/artemis/experiment_plans/full_grid_scan.py +++ b/src/artemis/experiment_plans/full_grid_scan.py @@ -23,7 +23,6 @@ from artemis.log import LOGGER from artemis.parameters.beamline_parameters import get_beamline_parameters from artemis.parameters.plan_specific.fgs_internal_params import GridScanParams -from artemis.utils.utils import Point3D if TYPE_CHECKING: from artemis.parameters.plan_specific.grid_scan_with_edge_detect_params import ( @@ -102,7 +101,7 @@ def detect_grid_and_do_gridscan(): parameters.artemis_params.ispyb_params.xtal_snapshots_omega_end = ( out_snapshot_filenames[1] ) - parameters.artemis_params.ispyb_params.upper_left = Point3D(**out_upper_left) + parameters.artemis_params.ispyb_params.upper_left = out_upper_left fgs_params.__post_init__() diff --git a/src/artemis/experiment_plans/oav_grid_detection_plan.py b/src/artemis/experiment_plans/oav_grid_detection_plan.py index df96b1bbe..2733c959b 100644 --- a/src/artemis/experiment_plans/oav_grid_detection_plan.py +++ b/src/artemis/experiment_plans/oav_grid_detection_plan.py @@ -2,7 +2,7 @@ import math from os.path import join as path_join -from typing import TYPE_CHECKING, Dict, List +from typing import TYPE_CHECKING, List import bluesky.plan_stubs as bps import numpy as np @@ -32,7 +32,7 @@ def grid_detection_plan( snapshot_template: str, snapshot_dir: str, out_snapshot_filenames: List[List[str]], - out_upper_left: Dict, + out_upper_left: list[float] | np.ndarray, width=600, box_size_microns=20, ): @@ -57,7 +57,7 @@ def grid_detection_main_plan( snapshot_template: str, snapshot_dir: str, out_snapshot_filenames: List[List[str]], - out_upper_left: Dict, + out_upper_left: list[float] | np.ndarray, grid_width_px: int, box_size_um: float, ): @@ -133,10 +133,10 @@ def grid_detection_main_plan( upper_left = (tip_x_px, min_y) if angle == 0: - out_upper_left["x"] = int(tip_x_px) - out_upper_left["y"] = int(min_y) + out_upper_left[0] = int(tip_x_px) + out_upper_left[1] = int(min_y) else: - out_upper_left["z"] = int(min_y) + out_upper_left[2] = int(min_y) yield from bps.abs_set(oav.snapshot.top_left_x, upper_left[0]) yield from bps.abs_set(oav.snapshot.top_left_y, upper_left[1]) diff --git a/src/artemis/experiment_plans/tests/test_fast_grid_scan_plan.py b/src/artemis/experiment_plans/tests/test_fast_grid_scan_plan.py index 953ecf1ed..ded0516bc 100644 --- a/src/artemis/experiment_plans/tests/test_fast_grid_scan_plan.py +++ b/src/artemis/experiment_plans/tests/test_fast_grid_scan_plan.py @@ -2,6 +2,7 @@ from unittest.mock import ANY, MagicMock, call, patch import bluesky.plan_stubs as bps +import numpy as np import pytest from bluesky.run_engine import RunEngine from dodal.devices.det_dim_constants import ( @@ -38,7 +39,6 @@ from artemis.log import set_up_logging_handlers from artemis.parameters import external_parameters from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D def test_given_full_parameters_dict_when_detector_name_used_and_converted_then_detector_constants_correct( @@ -177,11 +177,11 @@ def test_results_passed_to_move_motors( set_up_logging_handlers(logging_level="INFO", dev_mode=True) RE.subscribe(VerbosePlanExecutionLoggingCallback()) motor_position = test_params.experiment_params.grid_position_to_motor_position( - Point3D(1, 2, 3) + np.array([1, 2, 3]) ) RE(move_xyz(fake_fgs_composite.sample_motors, motor_position)) bps_mv.assert_called_once_with( - ANY, motor_position.x, ANY, motor_position.y, ANY, motor_position.z + ANY, motor_position[0], ANY, motor_position[1], ANY, motor_position[2] ) @@ -215,9 +215,9 @@ def test_individual_plans_triggered_once_and_only_once_in_composite_run( ) run_gridscan.assert_called_once_with(fake_fgs_composite, test_params) - move_xyz.assert_called_once_with( - ANY, Point3D(x=-0.05, y=0.05, z=0.15000000000000002) - ) + array_arg = move_xyz.call_args.args[1] + np.testing.assert_allclose(array_arg, np.array([-0.05, 0.05, 0.15])) + move_xyz.assert_called_once() @patch( @@ -250,9 +250,9 @@ def test_logging_within_plan( ) run_gridscan.assert_called_once_with(fake_fgs_composite, test_params) - move_xyz.assert_called_once_with( - ANY, Point3D(x=-0.05, y=0.05, z=0.15000000000000002) - ) + array_arg = move_xyz.call_args.args[1] + np.testing.assert_array_almost_equal(array_arg, np.array([-0.05, 0.05, 0.15])) + move_xyz.assert_called_once() @patch("artemis.experiment_plans.fast_grid_scan_plan.bps.sleep") diff --git a/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py b/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py index 57f8e2189..9d96ca934 100644 --- a/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py +++ b/src/artemis/external_interaction/callbacks/fgs/tests/test_fgs_callback_collection.py @@ -1,5 +1,6 @@ from unittest.mock import MagicMock +import numpy as np import pytest from bluesky.run_engine import RunEngine from dodal.devices.eiger import DetectorParams, EigerDetector @@ -14,7 +15,6 @@ from artemis.parameters.constants import SIM_BEAMLINE from artemis.parameters.external_parameters import from_file as default_raw_params from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D def test_callback_collection_init(): @@ -90,7 +90,7 @@ def test_communicator_in_composite_run( callbacks.zocalo_handler._wait_for_result = MagicMock() callbacks.zocalo_handler._run_end = MagicMock() callbacks.zocalo_handler._run_start = MagicMock() - callbacks.zocalo_handler.xray_centre_motor_position = Point3D(1, 2, 3) + callbacks.zocalo_handler.xray_centre_motor_position = np.array([1, 2, 3]) fast_grid_scan_composite = FGSComposite() # this is where it's currently getting stuck: diff --git a/src/artemis/external_interaction/callbacks/fgs/tests/test_zocalo_handler.py b/src/artemis/external_interaction/callbacks/fgs/tests/test_zocalo_handler.py index 0f7938664..7b318de92 100644 --- a/src/artemis/external_interaction/callbacks/fgs/tests/test_zocalo_handler.py +++ b/src/artemis/external_interaction/callbacks/fgs/tests/test_zocalo_handler.py @@ -1,6 +1,6 @@ -import operator from unittest.mock import MagicMock, call +import numpy as np import pytest from artemis.external_interaction.callbacks.fgs.fgs_callback_collection import ( @@ -11,7 +11,6 @@ from artemis.external_interaction.zocalo.zocalo_interaction import NoDiffractionFound from artemis.parameters.external_parameters import from_file as default_raw_params from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D EXPECTED_DCID = 100 EXPECTED_RUN_START_MESSAGE = {"event": "start", "ispyb_dcid": EXPECTED_DCID} @@ -82,7 +81,7 @@ def test_zocalo_called_to_wait_on_results_when_communicator_wait_for_results_cal callbacks = FGSCallbackCollection.from_params(dummy_params) mock_zocalo_functions(callbacks) callbacks.ispyb_handler.ispyb_ids = (0, 0, 100) - expected_centre_grid_coords = Point3D(1, 2, 3) + expected_centre_grid_coords = np.array([1, 2, 3]) single_crystal_result = [ { "max_voxel": [1, 2, 3], @@ -95,20 +94,16 @@ def test_zocalo_called_to_wait_on_results_when_communicator_wait_for_results_cal single_crystal_result ) - found_centre = callbacks.zocalo_handler.wait_for_results(Point3D(0, 0, 0))[0] + found_centre = callbacks.zocalo_handler.wait_for_results(np.array([0, 0, 0]))[0] callbacks.zocalo_handler.zocalo_interactor.wait_for_result.assert_called_once_with( 100 ) expected_centre_motor_coords = ( dummy_params.experiment_params.grid_position_to_motor_position( - Point3D( - expected_centre_grid_coords.x - 0.5, - expected_centre_grid_coords.y - 0.5, - expected_centre_grid_coords.z - 0.5, - ) + expected_centre_grid_coords - 0.5 ) ) - assert found_centre == expected_centre_motor_coords + np.testing.assert_array_equal(found_centre, expected_centre_motor_coords) def test_GIVEN_no_results_from_zocalo_WHEN_communicator_wait_for_results_called_THEN_fallback_centre_used( @@ -121,13 +116,13 @@ def test_GIVEN_no_results_from_zocalo_WHEN_communicator_wait_for_results_called_ NoDiffractionFound() ) - fallback_position = Point3D(1, 2, 3) + fallback_position = np.array([1, 2, 3]) found_centre = callbacks.zocalo_handler.wait_for_results(fallback_position)[0] callbacks.zocalo_handler.zocalo_interactor.wait_for_result.assert_called_once_with( 100 ) - assert found_centre == fallback_position + np.testing.assert_array_equal(found_centre, fallback_position) def test_GIVEN_ispyb_not_started_WHEN_trigger_zocalo_handler_THEN_raises_exception( @@ -146,11 +141,11 @@ def test_multiple_results_from_zocalo_sorted_by_total_count_returns_centre_and_b callbacks = FGSCallbackCollection.from_params(dummy_params) mock_zocalo_functions(callbacks) callbacks.ispyb_handler.ispyb_ids = (0, 0, 100) - expected_centre_grid_coords = Point3D(4, 6, 2) + expected_centre_grid_coords = np.array([4, 6, 2]) multi_crystal_result = [ { "max_voxel": [1, 2, 3], - "centre_of_mass": Point3D(3, 11, 11), + "centre_of_mass": np.array([3, 11, 11]), "bounding_box": [[1, 1, 1], [3, 3, 3]], "n_voxels": 2, "total_count": 192512.0, @@ -167,21 +162,23 @@ def test_multiple_results_from_zocalo_sorted_by_total_count_returns_centre_and_b multi_crystal_result ) found_centre, found_bbox = callbacks.zocalo_handler.wait_for_results( - Point3D(0, 0, 0) + np.array([0, 0, 0]) ) callbacks.zocalo_handler.zocalo_interactor.wait_for_result.assert_called_once_with( 100 ) expected_centre_motor_coords = ( dummy_params.experiment_params.grid_position_to_motor_position( - Point3D( - expected_centre_grid_coords.x - 0.5, - expected_centre_grid_coords.y - 0.5, - expected_centre_grid_coords.z - 0.5, + np.array( + [ + expected_centre_grid_coords[0] - 0.5, + expected_centre_grid_coords[1] - 0.5, + expected_centre_grid_coords[2] - 0.5, + ] ) ) ) - assert found_centre == expected_centre_motor_coords + np.testing.assert_array_equal(found_centre, expected_centre_motor_coords) - expected_bbox_size = list(map(operator.sub, [8, 8, 7], [2, 2, 2])) - assert found_bbox == expected_bbox_size + expected_bbox_size = np.array([8, 8, 7]) - np.array([2, 2, 2]) + np.testing.assert_array_equal(found_bbox, expected_bbox_size) diff --git a/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py b/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py index 9000f94ad..3090a0467 100644 --- a/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py +++ b/src/artemis/external_interaction/callbacks/fgs/zocalo_callback.py @@ -1,10 +1,11 @@ from __future__ import annotations -import operator import time from typing import Callable, Optional +import numpy as np from bluesky.callbacks import CallbackBase +from numpy import ndarray from artemis.external_interaction.callbacks.fgs.ispyb_callback import ( FGSISPyBHandlerCallback, @@ -16,7 +17,6 @@ ) from artemis.log import LOGGER from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D class FGSZocaloCallback(CallbackBase): @@ -43,7 +43,7 @@ def __init__( self, parameters: FGSInternalParameters, ispyb_handler: FGSISPyBHandlerCallback ): self.grid_position_to_motor_position: Callable[ - [Point3D], Point3D + [ndarray], ndarray ] = parameters.experiment_params.grid_position_to_motor_position self.processing_start_time = 0.0 self.processing_time = 0.0 @@ -76,14 +76,14 @@ def stop(self, doc: dict): self.zocalo_interactor.run_end(id) self.processing_start_time = time.time() - def wait_for_results(self, fallback_xyz: Point3D) -> Point3D: + def wait_for_results(self, fallback_xyz: ndarray) -> tuple[ndarray, Optional[list]]: """Blocks until a centre has been received from Zocalo Args: - fallback_xyz (Point3D): The position to fallback to if no centre is found + fallback_xyz (ndarray): The position to fallback to if no centre is found Returns: - Point3D: The xray centre position to move to + ndarray: The xray centre position to move to """ datacollection_group_id = self.ispyb.ispyb_ids[2] @@ -102,13 +102,12 @@ def wait_for_results(self, fallback_xyz: Point3D) -> Point3D: bboxes = [] for n, res in enumerate(raw_results): bboxes.append( - list( - map( - operator.sub, res["bounding_box"][1], res["bounding_box"][0] - ) - ) + np.array(res["bounding_box"][1]) - np.array(res["bounding_box"][0]) ) - nicely_formatted_com = [f"{com:.2f}" for com in res["centre_of_mass"]] + + nicely_formatted_com = [ + f"{np.round(com,2)}" for com in res["centre_of_mass"] + ] crystal_summary += ( f"Crystal {n+1}: " f"Strength {res['total_count']}; " @@ -117,11 +116,11 @@ def wait_for_results(self, fallback_xyz: Point3D) -> Point3D: ) self.ispyb.append_to_comment(crystal_summary) - raw_centre = Point3D(*(raw_results[0]["centre_of_mass"])) + raw_centre = np.array([*(raw_results[0]["centre_of_mass"])]) # _wait_for_result returns the centre of the grid box, but we want the corner - results = Point3D( - raw_centre.x - 0.5, raw_centre.y - 0.5, raw_centre.z - 0.5 + results = np.array( + [raw_centre[0] - 0.5, raw_centre[1] - 0.5, raw_centre[2] - 0.5] ) xray_centre = self.grid_position_to_motor_position(results) diff --git a/src/artemis/external_interaction/ispyb/ispyb_dataclass.py b/src/artemis/external_interaction/ispyb/ispyb_dataclass.py index 29710adab..05a2a016a 100644 --- a/src/artemis/external_interaction/ispyb/ispyb_dataclass.py +++ b/src/artemis/external_interaction/ispyb/ispyb_dataclass.py @@ -1,10 +1,9 @@ from enum import Enum -from typing import Any, List, Optional +from typing import Any, Dict, List, Optional +import numpy as np from pydantic import BaseModel, validator -from artemis.utils.utils import Point3D - ISPYB_PARAM_DEFAULTS = { "sample_id": None, "sample_barcode": None, @@ -36,25 +35,36 @@ class IspybParams(BaseModel): visit_path: str microns_per_pixel_x: float microns_per_pixel_y: float + upper_left: np.ndarray + position: np.ndarray + + class Config: + arbitrary_types_allowed = True + json_encoders = {np.ndarray: lambda a: a.tolist()} - upper_left: Point3D - position: Point3D + def dict(self, **kwargs): + as_dict = super().dict(**kwargs) + as_dict["upper_left"] = as_dict["upper_left"].tolist() + as_dict["position"] = as_dict["position"].tolist() + return as_dict @validator("upper_left", pre=True) def _parse_upper_left( - cls, upper_left: list[int | float] | Point3D, values: dict[str, Any] - ) -> Point3D: - if isinstance(upper_left, Point3D): + cls, upper_left: list[int | float] | np.ndarray, values: Dict[str, Any] + ) -> np.ndarray: + assert len(upper_left) == 3 + if isinstance(upper_left, np.ndarray): return upper_left - return Point3D(upper_left[0], upper_left[1], upper_left[2]) + return np.array(upper_left) @validator("position", pre=True) def _parse_position( - cls, position: list[int | float] | Point3D, values: dict[str, Any] - ) -> Point3D: - if isinstance(position, Point3D): + cls, position: list[int | float] | np.ndarray, values: Dict[str, Any] + ) -> np.ndarray: + assert len(position) == 3 + if isinstance(position, np.ndarray): return position - return Point3D(position[0], position[1], position[2]) + return np.array(position) transmission: float flux: float @@ -77,6 +87,12 @@ def _parse_position( xtal_snapshots_omega_start: Optional[List[str]] = None xtal_snapshots_omega_end: Optional[List[str]] = None + def __eq__(self, other) -> bool: + if not isinstance(other, IspybParams): + return NotImplemented + else: + return self.json() == other.json() + class Orientation(Enum): HORIZONTAL = "horizontal" diff --git a/src/artemis/external_interaction/ispyb/store_in_ispyb.py b/src/artemis/external_interaction/ispyb/store_in_ispyb.py index 43c8ea9df..feef623db 100755 --- a/src/artemis/external_interaction/ispyb/store_in_ispyb.py +++ b/src/artemis/external_interaction/ispyb/store_in_ispyb.py @@ -8,12 +8,12 @@ import dodal.devices.oav.utils as oav_utils import ispyb import ispyb.sqlalchemy +import numpy as np from sqlalchemy.connectors import Connector from artemis.external_interaction.ispyb.ispyb_dataclass import Orientation from artemis.log import LOGGER from artemis.tracing import TRACER -from artemis.utils.utils import Point2D if TYPE_CHECKING: from artemis.parameters.internal_parameters import InternalParameters @@ -83,9 +83,11 @@ def store_grid_scan(self, full_params: InternalParameters): self.run_number = self.detector_params.run_number self.omega_start = self.detector_params.omega_start self.xtal_snapshots = self.ispyb_params.xtal_snapshots_omega_start - self.upper_left = Point2D( - self.ispyb_params.upper_left.x, - self.ispyb_params.upper_left.y, + self.upper_left = np.array( + [ + self.ispyb_params.upper_left[0], + self.ispyb_params.upper_left[1], + ] ) self.y_steps = full_params.experiment_params.y_steps self.y_step_size = full_params.experiment_params.y_step_size @@ -165,8 +167,8 @@ def _construct_comment(self) -> str: f"{self.y_steps} images in " f"{self.full_params.experiment_params.x_step_size*1e3} um by " f"{self.y_step_size*1e3} um steps. " - f"Top left (px): [{int(self.upper_left.x)},{int(self.upper_left.y)}], " - f"bottom right (px): [{bottom_right.x},{bottom_right.y}]." + f"Top left (px): [{int(self.upper_left[0])},{int(self.upper_left[1])}], " + f"bottom right (px): [{bottom_right[0]},{bottom_right[1]}]." ) @TRACER.start_as_current_span("store_ispyb_datacollection_table") @@ -313,9 +315,11 @@ def __prepare_second_scan_params(self): self.omega_start += 90 self.run_number += 1 self.xtal_snapshots = self.ispyb_params.xtal_snapshots_omega_end - self.upper_left = Point2D( - self.ispyb_params.upper_left.x, - self.ispyb_params.upper_left.z, + self.upper_left = np.array( + [ + self.ispyb_params.upper_left[0], + self.ispyb_params.upper_left[2], + ] ) self.y_steps = self.full_params.experiment_params.z_steps self.y_step_size = self.full_params.experiment_params.z_step_size diff --git a/src/artemis/external_interaction/system_tests/conftest.py b/src/artemis/external_interaction/system_tests/conftest.py index 8ec93e5bb..8807e5330 100644 --- a/src/artemis/external_interaction/system_tests/conftest.py +++ b/src/artemis/external_interaction/system_tests/conftest.py @@ -3,6 +3,7 @@ from typing import Callable import ispyb.sqlalchemy +import numpy as np import pytest from ispyb.sqlalchemy import DataCollection from sqlalchemy import create_engine @@ -15,7 +16,6 @@ ) from artemis.parameters.external_parameters import from_file as default_raw_params from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D ISPYB_CONFIG = "/dls_sw/dasc/mariadb/credentials/ispyb-dev.cfg" @@ -76,8 +76,8 @@ def fetch_comment() -> Callable: @pytest.fixture def dummy_params(): - dummy_params = FGSInternalParameters(**default_raw_params()) - dummy_params.artemis_params.ispyb_params.upper_left = Point3D(100, 100, 50) + dummy_params = FGSInternalParameters(default_raw_params()) + dummy_params.artemis_params.ispyb_params.upper_left = np.array([100, 100, 50]) dummy_params.artemis_params.ispyb_params.microns_per_pixel_x = 0.8 dummy_params.artemis_params.ispyb_params.microns_per_pixel_y = 0.8 dummy_params.artemis_params.ispyb_params.visit_path = ( diff --git a/src/artemis/external_interaction/system_tests/test_zocalo_system.py b/src/artemis/external_interaction/system_tests/test_zocalo_system.py index 54c43394f..842210320 100644 --- a/src/artemis/external_interaction/system_tests/test_zocalo_system.py +++ b/src/artemis/external_interaction/system_tests/test_zocalo_system.py @@ -1,3 +1,4 @@ +import numpy as np import pytest from artemis.external_interaction.callbacks.fgs.fgs_callback_collection import ( @@ -10,7 +11,6 @@ ) from artemis.parameters.external_parameters import from_file as default_raw_params from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D @pytest.mark.s03 @@ -29,7 +29,7 @@ def test_when_running_start_stop_then_get_expected_returned_results(zocalo_env): @pytest.fixture def run_zocalo_with_dev_ispyb(dummy_params: FGSInternalParameters, dummy_ispyb_3d): - def inner(sample_name="", fallback=Point3D(0, 0, 0)): + def inner(sample_name="", fallback=np.array([0, 0, 0])): dummy_params.artemis_params.detector_params.prefix = sample_name zc: FGSZocaloCallback = FGSCallbackCollection.from_params( dummy_params @@ -49,7 +49,7 @@ def inner(sample_name="", fallback=Point3D(0, 0, 0)): def test_given_a_result_with_no_diffraction_when_zocalo_called_then_move_to_fallback( run_zocalo_with_dev_ispyb, zocalo_env ): - fallback = Point3D(1, 2, 3) + fallback = np.array([1, 2, 3]) zc, centre = run_zocalo_with_dev_ispyb("NO_DIFF", fallback) assert centre == fallback diff --git a/src/artemis/external_interaction/unit_tests/test_store_in_ispyb.py b/src/artemis/external_interaction/unit_tests/test_store_in_ispyb.py index 337a413aa..ddf15ef2a 100644 --- a/src/artemis/external_interaction/unit_tests/test_store_in_ispyb.py +++ b/src/artemis/external_interaction/unit_tests/test_store_in_ispyb.py @@ -1,6 +1,7 @@ import re from unittest.mock import MagicMock, Mock, mock_open, patch +import numpy as np import pytest from ispyb.sp.mxacquisition import MXAcquisition from mockito import mock, when @@ -12,7 +13,6 @@ from artemis.parameters.constants import SIM_ISPYB_CONFIG from artemis.parameters.external_parameters import from_file as default_raw_params from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D TEST_DATA_COLLECTION_IDS = [12, 13] TEST_DATA_COLLECTION_GROUP_ID = 34 @@ -26,7 +26,7 @@ @pytest.fixture def dummy_params(): dummy_params = FGSInternalParameters(**default_raw_params()) - dummy_params.artemis_params.ispyb_params.upper_left = Point3D(100, 100, 50) + dummy_params.artemis_params.ispyb_params.upper_left = np.array([100, 100, 50]) dummy_params.artemis_params.ispyb_params.microns_per_pixel_x = 0.8 dummy_params.artemis_params.ispyb_params.microns_per_pixel_y = 0.8 return dummy_params @@ -127,7 +127,7 @@ def test_store_3d_grid_scan( y = 1 z = 2 - dummy_params.artemis_params.ispyb_params.upper_left = Point3D(x, y, z) + dummy_params.artemis_params.ispyb_params.upper_left = np.array([x, y, z]) dummy_params.experiment_params.z_step_size = 0.2 assert dummy_ispyb_3d.experiment_type == "Mesh3D" @@ -153,8 +153,8 @@ def test_store_3d_grid_scan( assert dummy_ispyb_3d.y_step_size == dummy_params.experiment_params.z_step_size assert dummy_ispyb_3d.y_steps == dummy_params.experiment_params.z_steps - assert dummy_ispyb_3d.upper_left.x == x - assert dummy_ispyb_3d.upper_left.y == z + assert dummy_ispyb_3d.upper_left[0] == x + assert dummy_ispyb_3d.upper_left[1] == z def setup_mock_return_values(ispyb_conn): @@ -313,8 +313,8 @@ def test_ispyb_deposition_rounds_to_int( mock_ispyb_conn.return_value.__enter__.return_value.mx_acquisition ) mock_upsert_data_collection = mock_mx_aquisition.upsert_data_collection - dummy_ispyb.full_params.artemis_params.ispyb_params.upper_left = Point3D( - 0.01, 100, 50 + dummy_ispyb.full_params.artemis_params.ispyb_params.upper_left = np.array( + [0.01, 100, 50] ) dummy_ispyb.begin_deposition() mock_upsert_call_args = mock_upsert_data_collection.call_args_list[0][0] diff --git a/src/artemis/external_interaction/unit_tests/test_zocalo_interaction.py b/src/artemis/external_interaction/unit_tests/test_zocalo_interaction.py index 84873ad0f..97ed315ae 100644 --- a/src/artemis/external_interaction/unit_tests/test_zocalo_interaction.py +++ b/src/artemis/external_interaction/unit_tests/test_zocalo_interaction.py @@ -6,6 +6,7 @@ from typing import Callable, Dict from unittest.mock import MagicMock, patch +import numpy as np import pytest from pytest import mark, raises from zocalo.configuration import Configuration @@ -15,7 +16,6 @@ ZocaloInteractor, ) from artemis.parameters.constants import SIM_ZOCALO_ENV -from artemis.utils.utils import Point3D EXPECTED_DCID = 100 EXPECTED_RUN_START_MESSAGE = {"event": "start", "ispyb_dcid": EXPECTED_DCID} @@ -137,8 +137,10 @@ def test_when_message_recieved_from_zocalo_then_point_returned( return_value = future.result() assert type(return_value) == list - returned_com = Point3D(*return_value[0]["centre_of_mass"]) - assert returned_com == Point3D(*centre_of_mass_coords) + returned_com = np.array([*return_value[0]["centre_of_mass"]]) + np.testing.assert_array_almost_equal( + returned_com, np.array([*centre_of_mass_coords]) + ) @patch("workflows.recipe.wrap_subscribe") diff --git a/src/artemis/external_interaction/zocalo/zocalo_interaction.py b/src/artemis/external_interaction/zocalo/zocalo_interaction.py index 7f098572a..070a1c00f 100644 --- a/src/artemis/external_interaction/zocalo/zocalo_interaction.py +++ b/src/artemis/external_interaction/zocalo/zocalo_interaction.py @@ -8,11 +8,11 @@ import workflows.recipe import workflows.transport import zocalo.configuration +from numpy import ndarray from workflows.transport import lookup import artemis.log from artemis.exceptions import WarningException -from artemis.utils.utils import Point3D TIMEOUT = 90 @@ -81,8 +81,8 @@ def run_end(self, data_collection_id: int): ) def wait_for_result( - self, data_collection_group_id: int, timeout: int | None = None - ) -> Point3D: + self, data_collection_group_id: int, timeout: int = None + ) -> ndarray: """Block until a result is received from Zocalo. Args: data_collection_group_id (int): The ID of the data collection group representing diff --git a/src/artemis/parameters/internal_parameters.py b/src/artemis/parameters/internal_parameters.py index 527052f08..8c80c7c3e 100644 --- a/src/artemis/parameters/internal_parameters.py +++ b/src/artemis/parameters/internal_parameters.py @@ -50,34 +50,6 @@ class Config: **IspybParams.Config.json_encoders, } - def __repr__(self): - return ( - "artemis_params:\n" - f" zocalo_environment: {self.zocalo_environment}\n" - f" beamline: {self.beamline}\n" - f" insertion_prefix: {self.insertion_prefix}\n" - f" experiment_type: {self.experiment_type}\n" - f" detector_params: {self.detector_params}\n" - f" ispyb_params: {self.ispyb_params}\n" - ) - - def __eq__(self, other) -> bool: - if not isinstance(other, ArtemisParameters): - return NotImplemented - elif self.zocalo_environment != other.zocalo_environment: - return False - elif self.beamline != other.beamline: - return False - elif self.insertion_prefix != other.insertion_prefix: - return False - elif self.experiment_type != other.experiment_type: - return False - elif self.detector_params != other.detector_params: - return False - elif self.ispyb_params != other.ispyb_params: - return False - return True - def flatten_dict(d: dict, parent_items: dict = {}) -> dict: """Flatten a dictionary assuming all keys are unique.""" diff --git a/src/artemis/parameters/plan_specific/fgs_internal_params.py b/src/artemis/parameters/plan_specific/fgs_internal_params.py index 2c3094061..5935bd2a1 100644 --- a/src/artemis/parameters/plan_specific/fgs_internal_params.py +++ b/src/artemis/parameters/plan_specific/fgs_internal_params.py @@ -2,6 +2,7 @@ from typing import Any +import numpy as np from dodal.devices.detector import TriggerMode from dodal.devices.fast_grid_scan import GridScanParams from pydantic import validator @@ -12,7 +13,6 @@ extract_artemis_params_from_flat_dict, extract_experiment_params_from_flat_dict, ) -from artemis.utils.utils import Point3D class FGSInternalParameters(InternalParameters): @@ -43,11 +43,11 @@ def _preprocess_artemis_params( ): experiment_params: GridScanParams = values["experiment_params"] all_params["num_images"] = experiment_params.get_num_images() - all_params["position"] = Point3D(*all_params["position"]) + all_params["position"] = np.array(all_params["position"]) all_params["omega_increment"] = 0 all_params["num_triggers"] = all_params["num_images"] all_params["num_images_per_trigger"] = 1 all_params["trigger_mode"] = TriggerMode.FREE_RUN - all_params["upper_left"] = Point3D(*all_params["upper_left"]) + all_params["upper_left"] = np.array(all_params["upper_left"]) artemis_param_dict = extract_artemis_params_from_flat_dict(all_params) return ArtemisParameters(**artemis_param_dict) diff --git a/src/artemis/parameters/plan_specific/grid_scan_with_edge_detect_params.py b/src/artemis/parameters/plan_specific/grid_scan_with_edge_detect_params.py index dd9c12297..ab2bc7d7f 100644 --- a/src/artemis/parameters/plan_specific/grid_scan_with_edge_detect_params.py +++ b/src/artemis/parameters/plan_specific/grid_scan_with_edge_detect_params.py @@ -2,6 +2,7 @@ from typing import Any +import numpy as np from dataclasses_json import DataClassJsonMixin from dodal.devices.detector import TriggerMode from dodal.parameters.experiment_parameter_base import AbstractExperimentParameterBase @@ -16,7 +17,6 @@ flatten_dict, get_extracted_experiment_and_flat_artemis_params, ) -from artemis.utils.utils import Point3D @dataclass @@ -61,10 +61,10 @@ def _preprocess_artemis_params( ): experiment_params: GridScanWithEdgeDetectParams = values["experiment_params"] all_params["num_images"] = experiment_params.get_num_images() - all_params["position"] = Point3D(*all_params["position"]) + all_params["position"] = np.array(all_params["position"]) all_params["omega_increment"] = 0 all_params["num_triggers"] = all_params["num_images"] all_params["num_images_per_trigger"] = 1 all_params["trigger_mode"] = TriggerMode.FREE_RUN - all_params["upper_left"] = Point3D(0, 0, 0) + all_params["upper_left"] = np.array([0, 0, 0]) return ArtemisParameters(**extract_artemis_params_from_flat_dict(all_params)) diff --git a/src/artemis/parameters/plan_specific/rotation_scan_internal_params.py b/src/artemis/parameters/plan_specific/rotation_scan_internal_params.py index c9081917c..22f544cad 100644 --- a/src/artemis/parameters/plan_specific/rotation_scan_internal_params.py +++ b/src/artemis/parameters/plan_specific/rotation_scan_internal_params.py @@ -2,6 +2,7 @@ from typing import Any, Optional +import numpy as np from dodal.devices.motors import XYZLimitBundle from dodal.devices.zebra import RotationDirection from dodal.parameters.experiment_parameter_base import AbstractExperimentParameterBase @@ -13,7 +14,6 @@ extract_artemis_params_from_flat_dict, extract_experiment_params_from_flat_dict, ) -from artemis.utils.utils import Point3D class RotationScanParams(BaseModel, AbstractExperimentParameterBase): @@ -80,12 +80,12 @@ def _preprocess_artemis_params( ): experiment_params: RotationScanParams = values["experiment_params"] all_params["num_images"] = experiment_params.get_num_images() - all_params["position"] = Point3D(*all_params["position"]) + all_params["position"] = np.array(all_params["position"]) if all_params["rotation_axis"] == "omega": all_params["omega_increment"] = all_params["rotation_increment"] else: all_params["omega_increment"] = 0 all_params["num_triggers"] = 1 all_params["num_images_per_trigger"] = all_params["num_images"] - all_params["upper_left"] = Point3D(*all_params["upper_left"]) + all_params["upper_left"] = np.array(all_params["upper_left"]) return ArtemisParameters(**extract_artemis_params_from_flat_dict(all_params)) diff --git a/src/artemis/parameters/plan_specific/tests/test_fgs_internal_parameters.py b/src/artemis/parameters/plan_specific/tests/test_fgs_internal_parameters.py index b5f4c5c5c..342264422 100644 --- a/src/artemis/parameters/plan_specific/tests/test_fgs_internal_parameters.py +++ b/src/artemis/parameters/plan_specific/tests/test_fgs_internal_parameters.py @@ -1,9 +1,9 @@ +import numpy as np from dodal.devices.det_dim_constants import EIGER2_X_16M_SIZE from dodal.devices.fast_grid_scan import GridScanParams from artemis.parameters import external_parameters from artemis.parameters.plan_specific.fgs_internal_params import FGSInternalParameters -from artemis.utils.utils import Point3D def test_FGS_parameters_load_from_file(): @@ -16,8 +16,8 @@ def test_FGS_parameters_load_from_file(): ispyb_params = internal_parameters.artemis_params.ispyb_params - assert ispyb_params.position == Point3D(10, 20, 30) - assert ispyb_params.upper_left == Point3D(10, 20, 30) + np.testing.assert_array_equal(ispyb_params.position, np.array([10, 20, 30])) + np.testing.assert_array_equal(ispyb_params.upper_left, np.array([10, 20, 30])) detector_params = internal_parameters.artemis_params.detector_params diff --git a/src/artemis/parameters/plan_specific/tests/test_rotation_internal_parameters.py b/src/artemis/parameters/plan_specific/tests/test_rotation_internal_parameters.py index 86b366307..e0af0a25a 100644 --- a/src/artemis/parameters/plan_specific/tests/test_rotation_internal_parameters.py +++ b/src/artemis/parameters/plan_specific/tests/test_rotation_internal_parameters.py @@ -1,5 +1,6 @@ from unittest.mock import MagicMock +import numpy as np from dodal.devices.det_dim_constants import EIGER2_X_16M_SIZE from dodal.devices.motors import XYZLimitBundle @@ -8,7 +9,6 @@ RotationInternalParameters, RotationScanParams, ) -from artemis.utils.utils import Point3D def test_rotation_scan_param_validity(): @@ -52,8 +52,8 @@ def test_rotation_parameters_load_from_file(): ispyb_params = internal_parameters.artemis_params.ispyb_params - assert ispyb_params.position == Point3D(10, 20, 30) - assert ispyb_params.upper_left == Point3D(10, 20, 30) + np.testing.assert_array_equal(ispyb_params.position, np.array([10, 20, 30])) + np.testing.assert_array_equal(ispyb_params.upper_left, np.array([10, 20, 30])) detector_params = internal_parameters.artemis_params.detector_params diff --git a/src/artemis/parameters/tests/test_internal_parameters.py b/src/artemis/parameters/tests/test_internal_parameters.py index 65464ee7c..67811b7c2 100644 --- a/src/artemis/parameters/tests/test_internal_parameters.py +++ b/src/artemis/parameters/tests/test_internal_parameters.py @@ -1,12 +1,16 @@ import copy import json +import numpy as np import pytest from dodal.devices.detector import DetectorParams from dodal.devices.fast_grid_scan import GridScanParams from pydantic import ValidationError -from artemis.external_interaction.ispyb.ispyb_dataclass import IspybParams +from artemis.external_interaction.ispyb.ispyb_dataclass import ( + ISPYB_PARAM_DEFAULTS, + IspybParams, +) from artemis.parameters import external_parameters from artemis.parameters.external_parameters import from_file from artemis.parameters.internal_parameters import ( @@ -45,6 +49,19 @@ def test_cant_initialise_abstract_internalparams(): ) +def test_ispyb_param_dict(): + ispyb_params = IspybParams(**ISPYB_PARAM_DEFAULTS) + as_dict = ispyb_params.dict() + assert isinstance(as_dict.get("position"), list) + modified_params = copy.deepcopy(ISPYB_PARAM_DEFAULTS) + modified_params["position"] = [123, 7777777, 3] + modified_ispyb_params = IspybParams(**modified_params) + assert ispyb_params != modified_ispyb_params + assert isinstance(modified_ispyb_params.position, np.ndarray) + modified_as_dict = modified_ispyb_params.dict() + assert modified_as_dict.get("position") == [123, 7777777, 3] + + def test_internal_param_serialisation_deserialisation(): data = from_file() internal_parameters = FGSInternalParameters(**data) diff --git a/src/artemis/utils/utils.py b/src/artemis/utils/utils.py index fe8ffda5c..e69de29bb 100644 --- a/src/artemis/utils/utils.py +++ b/src/artemis/utils/utils.py @@ -1,4 +0,0 @@ -from collections import namedtuple - -Point2D = namedtuple("Point2D", ["x", "y"]) -Point3D = namedtuple("Point3D", ["x", "y", "z"])