Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/artemis/device_setup_plans/unit_tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from unittest.mock import MagicMock

import pytest
from bluesky import RunEngine
from bluesky import plan_stubs as bps
from bluesky.run_engine import RunEngine
from dodal.beamlines import i03

from artemis.device_setup_plans.utils import (
start_preparing_data_collection_then_do_plan,
)


def test_given_plan_raises_when_exception_raised_then_eiger_disarmed_and_correct_exception_returned():
class TestException(Exception):
pass

def my_plan():
yield from bps.null()
raise TestException()

eiger = i03.eiger(fake_with_ophyd_sim=True)
eiger.detector_params = MagicMock()
eiger.async_stage = MagicMock()
eiger.disarm_detector = MagicMock()

RE = RunEngine()

with pytest.raises(TestException):
RE(
start_preparing_data_collection_then_do_plan(
eiger, MagicMock(), 0.2, my_plan()
)
)

# Check detector was armed
eiger.async_stage.assert_called_once()

eiger.disarm_detector.assert_called_once()


def test_when_preparing_data_collection_then_transmission_set():
def my_plan():
yield from bps.null()

attenuator = MagicMock()
RE = RunEngine()

RE(
start_preparing_data_collection_then_do_plan(
MagicMock(), attenuator, 0.2, my_plan()
)
)

# Check transmission set
attenuator.set.assert_called_once_with(0.2)
30 changes: 30 additions & 0 deletions src/artemis/device_setup_plans/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Generator

from bluesky import plan_stubs as bps
from bluesky import preprocessors as bpp
from bluesky.utils import Msg
from dodal.devices.attenuator import Attenuator
from dodal.devices.eiger import EigerDetector


def start_preparing_data_collection_then_do_plan(
eiger: EigerDetector,
attenuator: Attenuator,
transmission_fraction: float,
plan_to_run: Generator[Msg, None, None],
group="ready_for_data_collection",
) -> Generator[Msg, None, None]:
"""Starts preparing for the next data collection by arming the eiger and setting the
transmission. Then runs the given plan. If the plan fails it will stop disarm the eiger.
"""
yield from bps.abs_set(eiger.do_arm, 1, group=group)
yield from bps.abs_set(
attenuator,
transmission_fraction,
group=group,
)

yield from bpp.contingency_wrapper(
plan_to_run,
except_plan=lambda e: (yield from bps.stop(eiger)),
)
11 changes: 11 additions & 0 deletions src/artemis/experiment_plans/experiment_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from artemis.experiment_plans import (
fast_grid_scan_plan,
full_grid_scan_plan,
pin_centre_then_xray_centre_plan,
rotation_scan_plan,
stepped_grid_scan_plan,
)
Expand All @@ -24,6 +25,10 @@
GridScanWithEdgeDetectInternalParameters,
GridScanWithEdgeDetectParams,
)
from artemis.parameters.plan_specific.pin_centre_then_xray_centre_params import (
PinCentreThenXrayCentreInternalParameters,
PinCentreThenXrayCentreParams,
)
from artemis.parameters.plan_specific.rotation_scan_internal_params import (
RotationInternalParameters,
RotationScanParams,
Expand Down Expand Up @@ -62,6 +67,12 @@ def do_nothing():
"experiment_param_type": RotationScanParams,
"callback_collection_type": RotationCallbackCollection,
},
"pin_tip_centre_then_xray_centre": {
"setup": pin_centre_then_xray_centre_plan.create_devices,
"internal_param_type": PinCentreThenXrayCentreInternalParameters,
"experiment_param_type": PinCentreThenXrayCentreParams,
"callback_collection_type": NullPlanCallbackCollection,
},
"stepped_grid_scan": {
"setup": stepped_grid_scan_plan.create_devices,
"run": stepped_grid_scan_plan.get_plan,
Expand Down
47 changes: 11 additions & 36 deletions src/artemis/experiment_plans/full_grid_scan_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
from dodal.devices.eiger import EigerDetector
from dodal.devices.oav.oav_parameters import OAV_CONFIG_FILE_DEFAULTS, OAVParameters

from artemis.device_setup_plans.utils import (
start_preparing_data_collection_then_do_plan,
)
from artemis.experiment_plans.fast_grid_scan_plan import (
create_devices as fgs_create_devices,
)
Expand Down Expand Up @@ -80,35 +83,6 @@ def create_parameters_for_fast_grid_scan(
return fast_grid_scan_parameters


def start_arming_then_do_grid(
parameters: GridScanWithEdgeDetectInternalParameters,
attenuator: Attenuator,
backlight: Backlight,
eiger: EigerDetector,
aperture_scatterguard: ApertureScatterguard,
detector_motion: DetectorMotion,
oav_params: OAVParameters,
):
# Start stage with asynchronous arming here
yield from bps.abs_set(eiger.do_arm, 1, group="ready_for_data_collection")
yield from bps.abs_set(
attenuator,
parameters.artemis_params.ispyb_params.transmission_fraction,
group="ready_for_data_collection",
)

yield from bpp.contingency_wrapper(
detect_grid_and_do_gridscan(
parameters,
backlight,
aperture_scatterguard,
detector_motion,
oav_params,
),
except_plan=lambda e: (yield from bps.stop(eiger)),
)


def detect_grid_and_do_gridscan(
parameters: GridScanWithEdgeDetectInternalParameters,
backlight: Backlight,
Expand Down Expand Up @@ -196,12 +170,13 @@ def full_grid_scan(

oav_params = OAVParameters("xrayCentring", **oav_param_files)

return start_arming_then_do_grid(
parameters,
attenuator,
backlight,
plan_to_perform = detect_grid_and_do_gridscan(
parameters, backlight, aperture_scatterguard, detector_motion, oav_params
)

return start_preparing_data_collection_then_do_plan(
eiger,
aperture_scatterguard,
detector_motion,
oav_params,
attenuator,
parameters.artemis_params.ispyb_params.transmission_fraction,
plan_to_perform,
)
89 changes: 89 additions & 0 deletions src/artemis/experiment_plans/pin_centre_then_xray_centre_plan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import json

from blueapi.core import MsgGenerator
from dodal.beamlines import i03
from dodal.devices.attenuator import Attenuator
from dodal.devices.eiger import EigerDetector
from dodal.devices.oav.oav_parameters import OAV_CONFIG_FILE_DEFAULTS, OAVParameters

from artemis.device_setup_plans.utils import (
start_preparing_data_collection_then_do_plan,
)
from artemis.experiment_plans.full_grid_scan_plan import (
create_devices as full_grid_create_devices,
)
from artemis.experiment_plans.full_grid_scan_plan import detect_grid_and_do_gridscan
from artemis.experiment_plans.pin_tip_centring_plan import (
create_devices as pin_tip_create_devices,
)
from artemis.experiment_plans.pin_tip_centring_plan import pin_tip_centre_plan
from artemis.log import LOGGER
from artemis.parameters.plan_specific.grid_scan_with_edge_detect_params import (
GridScanWithEdgeDetectInternalParameters,
)
from artemis.parameters.plan_specific.pin_centre_then_xray_centre_params import (
PinCentreThenXrayCentreInternalParameters,
)


def create_devices():
full_grid_create_devices()
pin_tip_create_devices()


def create_parameters_for_grid_detection(
pin_centre_parameters: PinCentreThenXrayCentreInternalParameters,
) -> GridScanWithEdgeDetectInternalParameters:
params_json = json.loads(pin_centre_parameters.json())
grid_detect_and_xray_centre = GridScanWithEdgeDetectInternalParameters(
**params_json
)
LOGGER.info(
f"Parameters for grid detect and xray centre: {grid_detect_and_xray_centre}"
)
return grid_detect_and_xray_centre


def pin_centre_then_xray_centre_plan(
parameters: PinCentreThenXrayCentreInternalParameters,
oav_config_files=OAV_CONFIG_FILE_DEFAULTS,
):
"""Plan that perfoms a pin tip centre followed by an xray centre to completely
centre the sample"""
oav_config_files["oav_config_json"] = parameters.experiment_params.oav_centring_file

yield from pin_tip_centre_plan(
parameters.experiment_params.tip_offset_microns, oav_config_files
Comment thread
DominicOram marked this conversation as resolved.
)
grid_detect_params = create_parameters_for_grid_detection(parameters)

backlight = i03.backlight()
aperture_scattergaurd = i03.aperture_scatterguard()
detector_motion = i03.detector_motion()
Comment thread
DominicOram marked this conversation as resolved.
oav_params = OAVParameters("xrayCentring", **oav_config_files)

yield from detect_grid_and_do_gridscan(
grid_detect_params,
backlight,
aperture_scattergaurd,
detector_motion,
oav_params,
)


def pin_tip_centre_then_xray_centre(
parameters: PinCentreThenXrayCentreInternalParameters,
) -> MsgGenerator:
"""Starts preparing for collection then performs the pin tip centre and xray centre"""

eiger: EigerDetector = i03.eiger()
attenuator: Attenuator = i03.attenuator()

eiger.set_detector_parameters(parameters.artemis_params.detector_params)

return start_preparing_data_collection_then_do_plan(
eiger,
attenuator,
parameters.artemis_params.ispyb_params.transmission_fraction,
pin_centre_then_xray_centre_plan(parameters),
)
6 changes: 5 additions & 1 deletion src/artemis/experiment_plans/pin_tip_centring_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def move_smargon_warn_on_out_of_range(smargon: Smargon, position: np.ndarray):


def pin_tip_centre_plan(
tip_offset_microns: float = 900,
tip_offset_microns: float,
oav_config_files: Dict[str, str] = OAV_CONFIG_FILE_DEFAULTS,
):
"""Finds the tip of the pin and moves to roughly the centre based on this tip. Does
Expand All @@ -116,6 +116,10 @@ def offset_and_move(tip: Pixel):

LOGGER.info(f"Tip offset in pixels: {tip_offset_px}")

# need to wait for the OAV image to update
# See #673 for improvements
yield from bps.sleep(0.3)
Comment thread
DominicOram marked this conversation as resolved.

yield from pre_centring_setup_oav(oav, oav_params)

tip = yield from move_pin_into_view(oav, smargon)
Expand Down
78 changes: 1 addition & 77 deletions src/artemis/experiment_plans/tests/test_full_grid_scan_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from unittest.mock import ANY, MagicMock, patch

import pytest
from bluesky import RunEngine
from bluesky.run_engine import RunEngine
from dodal.beamlines.i03 import detector_motion
from dodal.devices.aperturescatterguard import AperturePositions, ApertureScatterguard
from dodal.devices.backlight import Backlight
Expand All @@ -15,7 +15,6 @@
create_devices,
detect_grid_and_do_gridscan,
full_grid_scan,
start_arming_then_do_grid,
wait_for_det_to_finish_moving,
)
from artemis.external_interaction.callbacks.oav_snapshot_callback import (
Expand Down Expand Up @@ -222,78 +221,3 @@ def test_when_full_grid_scan_run_then_parameters_sent_to_fgs_as_expected(

# Parameters can be serialized
params.json()


@patch(
"artemis.experiment_plans.full_grid_scan_plan.grid_detection_plan", autospec=True
)
@patch(
"artemis.experiment_plans.full_grid_scan_plan.OavSnapshotCallback",
autospec=True,
spec_set=True,
)
def test_grid_detection_running_when_exception_raised_then_eiger_disarmed_and_correct_exception_returned(
mock_oav_callback: MagicMock,
mock_grid_detection_plan: MagicMock,
eiger: EigerDetector,
RE: RunEngine,
test_full_grid_scan_params: GridScanWithEdgeDetectInternalParameters,
mock_subscriptions: MagicMock,
test_config_files: Dict,
):
class DetectException(Exception):
pass

mock_grid_detection_plan.side_effect = DetectException()
eiger.detector_params = MagicMock()
eiger.async_stage = MagicMock()
eiger.disarm_detector = MagicMock()

with patch(
"artemis.external_interaction.callbacks.fgs.fgs_callback_collection.FGSCallbackCollection.from_params",
return_value=mock_subscriptions,
autospec=True,
):
with pytest.raises(DetectException):
RE(
start_arming_then_do_grid(
parameters=test_full_grid_scan_params,
attenuator=MagicMock(),
backlight=MagicMock(),
eiger=eiger,
aperture_scatterguard=MagicMock(),
detector_motion=MagicMock(),
oav_params=OAVParameters("xrayCentring", **test_config_files),
)
)

# Check detector was armed
eiger.async_stage.assert_called_once()

eiger.disarm_detector.assert_called_once()


@patch("artemis.experiment_plans.full_grid_scan_plan.detect_grid_and_do_gridscan")
def test_when_start_arming_then_transmission_set(
mock_grid_detection_plan: MagicMock,
RE: RunEngine,
test_full_grid_scan_params: GridScanWithEdgeDetectInternalParameters,
test_config_files: Dict,
):
attenuator = MagicMock()
RE(
start_arming_then_do_grid(
parameters=test_full_grid_scan_params,
attenuator=attenuator,
backlight=MagicMock(),
eiger=MagicMock(),
aperture_scatterguard=MagicMock(),
detector_motion=MagicMock(),
oav_params=OAVParameters("xrayCentring", **test_config_files),
)
)

# Check transmission set
attenuator.set.assert_called_once_with(
test_full_grid_scan_params.artemis_params.ispyb_params.transmission_fraction
)
Loading