From b3f08e239bc30068296fc425d4a9199f0117deb6 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Mon, 23 Oct 2023 16:41:48 +0100 Subject: [PATCH 1/7] Initial VMXm FGS plan --- .../vmxm_flyscan_xray_centre_plan.py | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100755 src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py new file mode 100755 index 000000000..25de80e63 --- /dev/null +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -0,0 +1,289 @@ +from __future__ import annotations + +import argparse +import dataclasses +from typing import TYPE_CHECKING, Any + +import bluesky.plan_stubs as bps +import bluesky.preprocessors as bpp +import numpy as np +from blueapi.core import BlueskyContext, MsgGenerator +from bluesky.preprocessors import finalize_wrapper, make_decorator +from bluesky.run_engine import RunEngine +from bluesky.utils import ProgressBarManager +from dodal.devices.backlight import Backlight +from dodal.devices.eiger import EigerDetector +from dodal.devices.fast_grid_scan import FastGridScan +from dodal.devices.fast_grid_scan import set_fast_grid_scan_params as set_flyscan_params +from dodal.devices.smargon import Smargon +from dodal.devices.synchrotron import Synchrotron +from dodal.devices.vmxm.vmxm_attenuator import VmxmAttenuator +from dodal.devices.zebra import ( + Zebra, +) + +import hyperion.log +from hyperion.device_setup_plans.manipulate_sample import move_x_y_z +from hyperion.device_setup_plans.setup_zebra import ( + set_zebra_shutter_to_manual, +) +from hyperion.exceptions import WarningException +from hyperion.external_interaction.callbacks.xray_centre.callback_collection import ( + XrayCentreCallbackCollection, +) +from hyperion.parameters import external_parameters +from hyperion.parameters.constants import SIM_BEAMLINE +from hyperion.tracing import TRACER +from hyperion.utils.context import device_composite_from_context, setup_context + +if TYPE_CHECKING: + from hyperion.parameters.plan_specific.gridscan_internal_params import ( + GridscanInternalParameters, + ) + + +@dataclasses.dataclass +class VmxmFlyScanXRayCentreComposite: + """All devices which are directly or indirectly required by this plan""" + + attenuator: VmxmAttenuator + backlight: Backlight + eiger: EigerDetector + fast_grid_scan: FastGridScan + smargon: Smargon + synchrotron: Synchrotron + zebra: Zebra + + @property + def sample_motors(self) -> Smargon: + """Convenience alias with a more user-friendly name""" + return self.smargon + + +def create_devices(context: BlueskyContext) -> VmxmFlyScanXRayCentreComposite: + """Creates the devices required for the plan and connect to them""" + return device_composite_from_context(context, VmxmFlyScanXRayCentreComposite) + + +def wait_for_gridscan_valid(fgs_motors: FastGridScan, timeout=0.5): + hyperion.log.LOGGER.info("Waiting for valid fgs_params") + SLEEP_PER_CHECK = 0.1 + times_to_check = int(timeout / SLEEP_PER_CHECK) + for _ in range(times_to_check): + scan_invalid = yield from bps.rd(fgs_motors.scan_invalid) + pos_counter = yield from bps.rd(fgs_motors.position_counter) + hyperion.log.LOGGER.debug( + f"Scan invalid: {scan_invalid} and position counter: {pos_counter}" + ) + if not scan_invalid and pos_counter == 0: + hyperion.log.LOGGER.info("Gridscan scan valid and position counter reset") + return + yield from bps.sleep(SLEEP_PER_CHECK) + raise WarningException("Scan invalid - pin too long/short/bent and out of range") + + +def tidy_up_plans(fgs_composite: VmxmFlyScanXRayCentreComposite): + hyperion.log.LOGGER.info("Tidying up Zebra") + yield from set_zebra_shutter_to_manual(fgs_composite.zebra) + + +@bpp.set_run_key_decorator("run_gridscan") +@bpp.run_decorator(md={"subplan_name": "run_gridscan"}) +def run_gridscan( + fgs_composite: VmxmFlyScanXRayCentreComposite, + parameters: GridscanInternalParameters, + md={ + "plan_name": "run_gridscan", + }, +): + sample_motors = fgs_composite.sample_motors + + # Currently gridscan only works for omega 0, see # + with TRACER.start_span("moving_omega_to_0"): + yield from bps.abs_set(sample_motors.omega, 0) + + fgs_motors = fgs_composite.fast_grid_scan + + # TODO: Check topup gate + yield from set_flyscan_params(fgs_motors, parameters.experiment_params) + yield from wait_for_gridscan_valid(fgs_motors) + + @bpp.set_run_key_decorator("do_fgs") + @bpp.run_decorator(md={"subplan_name": "do_fgs"}) + @bpp.contingency_decorator( + except_plan=lambda e: (yield from bps.stop(fgs_composite.eiger)), + else_plan=lambda: (yield from bps.unstage(fgs_composite.eiger)), + ) + def do_fgs(): + yield from bps.wait() # Wait for all moves to complete + yield from bps.kickoff(fgs_motors) + yield from bps.complete(fgs_motors, wait=True) + + hyperion.log.LOGGER.info("Waiting for arming to finish") + yield from bps.wait("ready_for_data_collection") + yield from bps.stage(fgs_composite.eiger) + + with TRACER.start_span("do_fgs"): + yield from do_fgs() + + yield from bps.abs_set(fgs_motors.z_steps, 0, wait=False) + + +def setup_vmxm_zebra_for_gridscan( + zebra: Zebra, group="setup_zebra_for_gridscan", wait=False +): + # VMXm-specific + yield from bps.abs_set(zebra.output.out_pvs[4], 1, group=group) + + if wait: + yield from bps.wait(group) + + +@bpp.set_run_key_decorator("run_gridscan_and_move") +@bpp.run_decorator(md={"subplan_name": "run_gridscan_and_move"}) +def run_gridscan_and_move( + fgs_composite: VmxmFlyScanXRayCentreComposite, + parameters: GridscanInternalParameters, + subscriptions: XrayCentreCallbackCollection, +): + """A multi-run plan which runs a gridscan, gets the results from zocalo + 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 = 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_vmxm_zebra_for_gridscan(fgs_composite.zebra) + + hyperion.log.LOGGER.info("Starting grid scan") + + yield from run_gridscan(fgs_composite, parameters) + + # the data were submitted to zocalo by the zocalo callback during the gridscan, + # but results may not be ready, and need to be collected regardless. + # it might not be ideal to block for this, see #327 + xray_centre, _ = subscriptions.zocalo_handler.wait_for_results(initial_xyz) + + # once we have the results, go to the appropriate position + hyperion.log.LOGGER.info("Moving to centre of mass.") + with TRACER.start_span("move_to_result"): + yield from move_x_y_z(fgs_composite.sample_motors, *xray_centre, wait=True) + + +def transmission_for_collection_wrapper( + plan, + attenuator: VmxmAttenuator, + desired_transmission_fraction: float, +): + """Sets the transmission for the data collection, esuring the xbpm feedback is valid + this wrapper should be run around every data collection. + + XBPM feedback isn't reliable during collections due to: + * Objects (e.g. attenuator) crossing the beam can cause large (incorrect) feedback movements + * Lower transmissions/higher energies are less reliable for the xbpm + + So we need to keep the transmission at 100% and the feedback on when not collecting + and then turn it off and set the correct transmission for collection. The feedback + mostly accounts for slow thermal drift so it is safe to assume that the beam is + stable during a collection. + + Args: + plan: The plan performing the data collection + xbpm_feedback (XBPMFeedback): The XBPM device that is responsible for keeping + the beam in position + attenuator (Attenuator): The attenuator used to set transmission + desired_transmission_fraction (float): The desired transmission for the collection + """ + + def _inner_plan(): + yield from bps.mv(attenuator, desired_transmission_fraction) + return (yield from plan) + + def _set_transmission_to_1(): + yield from bps.mv(attenuator, 1.0) + + return ( + yield from finalize_wrapper( + _inner_plan(), + _set_transmission_to_1(), + ) + ) + + +transmission_for_collection_decorator = make_decorator( + transmission_for_collection_wrapper +) + + +def flyscan_xray_centre( + composite: VmxmFlyScanXRayCentreComposite, + parameters: Any, +) -> MsgGenerator: + """Create the plan to run the grid scan based on provided parameters. + + The ispyb handler should be added to the whole gridscan as we want to capture errors + at any point in it. + + Args: + parameters (FGSInternalParameters): The parameters to run the scan. + + Returns: + Generator: The plan for the gridscan + """ + composite.eiger.set_detector_parameters(parameters.hyperion_params.detector_params) + + subscriptions = XrayCentreCallbackCollection.from_params(parameters) + + yield from bps.mv( + composite.attenuator, + parameters.hyperion_params.ispyb_params.transmission_fraction, + ) + + @bpp.subs_decorator( # subscribe the RE to nexus, ispyb, and zocalo callbacks + list(subscriptions) # must be the outermost decorator to receive the metadata + ) + @bpp.set_run_key_decorator("run_gridscan_move_and_tidy") + @bpp.run_decorator( # attach experiment metadata to the start document + md={ + "subplan_name": "run_gridscan_move_and_tidy", + "hyperion_internal_parameters": parameters.json(), + } + ) + @bpp.finalize_decorator(lambda: tidy_up_plans(composite)) + @transmission_for_collection_decorator( + composite.attenuator, + parameters.hyperion_params.ispyb_params.transmission_fraction, + ) + def run_gridscan_and_move_and_tidy(fgs_composite, params, comms): + yield from run_gridscan_and_move(fgs_composite, params, comms) + + return run_gridscan_and_move_and_tidy(composite, parameters, subscriptions) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--beamline", + help="The beamline prefix this is being run on", + default=SIM_BEAMLINE, + ) + args = parser.parse_args() + + RE = RunEngine({}) + RE.waiting_hook = ProgressBarManager() + from hyperion.parameters.plan_specific.gridscan_internal_params import ( + GridscanInternalParameters, + ) + + parameters = GridscanInternalParameters(**external_parameters.from_file()) + subscriptions = XrayCentreCallbackCollection.from_params(parameters) + + context = setup_context(wait_for_connection=True) + composite = create_devices(context) + + RE(flyscan_xray_centre(composite, parameters)) From 325d8109f954d257746eccce6994b527082b407e Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Fri, 27 Oct 2023 16:23:13 +0100 Subject: [PATCH 2/7] VMXM - changes on beamline --- src/hyperion/experiment_plans/__init__.py | 2 + .../experiment_plans/experiment_registry.py | 30 +-------- .../vmxm_flyscan_xray_centre_plan.py | 62 ++++++++++--------- test_parameters.json | 16 ++--- 4 files changed, 46 insertions(+), 64 deletions(-) diff --git a/src/hyperion/experiment_plans/__init__.py b/src/hyperion/experiment_plans/__init__.py index a2e9f07fe..ffcbc9009 100644 --- a/src/hyperion/experiment_plans/__init__.py +++ b/src/hyperion/experiment_plans/__init__.py @@ -2,6 +2,7 @@ The __all__ list in here are the plans that are externally available from outside Hyperion. """ +from hyperion.experiment_plans.vmxm_flyscan_xray_centre_plan import vmxm_flyscan_xray_centre from hyperion.experiment_plans.flyscan_xray_centre_plan import flyscan_xray_centre from hyperion.experiment_plans.grid_detect_then_xray_centre_plan import ( grid_detect_then_xray_centre, @@ -16,4 +17,5 @@ "grid_detect_then_xray_centre", "rotation_scan", "pin_tip_centre_then_xray_centre", + "vmxm_flyscan_xray_centre" ] diff --git a/src/hyperion/experiment_plans/experiment_registry.py b/src/hyperion/experiment_plans/experiment_registry.py index c0fb7e543..7c4f8bf44 100644 --- a/src/hyperion/experiment_plans/experiment_registry.py +++ b/src/hyperion/experiment_plans/experiment_registry.py @@ -4,7 +4,7 @@ from dodal.devices.fast_grid_scan import GridScanParams -import hyperion.experiment_plans.flyscan_xray_centre_plan as flyscan_xray_centre_plan +import hyperion.experiment_plans.vmxm_flyscan_xray_centre_plan as vmxm_flyscan_xray_centre_plan import hyperion.experiment_plans.rotation_scan_plan as rotation_scan_plan from hyperion.experiment_plans import ( grid_detect_then_xray_centre_plan, @@ -51,36 +51,12 @@ def do_nothing(): EXPERIMENT_TYPES = Union[GridScanParams, RotationScanParams, SteppedGridScanParams] PLAN_REGISTRY: dict[str, dict[str, Callable]] = { - "flyscan_xray_centre": { - "setup": flyscan_xray_centre_plan.create_devices, + "vmxm_flyscan_xray_centre": { + "setup": vmxm_flyscan_xray_centre_plan.create_devices, "internal_param_type": GridscanInternalParameters, "experiment_param_type": GridScanParams, "callback_collection_type": XrayCentreCallbackCollection, }, - "grid_detect_then_xray_centre": { - "setup": grid_detect_then_xray_centre_plan.create_devices, - "internal_param_type": GridScanWithEdgeDetectInternalParameters, - "experiment_param_type": GridScanWithEdgeDetectParams, - "callback_collection_type": NullPlanCallbackCollection, - }, - "rotation_scan": { - "setup": rotation_scan_plan.create_devices, - "internal_param_type": RotationInternalParameters, - "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, - "internal_param_type": SteppedGridScanInternalParameters, - "experiment_param_type": SteppedGridScanParams, - "callback_collection_type": NullPlanCallbackCollection, - }, } EXPERIMENT_NAMES = list(PLAN_REGISTRY.keys()) EXPERIMENT_TYPE_LIST = [p["experiment_param_type"] for p in PLAN_REGISTRY.values()] diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py index 25de80e63..151f81052 100755 --- a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -50,14 +50,14 @@ class VmxmFlyScanXRayCentreComposite: backlight: Backlight eiger: EigerDetector fast_grid_scan: FastGridScan - smargon: Smargon + # smargon: Smargon synchrotron: Synchrotron zebra: Zebra - @property - def sample_motors(self) -> Smargon: - """Convenience alias with a more user-friendly name""" - return self.smargon + # @property + # def sample_motors(self) -> Smargon: + # """Convenience alias with a more user-friendly name""" + # return self.smargon def create_devices(context: BlueskyContext) -> VmxmFlyScanXRayCentreComposite: @@ -96,11 +96,11 @@ def run_gridscan( "plan_name": "run_gridscan", }, ): - sample_motors = fgs_composite.sample_motors + # sample_motors = fgs_composite.sample_motors - # Currently gridscan only works for omega 0, see # - with TRACER.start_span("moving_omega_to_0"): - yield from bps.abs_set(sample_motors.omega, 0) + # # Currently gridscan only works for omega 0, see # + # with TRACER.start_span("moving_omega_to_0"): + # yield from bps.abs_set(sample_motors.omega, 0) fgs_motors = fgs_composite.fast_grid_scan @@ -116,7 +116,9 @@ def run_gridscan( ) def do_fgs(): yield from bps.wait() # Wait for all moves to complete + hyperion.log.LOGGER.info("Kicking off") yield from bps.kickoff(fgs_motors) + yield from bps.complete(fgs_motors, wait=True) hyperion.log.LOGGER.info("Waiting for arming to finish") @@ -150,13 +152,13 @@ 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 = 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)), - ] - ) + # 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_vmxm_zebra_for_gridscan(fgs_composite.zebra) @@ -167,12 +169,12 @@ def run_gridscan_and_move( # the data were submitted to zocalo by the zocalo callback during the gridscan, # but results may not be ready, and need to be collected regardless. # it might not be ideal to block for this, see #327 - xray_centre, _ = subscriptions.zocalo_handler.wait_for_results(initial_xyz) + # xray_centre, _ = subscriptions.zocalo_handler.wait_for_results(initial_xyz) - # once we have the results, go to the appropriate position - hyperion.log.LOGGER.info("Moving to centre of mass.") - with TRACER.start_span("move_to_result"): - yield from move_x_y_z(fgs_composite.sample_motors, *xray_centre, wait=True) + # # once we have the results, go to the appropriate position + # hyperion.log.LOGGER.info("Moving to centre of mass.") + # with TRACER.start_span("move_to_result"): + # yield from move_x_y_z(fgs_composite.sample_motors, *xray_centre, wait=True) def transmission_for_collection_wrapper( @@ -220,7 +222,7 @@ def _set_transmission_to_1(): ) -def flyscan_xray_centre( +def vmxm_flyscan_xray_centre( composite: VmxmFlyScanXRayCentreComposite, parameters: Any, ) -> MsgGenerator: @@ -239,14 +241,11 @@ def flyscan_xray_centre( subscriptions = XrayCentreCallbackCollection.from_params(parameters) - yield from bps.mv( - composite.attenuator, - parameters.hyperion_params.ispyb_params.transmission_fraction, - ) - @bpp.subs_decorator( # subscribe the RE to nexus, ispyb, and zocalo callbacks - list(subscriptions) # must be the outermost decorator to receive the metadata - ) + + #@bpp.subs_decorator( # subscribe the RE to nexus, ispyb, and zocalo callbacks + # list(subscriptions) # must be the outermost decorator to receive the metadata + #) @bpp.set_run_key_decorator("run_gridscan_move_and_tidy") @bpp.run_decorator( # attach experiment metadata to the start document md={ @@ -260,6 +259,11 @@ def flyscan_xray_centre( parameters.hyperion_params.ispyb_params.transmission_fraction, ) def run_gridscan_and_move_and_tidy(fgs_composite, params, comms): + yield from bps.mv( + composite.attenuator, + parameters.hyperion_params.ispyb_params.transmission_fraction, + ) + yield from run_gridscan_and_move(fgs_composite, params, comms) return run_gridscan_and_move_and_tidy(composite, parameters, subscriptions) diff --git a/test_parameters.json b/test_parameters.json index f75f07ef2..75a986220 100644 --- a/test_parameters.json +++ b/test_parameters.json @@ -3,13 +3,13 @@ "hyperion_params": { "beamline": "BL03S", "insertion_prefix": "SR03S", - "detector": "EIGER2_X_16M", + "detector": "EIGER2_X_9M", "zocalo_environment": "dev_artemis", - "experiment_type": "flyscan_xray_centre", + "experiment_type": "vmxm_flyscan_xray_centre", "detector_params": { - "current_energy_ev": 100, - "directory": "/tmp", - "prefix": "file_name", + "current_energy_ev": 6000, + "directory": "/dls/mx/data/cm33865/cm33865-5/hyperion_tests/", + "prefix": "test_hyperion_fgs", "run_number": 0, "use_roi_mode": false, "det_dist_to_beam_converter_path": "src/hyperion/unit_tests/test_lookup_table.txt" @@ -59,17 +59,17 @@ "experiment_params": { "x_steps": 5, "y_steps": 10, - "z_steps": 2, + "z_steps": 0, "x_step_size": 0.1, "y_step_size": 0.1, "z_step_size": 0.1, - "dwell_time": 0.2, + "dwell_time": 5, "x_start": 0.0, "y1_start": 0.0, "y2_start": 0.0, "z1_start": 0.0, "z2_start": 0.0, - "exposure_time": 0.1, + "exposure_time": 0.005, "detector_distance": 100.0, "omega_start": 0.0 } From 627e3c778cad52aa478def821cf4c2ff010cc07c Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Fri, 3 Nov 2023 14:48:49 +0000 Subject: [PATCH 3/7] VMXm: fast grid scan beamline changes --- run_hyperion.sh | 2 +- .../vmxm_flyscan_xray_centre_plan.py | 46 ++++++++++++------- .../callbacks/ispyb_callback_base.py | 33 ++++++------- .../xray_centre/callback_collection.py | 6 +-- .../callbacks/xray_centre/nexus_callback.py | 15 +++--- .../external_interaction/nexus/nexus_utils.py | 8 +--- .../external_interaction/nexus/write_nexus.py | 4 +- src/hyperion/unit_tests/test_lookup_table.txt | 4 +- 8 files changed, 64 insertions(+), 54 deletions(-) diff --git a/run_hyperion.sh b/run_hyperion.sh index 46f9944dd..879315c81 100755 --- a/run_hyperion.sh +++ b/run_hyperion.sh @@ -92,7 +92,7 @@ if [[ $START == 1 ]]; then exit 1 fi - ISPYB_CONFIG_PATH="/dls_sw/dasc/mariadb/credentials/ispyb-artemis-${BEAMLINE}.cfg" + ISPYB_CONFIG_PATH="/dls_sw/dasc/mariadb/credentials/ispyb-artemis-i03.cfg" export ISPYB_CONFIG_PATH fi diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py index 151f81052..3a5dd61cb 100755 --- a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -32,7 +32,7 @@ XrayCentreCallbackCollection, ) from hyperion.parameters import external_parameters -from hyperion.parameters.constants import SIM_BEAMLINE +from hyperion.parameters.constants import SIM_BEAMLINE, ISPYB_PLAN_NAME from hyperion.tracing import TRACER from hyperion.utils.context import device_composite_from_context, setup_context @@ -104,9 +104,19 @@ def run_gridscan( fgs_motors = fgs_composite.fast_grid_scan + hyperion.log.LOGGER.info("TOM 1") + yield from bps.create( + name=ISPYB_PLAN_NAME + ) + yield from bps.read(fgs_composite.synchrotron.machine_status.synchrotron_mode) + yield from bps.save() + hyperion.log.LOGGER.info("TOM 2") + # TODO: Check topup gate yield from set_flyscan_params(fgs_motors, parameters.experiment_params) + hyperion.log.LOGGER.info("TOM 3") yield from wait_for_gridscan_valid(fgs_motors) + hyperion.log.LOGGER.info("TOM 4") @bpp.set_run_key_decorator("do_fgs") @bpp.run_decorator(md={"subplan_name": "do_fgs"}) @@ -118,12 +128,16 @@ def do_fgs(): yield from bps.wait() # Wait for all moves to complete hyperion.log.LOGGER.info("Kicking off") yield from bps.kickoff(fgs_motors) + hyperion.log.LOGGER.info("TOM 5") yield from bps.complete(fgs_motors, wait=True) + hyperion.log.LOGGER.info("TOM 6") hyperion.log.LOGGER.info("Waiting for arming to finish") yield from bps.wait("ready_for_data_collection") + hyperion.log.LOGGER.info("TOM 7") yield from bps.stage(fgs_composite.eiger) + hyperion.log.LOGGER.info("TOM 8") with TRACER.start_span("do_fgs"): yield from do_fgs() @@ -217,9 +231,9 @@ def _set_transmission_to_1(): ) -transmission_for_collection_decorator = make_decorator( - transmission_for_collection_wrapper -) +# transmission_for_collection_decorator = make_decorator( +# transmission_for_collection_wrapper +# ) def vmxm_flyscan_xray_centre( @@ -241,11 +255,9 @@ def vmxm_flyscan_xray_centre( subscriptions = XrayCentreCallbackCollection.from_params(parameters) - - - #@bpp.subs_decorator( # subscribe the RE to nexus, ispyb, and zocalo callbacks - # list(subscriptions) # must be the outermost decorator to receive the metadata - #) + @bpp.subs_decorator( # subscribe the RE to nexus, ispyb callbacks + list(subscriptions) # must be the outermost decorator to receive the metadata + ) @bpp.set_run_key_decorator("run_gridscan_move_and_tidy") @bpp.run_decorator( # attach experiment metadata to the start document md={ @@ -254,15 +266,15 @@ def vmxm_flyscan_xray_centre( } ) @bpp.finalize_decorator(lambda: tidy_up_plans(composite)) - @transmission_for_collection_decorator( - composite.attenuator, - parameters.hyperion_params.ispyb_params.transmission_fraction, - ) + # @transmission_for_collection_decorator( + # composite.attenuator, + # parameters.hyperion_params.ispyb_params.transmission_fraction, + # ) def run_gridscan_and_move_and_tidy(fgs_composite, params, comms): - yield from bps.mv( - composite.attenuator, - parameters.hyperion_params.ispyb_params.transmission_fraction, - ) + # yield from bps.mv( + # composite.attenuator, + # parameters.hyperion_params.ispyb_params.transmission_fraction, + # ) yield from run_gridscan_and_move(fgs_composite, params, comms) diff --git a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py index e7a7453ff..b37a65811 100644 --- a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py +++ b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py @@ -39,6 +39,7 @@ def descriptor(self, doc: dict): self.descriptors[doc["uid"]] = doc def start(self, doc: dict): + LOGGER.info(f"ISPyB handler received start document {doc}.") if self.uid_to_finalize_on is None: self.uid_to_finalize_on = doc.get("uid") @@ -46,31 +47,31 @@ def event(self, doc: dict): """Subclasses should extend this to add a call to set_dcig_tag from hyperion.log""" - LOGGER.debug("ISPyB handler received event document.") + LOGGER.info(f"ISPyB handler received event document {doc}.") assert isinstance( self.ispyb, StoreInIspyb ), "ISPyB deposition can't be initialised!" event_descriptor = self.descriptors[doc["descriptor"]] if event_descriptor.get("name") == ISPYB_PLAN_NAME: - self.params.hyperion_params.ispyb_params.undulator_gap = doc["data"][ - "undulator_gap" - ] + # self.params.hyperion_params.ispyb_params.undulator_gap = doc["data"][ + # "undulator_gap" + # ] self.params.hyperion_params.ispyb_params.synchrotron_mode = doc["data"][ "synchrotron_machine_status_synchrotron_mode" ] - self.params.hyperion_params.ispyb_params.slit_gap_size_x = doc["data"][ - "s4_slit_gaps_xgap" - ] - self.params.hyperion_params.ispyb_params.slit_gap_size_y = doc["data"][ - "s4_slit_gaps_ygap" - ] - self.params.hyperion_params.ispyb_params.transmission_fraction = doc[ - "data" - ]["attenuator_actual_transmission"] - self.params.hyperion_params.ispyb_params.flux = doc["data"][ - "flux_flux_reading" - ] + # self.params.hyperion_params.ispyb_params.slit_gap_size_x = doc["data"][ + # "s4_slit_gaps_xgap" + # ] + # self.params.hyperion_params.ispyb_params.slit_gap_size_y = doc["data"][ + # "s4_slit_gaps_ygap" + # ] + # self.params.hyperion_params.ispyb_params.transmission_fraction = doc[ + # "data" + # ]["attenuator_actual_transmission"] + # self.params.hyperion_params.ispyb_params.flux = doc["data"][ + # "flux_flux_reading" + # ] LOGGER.info("Creating ispyb entry.") self.ispyb_ids = self.ispyb.begin_deposition() diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py b/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py index 65b1eb4b1..5a482d4f2 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py @@ -28,16 +28,16 @@ class XrayCentreCallbackCollection(AbstractPlanCallbackCollection): nexus_handler: GridscanNexusFileCallback ispyb_handler: GridscanISPyBCallback - zocalo_handler: XrayCentreZocaloCallback + # zocalo_handler: XrayCentreZocaloCallback @classmethod def from_params(cls, parameters: InternalParameters): nexus_handler = GridscanNexusFileCallback() ispyb_handler = GridscanISPyBCallback(parameters) - zocalo_handler = XrayCentreZocaloCallback(parameters, ispyb_handler) + # zocalo_handler = XrayCentreZocaloCallback(parameters, ispyb_handler) callback_collection = cls( nexus_handler=nexus_handler, ispyb_handler=ispyb_handler, - zocalo_handler=zocalo_handler, + # zocalo_handler=zocalo_handler, ) return callback_collection diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py b/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py index 7cee5ce70..bf5b1c5d8 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py @@ -55,12 +55,13 @@ def descriptor(self, doc): LOGGER.info("Initialising nexus writers") nexus_data_1 = self.parameters.get_nexus_info(1) - nexus_data_2 = self.parameters.get_nexus_info(2) + LOGGER.info(f"Nexus data 1: {nexus_data_1}") + # nexus_data_2 = self.parameters.get_nexus_info(2) self.nexus_writer_1 = NexusWriter(self.parameters, **nexus_data_1) - self.nexus_writer_2 = NexusWriter( - self.parameters, - **nexus_data_2, - vds_start_index=nexus_data_1["data_shape"][0], - ) + # self.nexus_writer_2 = NexusWriter( + # self.parameters, + # **nexus_data_2, + # vds_start_index=nexus_data_1["data_shape"][0], + # ) self.nexus_writer_1.create_nexus_file() - self.nexus_writer_2.create_nexus_file() + # self.nexus_writer_2.create_nexus_file() diff --git a/src/hyperion/external_interaction/nexus/nexus_utils.py b/src/hyperion/external_interaction/nexus/nexus_utils.py index 6365bb20a..26a82ba59 100644 --- a/src/hyperion/external_interaction/nexus/nexus_utils.py +++ b/src/hyperion/external_interaction/nexus/nexus_utils.py @@ -10,7 +10,7 @@ from hyperion.external_interaction.ispyb.ispyb_dataclass import IspybParams -def create_goniometer_axes( +def create_vmxm_goniometer_axes( omega_start: float, scan_points: dict | None, x_y_z_increments: tuple[float, float, float] = (0.0, 0.0, 0.0), @@ -54,11 +54,7 @@ def create_goniometer_axes( vector=(1.0, 0.0, 0.0), start_pos=0.0, increment=x_y_z_increments[0], - ), - Axis( - "chi", "sam_x", TransformationType.ROTATION, (0.006, -0.0264, 0.9996), 0.0 - ), - Axis("phi", "chi", TransformationType.ROTATION, (-1, -0.0025, -0.0056), 0.0), + ) ] return Goniometer(gonio_axes, scan_points) diff --git a/src/hyperion/external_interaction/nexus/write_nexus.py b/src/hyperion/external_interaction/nexus/write_nexus.py index 34f558b8d..e5c1512d6 100644 --- a/src/hyperion/external_interaction/nexus/write_nexus.py +++ b/src/hyperion/external_interaction/nexus/write_nexus.py @@ -13,7 +13,7 @@ from hyperion.external_interaction.nexus.nexus_utils import ( create_beam_and_attenuator_parameters, create_detector_parameters, - create_goniometer_axes, + create_vmxm_goniometer_axes, get_start_and_predicted_end_time, ) from hyperion.parameters.internal_parameters import InternalParameters @@ -66,7 +66,7 @@ def __init__( self.master_file: Path = ( self.directory / f"{self.filename}_{self.run_number}_master.h5" ) - self.goniometer: Goniometer = create_goniometer_axes( + self.goniometer: Goniometer = create_vmxm_goniometer_axes( self.omega_start, self.scan_points ) diff --git a/src/hyperion/unit_tests/test_lookup_table.txt b/src/hyperion/unit_tests/test_lookup_table.txt index 16fa297a0..a404ae943 100644 --- a/src/hyperion/unit_tests/test_lookup_table.txt +++ b/src/hyperion/unit_tests/test_lookup_table.txt @@ -1,5 +1,5 @@ # Beam converter lookup table for testing Units det_dist beam_x beam_y -100.0 150.0 160.0 -200.0 151.0 165.0 +300 123.35 130.64 +593 123.35 130.64 From 2b750e08ec4e65387f466b27ab87badc9b1117c6 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Tue, 7 Nov 2023 15:36:23 +0000 Subject: [PATCH 4/7] VMXm fast grid scans: hyperion tidy-up --- deploy/deploy_hyperion.py | 6 +- setup.cfg | 2 +- src/hyperion/experiment_plans/__init__.py | 6 +- .../experiment_plans/experiment_registry.py | 36 ++++- .../tests/test_grid_detection_plan.py | 2 +- .../vmxm_flyscan_xray_centre_plan.py | 142 +++--------------- .../callbacks/ispyb_callback_base.py | 38 ++--- .../xray_centre/callback_collection.py | 25 ++- .../callbacks/xray_centre/nexus_callback.py | 47 +++++- .../external_interaction/nexus/nexus_utils.py | 68 ++++++++- .../unit_tests/test_write_nexus.py | 2 +- .../plan_specific/gridscan_internal_params.py | 2 +- src/hyperion/unit_tests/test_lookup_table.txt | 4 +- test_parameters.json | 19 ++- 14 files changed, 228 insertions(+), 171 deletions(-) diff --git a/deploy/deploy_hyperion.py b/deploy/deploy_hyperion.py index 33d8fae34..16f07b30c 100644 --- a/deploy/deploy_hyperion.py +++ b/deploy/deploy_hyperion.py @@ -5,7 +5,7 @@ from git import Repo from packaging.version import Version -recognised_beamlines = ["dev", "i03"] +recognised_beamlines = ["dev", "i03", "i02-1"] class repo: @@ -32,7 +32,7 @@ def deploy(self, url): deploy_repo.git.checkout(self.latest_version_str) print("Setting permissions") - groups_to_give_permission = ["i03_staff", "gda2", "dls_dasc"] + groups_to_give_permission = ["i02-1_staff", "gda2", "dls_dasc"] setfacl_params = ",".join( [f"g:{group}:rwx" for group in groups_to_give_permission] ) @@ -69,6 +69,8 @@ def get_hyperion_release_dir_from_args(repo: repo) -> str: return "/tmp/hyperion_release_test/bluesky" elif args.beamline == "i03": return f"/dls_sw/{args.beamline}/software/bluesky" + elif args.beamline == "i02-1": + return f"/dls_sw/{args.beamline}/software/bluesky" else: raise Exception("not running in dev mode, exiting... (remove this)") diff --git a/setup.cfg b/setup.cfg index c434ab0be..92d80c3f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,7 @@ install_requires = xarray doct databroker - dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@ef3ef65cf717439567d23900d251f5d937fb4633 + dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@9adc9ee996ff70a1e3f340def3f85e10a318af82 pydantic<2.0 # See https://github.com/DiamondLightSource/hyperion/issues/774 diff --git a/src/hyperion/experiment_plans/__init__.py b/src/hyperion/experiment_plans/__init__.py index ffcbc9009..1008726b9 100644 --- a/src/hyperion/experiment_plans/__init__.py +++ b/src/hyperion/experiment_plans/__init__.py @@ -2,7 +2,6 @@ The __all__ list in here are the plans that are externally available from outside Hyperion. """ -from hyperion.experiment_plans.vmxm_flyscan_xray_centre_plan import vmxm_flyscan_xray_centre from hyperion.experiment_plans.flyscan_xray_centre_plan import flyscan_xray_centre from hyperion.experiment_plans.grid_detect_then_xray_centre_plan import ( grid_detect_then_xray_centre, @@ -11,11 +10,14 @@ pin_tip_centre_then_xray_centre, ) from hyperion.experiment_plans.rotation_scan_plan import rotation_scan +from hyperion.experiment_plans.vmxm_flyscan_xray_centre_plan import ( + vmxm_flyscan_xray_centre, +) __all__ = [ "flyscan_xray_centre", "grid_detect_then_xray_centre", "rotation_scan", "pin_tip_centre_then_xray_centre", - "vmxm_flyscan_xray_centre" + "vmxm_flyscan_xray_centre", ] diff --git a/src/hyperion/experiment_plans/experiment_registry.py b/src/hyperion/experiment_plans/experiment_registry.py index 7c4f8bf44..2973a3dc7 100644 --- a/src/hyperion/experiment_plans/experiment_registry.py +++ b/src/hyperion/experiment_plans/experiment_registry.py @@ -4,12 +4,13 @@ from dodal.devices.fast_grid_scan import GridScanParams -import hyperion.experiment_plans.vmxm_flyscan_xray_centre_plan as vmxm_flyscan_xray_centre_plan import hyperion.experiment_plans.rotation_scan_plan as rotation_scan_plan from hyperion.experiment_plans import ( + flyscan_xray_centre_plan, grid_detect_then_xray_centre_plan, pin_centre_then_xray_centre_plan, stepped_grid_scan_plan, + vmxm_flyscan_xray_centre_plan, ) from hyperion.external_interaction.callbacks.abstract_plan_callback_collection import ( NullPlanCallbackCollection, @@ -18,6 +19,7 @@ RotationCallbackCollection, ) from hyperion.external_interaction.callbacks.xray_centre.callback_collection import ( + VmxmFastGridScanCallbackCollection, XrayCentreCallbackCollection, ) from hyperion.parameters.plan_specific.grid_scan_with_edge_detect_params import ( @@ -51,11 +53,41 @@ def do_nothing(): EXPERIMENT_TYPES = Union[GridScanParams, RotationScanParams, SteppedGridScanParams] PLAN_REGISTRY: dict[str, dict[str, Callable]] = { + "flyscan_xray_centre": { + "setup": flyscan_xray_centre_plan.create_devices, + "internal_param_type": GridscanInternalParameters, + "experiment_param_type": GridScanParams, + "callback_collection_type": XrayCentreCallbackCollection, + }, "vmxm_flyscan_xray_centre": { "setup": vmxm_flyscan_xray_centre_plan.create_devices, "internal_param_type": GridscanInternalParameters, "experiment_param_type": GridScanParams, - "callback_collection_type": XrayCentreCallbackCollection, + "callback_collection_type": VmxmFastGridScanCallbackCollection, + }, + "grid_detect_then_xray_centre": { + "setup": grid_detect_then_xray_centre_plan.create_devices, + "internal_param_type": GridScanWithEdgeDetectInternalParameters, + "experiment_param_type": GridScanWithEdgeDetectParams, + "callback_collection_type": NullPlanCallbackCollection, + }, + "rotation_scan": { + "setup": rotation_scan_plan.create_devices, + "internal_param_type": RotationInternalParameters, + "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, + "internal_param_type": SteppedGridScanInternalParameters, + "experiment_param_type": SteppedGridScanParams, + "callback_collection_type": NullPlanCallbackCollection, }, } EXPERIMENT_NAMES = list(PLAN_REGISTRY.keys()) diff --git a/src/hyperion/experiment_plans/tests/test_grid_detection_plan.py b/src/hyperion/experiment_plans/tests/test_grid_detection_plan.py index ecea2a066..dfd350c2b 100644 --- a/src/hyperion/experiment_plans/tests/test_grid_detection_plan.py +++ b/src/hyperion/experiment_plans/tests/test_grid_detection_plan.py @@ -4,7 +4,7 @@ from bluesky.run_engine import RunEngine from dodal.beamlines import i03 from dodal.devices.backlight import Backlight -from dodal.devices.fast_grid_scan import GridAxis +from dodal.devices.fast_grid_scan_common import GridAxis from dodal.devices.oav.oav_detector import OAV from dodal.devices.oav.oav_parameters import OAVParameters from dodal.devices.smargon import Smargon diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py index 3a5dd61cb..ed58e2f72 100755 --- a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -6,16 +6,15 @@ import bluesky.plan_stubs as bps import bluesky.preprocessors as bpp -import numpy as np from blueapi.core import BlueskyContext, MsgGenerator -from bluesky.preprocessors import finalize_wrapper, make_decorator from bluesky.run_engine import RunEngine from bluesky.utils import ProgressBarManager -from dodal.devices.backlight import Backlight +from dodal.devices.backlight import VmxmBacklight from dodal.devices.eiger import EigerDetector -from dodal.devices.fast_grid_scan import FastGridScan -from dodal.devices.fast_grid_scan import set_fast_grid_scan_params as set_flyscan_params -from dodal.devices.smargon import Smargon +from dodal.devices.fast_grid_scan_2d import FastGridScan2D +from dodal.devices.fast_grid_scan_2d import ( + set_fast_grid_scan_params as set_flyscan_params, +) from dodal.devices.synchrotron import Synchrotron from dodal.devices.vmxm.vmxm_attenuator import VmxmAttenuator from dodal.devices.zebra import ( @@ -23,16 +22,15 @@ ) import hyperion.log -from hyperion.device_setup_plans.manipulate_sample import move_x_y_z from hyperion.device_setup_plans.setup_zebra import ( set_zebra_shutter_to_manual, ) from hyperion.exceptions import WarningException from hyperion.external_interaction.callbacks.xray_centre.callback_collection import ( - XrayCentreCallbackCollection, + VmxmFastGridScanCallbackCollection, ) from hyperion.parameters import external_parameters -from hyperion.parameters.constants import SIM_BEAMLINE, ISPYB_PLAN_NAME +from hyperion.parameters.constants import ISPYB_PLAN_NAME, SIM_BEAMLINE from hyperion.tracing import TRACER from hyperion.utils.context import device_composite_from_context, setup_context @@ -47,25 +45,19 @@ class VmxmFlyScanXRayCentreComposite: """All devices which are directly or indirectly required by this plan""" attenuator: VmxmAttenuator - backlight: Backlight + backlight: VmxmBacklight eiger: EigerDetector - fast_grid_scan: FastGridScan - # smargon: Smargon + fast_grid_scan: FastGridScan2D synchrotron: Synchrotron zebra: Zebra - # @property - # def sample_motors(self) -> Smargon: - # """Convenience alias with a more user-friendly name""" - # return self.smargon - def create_devices(context: BlueskyContext) -> VmxmFlyScanXRayCentreComposite: """Creates the devices required for the plan and connect to them""" return device_composite_from_context(context, VmxmFlyScanXRayCentreComposite) -def wait_for_gridscan_valid(fgs_motors: FastGridScan, timeout=0.5): +def wait_for_gridscan_valid(fgs_motors: FastGridScan2D, timeout=0.5): hyperion.log.LOGGER.info("Waiting for valid fgs_params") SLEEP_PER_CHECK = 0.1 times_to_check = int(timeout / SLEEP_PER_CHECK) @@ -79,7 +71,7 @@ def wait_for_gridscan_valid(fgs_motors: FastGridScan, timeout=0.5): hyperion.log.LOGGER.info("Gridscan scan valid and position counter reset") return yield from bps.sleep(SLEEP_PER_CHECK) - raise WarningException("Scan invalid - pin too long/short/bent and out of range") + raise WarningException("Scan invalid") def tidy_up_plans(fgs_composite: VmxmFlyScanXRayCentreComposite): @@ -96,27 +88,15 @@ def run_gridscan( "plan_name": "run_gridscan", }, ): - # sample_motors = fgs_composite.sample_motors - - # # Currently gridscan only works for omega 0, see # - # with TRACER.start_span("moving_omega_to_0"): - # yield from bps.abs_set(sample_motors.omega, 0) - fgs_motors = fgs_composite.fast_grid_scan - hyperion.log.LOGGER.info("TOM 1") - yield from bps.create( - name=ISPYB_PLAN_NAME - ) + yield from bps.create(name=ISPYB_PLAN_NAME) yield from bps.read(fgs_composite.synchrotron.machine_status.synchrotron_mode) yield from bps.save() - hyperion.log.LOGGER.info("TOM 2") # TODO: Check topup gate yield from set_flyscan_params(fgs_motors, parameters.experiment_params) - hyperion.log.LOGGER.info("TOM 3") yield from wait_for_gridscan_valid(fgs_motors) - hyperion.log.LOGGER.info("TOM 4") @bpp.set_run_key_decorator("do_fgs") @bpp.run_decorator(md={"subplan_name": "do_fgs"}) @@ -128,16 +108,12 @@ def do_fgs(): yield from bps.wait() # Wait for all moves to complete hyperion.log.LOGGER.info("Kicking off") yield from bps.kickoff(fgs_motors) - hyperion.log.LOGGER.info("TOM 5") yield from bps.complete(fgs_motors, wait=True) - hyperion.log.LOGGER.info("TOM 6") hyperion.log.LOGGER.info("Waiting for arming to finish") yield from bps.wait("ready_for_data_collection") - hyperion.log.LOGGER.info("TOM 7") yield from bps.stage(fgs_composite.eiger) - hyperion.log.LOGGER.info("TOM 8") with TRACER.start_span("do_fgs"): yield from do_fgs() @@ -148,8 +124,12 @@ def do_fgs(): def setup_vmxm_zebra_for_gridscan( zebra: Zebra, group="setup_zebra_for_gridscan", wait=False ): - # VMXm-specific - yield from bps.abs_set(zebra.output.out_pvs[4], 1, group=group) + # note: VMXm-specific + vmxm_zebra_input = 4 + vmxm_zebra_output = 1 + yield from bps.abs_set( + zebra.output.out_pvs[vmxm_zebra_input], vmxm_zebra_output, group=group + ) if wait: yield from bps.wait(group) @@ -160,81 +140,14 @@ def setup_vmxm_zebra_for_gridscan( def run_gridscan_and_move( fgs_composite: VmxmFlyScanXRayCentreComposite, parameters: GridscanInternalParameters, - subscriptions: XrayCentreCallbackCollection, + subscriptions: VmxmFastGridScanCallbackCollection, ): - """A multi-run plan which runs a gridscan, gets the results from zocalo - 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 = 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_vmxm_zebra_for_gridscan(fgs_composite.zebra) hyperion.log.LOGGER.info("Starting grid scan") yield from run_gridscan(fgs_composite, parameters) - # the data were submitted to zocalo by the zocalo callback during the gridscan, - # but results may not be ready, and need to be collected regardless. - # it might not be ideal to block for this, see #327 - # xray_centre, _ = subscriptions.zocalo_handler.wait_for_results(initial_xyz) - - # # once we have the results, go to the appropriate position - # hyperion.log.LOGGER.info("Moving to centre of mass.") - # with TRACER.start_span("move_to_result"): - # yield from move_x_y_z(fgs_composite.sample_motors, *xray_centre, wait=True) - - -def transmission_for_collection_wrapper( - plan, - attenuator: VmxmAttenuator, - desired_transmission_fraction: float, -): - """Sets the transmission for the data collection, esuring the xbpm feedback is valid - this wrapper should be run around every data collection. - - XBPM feedback isn't reliable during collections due to: - * Objects (e.g. attenuator) crossing the beam can cause large (incorrect) feedback movements - * Lower transmissions/higher energies are less reliable for the xbpm - - So we need to keep the transmission at 100% and the feedback on when not collecting - and then turn it off and set the correct transmission for collection. The feedback - mostly accounts for slow thermal drift so it is safe to assume that the beam is - stable during a collection. - - Args: - plan: The plan performing the data collection - xbpm_feedback (XBPMFeedback): The XBPM device that is responsible for keeping - the beam in position - attenuator (Attenuator): The attenuator used to set transmission - desired_transmission_fraction (float): The desired transmission for the collection - """ - - def _inner_plan(): - yield from bps.mv(attenuator, desired_transmission_fraction) - return (yield from plan) - - def _set_transmission_to_1(): - yield from bps.mv(attenuator, 1.0) - - return ( - yield from finalize_wrapper( - _inner_plan(), - _set_transmission_to_1(), - ) - ) - - -# transmission_for_collection_decorator = make_decorator( -# transmission_for_collection_wrapper -# ) - def vmxm_flyscan_xray_centre( composite: VmxmFlyScanXRayCentreComposite, @@ -253,10 +166,10 @@ def vmxm_flyscan_xray_centre( """ composite.eiger.set_detector_parameters(parameters.hyperion_params.detector_params) - subscriptions = XrayCentreCallbackCollection.from_params(parameters) + subscriptions = VmxmFastGridScanCallbackCollection.from_params(parameters) @bpp.subs_decorator( # subscribe the RE to nexus, ispyb callbacks - list(subscriptions) # must be the outermost decorator to receive the metadata + list(subscriptions) # must be the outermost decorator to receive the metadata ) @bpp.set_run_key_decorator("run_gridscan_move_and_tidy") @bpp.run_decorator( # attach experiment metadata to the start document @@ -266,16 +179,7 @@ def vmxm_flyscan_xray_centre( } ) @bpp.finalize_decorator(lambda: tidy_up_plans(composite)) - # @transmission_for_collection_decorator( - # composite.attenuator, - # parameters.hyperion_params.ispyb_params.transmission_fraction, - # ) def run_gridscan_and_move_and_tidy(fgs_composite, params, comms): - # yield from bps.mv( - # composite.attenuator, - # parameters.hyperion_params.ispyb_params.transmission_fraction, - # ) - yield from run_gridscan_and_move(fgs_composite, params, comms) return run_gridscan_and_move_and_tidy(composite, parameters, subscriptions) @@ -297,9 +201,9 @@ def run_gridscan_and_move_and_tidy(fgs_composite, params, comms): ) parameters = GridscanInternalParameters(**external_parameters.from_file()) - subscriptions = XrayCentreCallbackCollection.from_params(parameters) + subscriptions = VmxmFastGridScanCallbackCollection.from_params(parameters) context = setup_context(wait_for_connection=True) composite = create_devices(context) - RE(flyscan_xray_centre(composite, parameters)) + RE(vmxm_flyscan_xray_centre(composite, parameters)) diff --git a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py index b37a65811..81fc5e3a4 100644 --- a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py +++ b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py @@ -47,31 +47,31 @@ def event(self, doc: dict): """Subclasses should extend this to add a call to set_dcig_tag from hyperion.log""" - LOGGER.info(f"ISPyB handler received event document {doc}.") + LOGGER.debug(f"ISPyB handler received event document {doc}.") assert isinstance( self.ispyb, StoreInIspyb ), "ISPyB deposition can't be initialised!" event_descriptor = self.descriptors[doc["descriptor"]] if event_descriptor.get("name") == ISPYB_PLAN_NAME: - # self.params.hyperion_params.ispyb_params.undulator_gap = doc["data"][ - # "undulator_gap" - # ] - self.params.hyperion_params.ispyb_params.synchrotron_mode = doc["data"][ - "synchrotron_machine_status_synchrotron_mode" - ] - # self.params.hyperion_params.ispyb_params.slit_gap_size_x = doc["data"][ - # "s4_slit_gaps_xgap" - # ] - # self.params.hyperion_params.ispyb_params.slit_gap_size_y = doc["data"][ - # "s4_slit_gaps_ygap" - # ] - # self.params.hyperion_params.ispyb_params.transmission_fraction = doc[ - # "data" - # ]["attenuator_actual_transmission"] - # self.params.hyperion_params.ispyb_params.flux = doc["data"][ - # "flux_flux_reading" - # ] + self.params.hyperion_params.ispyb_params.undulator_gap = doc["data"].get( + "undulator_gap", 0.0 + ) + self.params.hyperion_params.ispyb_params.synchrotron_mode = doc["data"].get( + "synchrotron_machine_status_synchrotron_mode", None + ) + self.params.hyperion_params.ispyb_params.slit_gap_size_x = doc["data"].get( + "s4_slit_gaps_xgap", 0.0 + ) + self.params.hyperion_params.ispyb_params.slit_gap_size_y = doc["data"].get( + "s4_slit_gaps_ygap", 0.0 + ) + self.params.hyperion_params.ispyb_params.transmission_fraction = doc[ + "data" + ].get("attenuator_actual_transmission", 0.0) + self.params.hyperion_params.ispyb_params.flux = doc["data"].get( + "flux_flux_reading", 0.0 + ) LOGGER.info("Creating ispyb entry.") self.ispyb_ids = self.ispyb.begin_deposition() diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py b/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py index 5a482d4f2..b08f7b7e8 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py @@ -10,6 +10,7 @@ GridscanISPyBCallback, ) from hyperion.external_interaction.callbacks.xray_centre.nexus_callback import ( + Gridscan2DNexusFileCallback, GridscanNexusFileCallback, ) from hyperion.external_interaction.callbacks.xray_centre.zocalo_callback import ( @@ -28,16 +29,34 @@ class XrayCentreCallbackCollection(AbstractPlanCallbackCollection): nexus_handler: GridscanNexusFileCallback ispyb_handler: GridscanISPyBCallback - # zocalo_handler: XrayCentreZocaloCallback + zocalo_handler: XrayCentreZocaloCallback @classmethod def from_params(cls, parameters: InternalParameters): nexus_handler = GridscanNexusFileCallback() ispyb_handler = GridscanISPyBCallback(parameters) - # zocalo_handler = XrayCentreZocaloCallback(parameters, ispyb_handler) + zocalo_handler = XrayCentreZocaloCallback(parameters, ispyb_handler) + callback_collection = cls( + nexus_handler=nexus_handler, + ispyb_handler=ispyb_handler, + zocalo_handler=zocalo_handler, + ) + return callback_collection + + +@dataclass(frozen=True, order=True) +class VmxmFastGridScanCallbackCollection(AbstractPlanCallbackCollection): + """Like XRayCentreCallbackCollection, but without zocalo.""" + + nexus_handler: Gridscan2DNexusFileCallback + ispyb_handler: GridscanISPyBCallback + + @classmethod + def from_params(cls, parameters: InternalParameters): + nexus_handler = Gridscan2DNexusFileCallback() + ispyb_handler = GridscanISPyBCallback(parameters) callback_collection = cls( nexus_handler=nexus_handler, ispyb_handler=ispyb_handler, - # zocalo_handler=zocalo_handler, ) return callback_collection diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py b/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py index bf5b1c5d8..699a828f9 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py @@ -56,12 +56,45 @@ def descriptor(self, doc): LOGGER.info("Initialising nexus writers") nexus_data_1 = self.parameters.get_nexus_info(1) LOGGER.info(f"Nexus data 1: {nexus_data_1}") - # nexus_data_2 = self.parameters.get_nexus_info(2) + nexus_data_2 = self.parameters.get_nexus_info(2) self.nexus_writer_1 = NexusWriter(self.parameters, **nexus_data_1) - # self.nexus_writer_2 = NexusWriter( - # self.parameters, - # **nexus_data_2, - # vds_start_index=nexus_data_1["data_shape"][0], - # ) + self.nexus_writer_2 = NexusWriter( + self.parameters, + **nexus_data_2, + vds_start_index=nexus_data_1["data_shape"][0], + ) self.nexus_writer_1.create_nexus_file() - # self.nexus_writer_2.create_nexus_file() + self.nexus_writer_2.create_nexus_file() + + +class Gridscan2DNexusFileCallback(CallbackBase): + """Similar to above, but for a 2D gridscan""" + + def __init__(self) -> None: + self.parameters: GridscanInternalParameters | None = None + self.run_start_uid: str | None = None + self.nexus_writer: NexusWriter | None = None + + def start(self, doc: dict): + if doc.get("subplan_name") == "run_gridscan_move_and_tidy": + LOGGER.info( + "Nexus writer recieved start document with experiment parameters." + ) + json_params = doc.get("hyperion_internal_parameters") + self.parameters = GridscanInternalParameters.from_json(json_params) + self.run_start_uid = doc.get("uid") + + def descriptor(self, doc): + if doc.get("name") == ISPYB_PLAN_NAME: + assert ( + self.parameters is not None + ), "Nexus callback did not receive parameters before being asked to write!" + # TODO instead of ispyb wait for detector parameter reading in plan + # https://github.com/DiamondLightSource/python-hyperion/issues/629 + # and update parameters before creating writers + + LOGGER.info("Initialising nexus writer") + nexus_data = self.parameters.get_nexus_info(1) + LOGGER.info(f"Nexus data: {nexus_data}") + self.nexus_writer = NexusWriter(self.parameters, **nexus_data) + self.nexus_writer.create_nexus_file() diff --git a/src/hyperion/external_interaction/nexus/nexus_utils.py b/src/hyperion/external_interaction/nexus/nexus_utils.py index 26a82ba59..72b4b60f2 100644 --- a/src/hyperion/external_interaction/nexus/nexus_utils.py +++ b/src/hyperion/external_interaction/nexus/nexus_utils.py @@ -4,11 +4,67 @@ from datetime import datetime, timedelta from dodal.devices.detector import DetectorParams +from dodal.utils import get_beamline_name from nexgen.nxs_utils import Attenuator, Axis, Beam, Detector, EigerDetector, Goniometer from nexgen.nxs_utils.Axes import TransformationType from hyperion.external_interaction.ispyb.ispyb_dataclass import IspybParams +BL = get_beamline_name(None) + + +def create_i03_goniometer_axes( + omega_start: float, + scan_points: dict | None, + x_y_z_increments: tuple[float, float, float] = (0.0, 0.0, 0.0), +): + """Returns a Nexgen 'Goniometer' object with the dependency chain of I03's Smargon + goniometer. If scan points is provided these values will be used in preference to + those from the params object. + + Args: + omega_start (float): the starting position of omega, the only extra value that + needs to be specified except for the scan points. + scan_points (dict): a dictionary of points in the scan for each axis. Obtained + by calculating the scan path with scanspec and calling + consume() on it. + x_y_z_increments: optionally, specify the increments between each image for + the x, y, and z axes. Will be ignored if scan_points + is provided. + """ + gonio_axes = [ + Axis("omega", ".", TransformationType.ROTATION, (-1.0, 0.0, 0.0), omega_start), + Axis( + name="sam_z", + depends="omega", + transformation_type=TransformationType.TRANSLATION, + vector=(0.0, 0.0, 1.0), + start_pos=0.0, + increment=x_y_z_increments[2], + ), + Axis( + name="sam_y", + depends="sam_z", + transformation_type=TransformationType.TRANSLATION, + vector=(0.0, 1.0, 0.0), + start_pos=0.0, + increment=x_y_z_increments[1], + ), + Axis( + name="sam_x", + depends="sam_y", + transformation_type=TransformationType.TRANSLATION, + vector=(1.0, 0.0, 0.0), + start_pos=0.0, + increment=x_y_z_increments[0], + ), + Axis( + "chi", "sam_x", TransformationType.ROTATION, (0.006, -0.0264, 0.9996), 0.0 + ), + Axis("phi", "chi", TransformationType.ROTATION, (-1, -0.0025, -0.0056), 0.0), + ] + return Goniometer(gonio_axes, scan_points) + def create_vmxm_goniometer_axes( omega_start: float, @@ -54,11 +110,21 @@ def create_vmxm_goniometer_axes( vector=(1.0, 0.0, 0.0), start_pos=0.0, increment=x_y_z_increments[0], - ) + ), ] return Goniometer(gonio_axes, scan_points) +if BL == "i03": + create_goniometer_axes = create_i03_goniometer_axes +elif BL == "i02-1": + create_goniometer_axes = create_vmxm_goniometer_axes +else: + + def create_goniometer_axes(*a, **k): + raise ValueError("Cannot create goniometer axes on unknown beamline") + + def get_start_and_predicted_end_time(time_expected: float) -> tuple[str, str]: time_format = r"%Y-%m-%dT%H:%M:%SZ" start = datetime.utcfromtimestamp(time.time()) diff --git a/src/hyperion/external_interaction/unit_tests/test_write_nexus.py b/src/hyperion/external_interaction/unit_tests/test_write_nexus.py index 243cb13ba..ffc3d22b9 100644 --- a/src/hyperion/external_interaction/unit_tests/test_write_nexus.py +++ b/src/hyperion/external_interaction/unit_tests/test_write_nexus.py @@ -5,7 +5,7 @@ import h5py import numpy as np import pytest -from dodal.devices.fast_grid_scan import GridAxis, GridScanParams +from dodal.devices.fast_grid_scan_common import GridAxis, GridScanParams from hyperion.external_interaction.nexus.write_nexus import NexusWriter from hyperion.parameters.plan_specific.gridscan_internal_params import ( diff --git a/src/hyperion/parameters/plan_specific/gridscan_internal_params.py b/src/hyperion/parameters/plan_specific/gridscan_internal_params.py index 1f2a4d240..77441740b 100644 --- a/src/hyperion/parameters/plan_specific/gridscan_internal_params.py +++ b/src/hyperion/parameters/plan_specific/gridscan_internal_params.py @@ -4,7 +4,7 @@ import numpy as np from dodal.devices.detector import DetectorParams, TriggerMode -from dodal.devices.fast_grid_scan import GridAxis, GridScanParams +from dodal.devices.fast_grid_scan_common import GridAxis, GridScanParams from pydantic import validator from scanspec.core import Path as ScanPath from scanspec.specs import Line diff --git a/src/hyperion/unit_tests/test_lookup_table.txt b/src/hyperion/unit_tests/test_lookup_table.txt index a404ae943..16fa297a0 100644 --- a/src/hyperion/unit_tests/test_lookup_table.txt +++ b/src/hyperion/unit_tests/test_lookup_table.txt @@ -1,5 +1,5 @@ # Beam converter lookup table for testing Units det_dist beam_x beam_y -300 123.35 130.64 -593 123.35 130.64 +100.0 150.0 160.0 +200.0 151.0 165.0 diff --git a/test_parameters.json b/test_parameters.json index 75a986220..34e22570c 100644 --- a/test_parameters.json +++ b/test_parameters.json @@ -1,15 +1,15 @@ { - "params_version": "3.0.0", + "params_version": "4.0.0", "hyperion_params": { "beamline": "BL03S", "insertion_prefix": "SR03S", - "detector": "EIGER2_X_9M", + "detector": "EIGER2_X_16M", "zocalo_environment": "dev_artemis", - "experiment_type": "vmxm_flyscan_xray_centre", + "experiment_type": "flyscan_xray_centre", "detector_params": { - "current_energy_ev": 6000, - "directory": "/dls/mx/data/cm33865/cm33865-5/hyperion_tests/", - "prefix": "test_hyperion_fgs", + "current_energy_ev": 100, + "directory": "/tmp", + "prefix": "file_name", "run_number": 0, "use_roi_mode": false, "det_dist_to_beam_converter_path": "src/hyperion/unit_tests/test_lookup_table.txt" @@ -45,7 +45,6 @@ ], "transmission_fraction": 1.0, "flux": 10.0, - "wavelength": 0.01, "beam_size_x": 1.0, "beam_size_y": 1.0, "slit_gap_size_x": 1.0, @@ -59,17 +58,17 @@ "experiment_params": { "x_steps": 5, "y_steps": 10, - "z_steps": 0, + "z_steps": 2, "x_step_size": 0.1, "y_step_size": 0.1, "z_step_size": 0.1, - "dwell_time": 5, + "dwell_time_ms": 0.2, "x_start": 0.0, "y1_start": 0.0, "y2_start": 0.0, "z1_start": 0.0, "z2_start": 0.0, - "exposure_time": 0.005, + "exposure_time": 0.1, "detector_distance": 100.0, "omega_start": 0.0 } From a27463bbed18388960f81d53a3ea4bb79cac2ec1 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Tue, 7 Nov 2023 17:16:57 +0000 Subject: [PATCH 5/7] Merge tidy-ups --- deploy/deploy_hyperion.py | 5 +--- setup.cfg | 2 +- .../vmxm_flyscan_xray_centre_plan.py | 22 +++++++++++----- .../callbacks/ispyb_callback_base.py | 6 ++--- .../callbacks/rotation/callback_collection.py | 9 +++++-- .../callbacks/rotation/nexus_callback.py | 9 +++++-- .../xray_centre/callback_collection.py | 16 +++++++++--- .../callbacks/xray_centre/nexus_callback.py | 26 ++++++++++++++----- .../xray_centre/tests/test_nexus_handler.py | 9 ++++--- .../external_interaction/nexus/nexus_utils.py | 15 +---------- .../external_interaction/nexus/write_nexus.py | 18 ++++++++++--- .../unit_tests/test_write_nexus.py | 26 ++++++++++++++++--- src/hyperion/log.py | 7 ++++- test_parameter_defaults.json | 1 + 14 files changed, 117 insertions(+), 54 deletions(-) diff --git a/deploy/deploy_hyperion.py b/deploy/deploy_hyperion.py index aaae3d5c5..ffda7c5ed 100644 --- a/deploy/deploy_hyperion.py +++ b/deploy/deploy_hyperion.py @@ -5,7 +5,7 @@ from git import Repo from packaging.version import Version -recognised_beamlines = ["dev", "i03", "i02-1", "i04"]ain +recognised_beamlines = ["dev", "i03", "i02-1", "i04"] class repo: @@ -67,13 +67,10 @@ def get_hyperion_release_dir_from_args(repo: repo) -> str: if args.beamline == "dev": print("Running as dev") return "/tmp/hyperion_release_test/bluesky" -<<<<<<< HEAD elif args.beamline == "i03": return f"/dls_sw/{args.beamline}/software/bluesky" elif args.beamline == "i02-1": return f"/dls_sw/{args.beamline}/software/bluesky" -======= ->>>>>>> origin/main else: return f"/dls_sw/{args.beamline}/software/bluesky" diff --git a/setup.cfg b/setup.cfg index ada021446..041c41cca 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,7 @@ install_requires = xarray doct databroker - dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@9adc9ee996ff70a1e3f340def3f85e10a318af82 + dls-dodal @ git+https://github.com/DiamondLightSource/dodal.git@39084723b73adec1df9199b9fe601d68242eeb3d pydantic<2.0 # See https://github.com/DiamondLightSource/hyperion/issues/774 scipy diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py index ed58e2f72..b079ba5da 100755 --- a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -22,15 +22,12 @@ ) import hyperion.log -from hyperion.device_setup_plans.setup_zebra import ( - set_zebra_shutter_to_manual, -) from hyperion.exceptions import WarningException from hyperion.external_interaction.callbacks.xray_centre.callback_collection import ( VmxmFastGridScanCallbackCollection, ) from hyperion.parameters import external_parameters -from hyperion.parameters.constants import ISPYB_PLAN_NAME, SIM_BEAMLINE +from hyperion.parameters.constants import ISPYB_HARDWARE_READ_PLAN, SIM_BEAMLINE from hyperion.tracing import TRACER from hyperion.utils.context import device_composite_from_context, setup_context @@ -76,7 +73,7 @@ def wait_for_gridscan_valid(fgs_motors: FastGridScan2D, timeout=0.5): def tidy_up_plans(fgs_composite: VmxmFlyScanXRayCentreComposite): hyperion.log.LOGGER.info("Tidying up Zebra") - yield from set_zebra_shutter_to_manual(fgs_composite.zebra) + yield from tidyup_vmxm_zebra_after_gridscan(fgs_composite.zebra) @bpp.set_run_key_decorator("run_gridscan") @@ -90,7 +87,7 @@ def run_gridscan( ): fgs_motors = fgs_composite.fast_grid_scan - yield from bps.create(name=ISPYB_PLAN_NAME) + yield from bps.create(name=ISPYB_HARDWARE_READ_PLAN) yield from bps.read(fgs_composite.synchrotron.machine_status.synchrotron_mode) yield from bps.save() @@ -135,6 +132,19 @@ def setup_vmxm_zebra_for_gridscan( yield from bps.wait(group) +def tidyup_vmxm_zebra_after_gridscan( + zebra: Zebra, group="tidyup_vmxm_zebra_after_gridscan", wait=False +): # note: VMXm-specific + vmxm_zebra_input = 4 + vmxm_zebra_output = 36 + yield from bps.abs_set( + zebra.output.out_pvs[vmxm_zebra_input], vmxm_zebra_output, group=group + ) + + if wait: + yield from bps.wait(group) + + @bpp.set_run_key_decorator("run_gridscan_and_move") @bpp.run_decorator(md={"subplan_name": "run_gridscan_and_move"}) def run_gridscan_and_move( diff --git a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py index 134247f75..114dc2d11 100644 --- a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py +++ b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py @@ -78,9 +78,9 @@ def event(self, doc: dict): "flux_flux_reading", 0.0 ) - LOGGER.info("Creating ispyb entry.") - self.ispyb_ids = self.ispyb.begin_deposition() - LOGGER.info(f"Recieved ISPYB IDs: {self.ispyb_ids}") + LOGGER.info("Creating ispyb entry.") + self.ispyb_ids = self.ispyb.begin_deposition() + LOGGER.info(f"Recieved ISPYB IDs: {self.ispyb_ids}") def stop(self, doc: dict): """Subclasses must check that they are recieving a stop document for the correct diff --git a/src/hyperion/external_interaction/callbacks/rotation/callback_collection.py b/src/hyperion/external_interaction/callbacks/rotation/callback_collection.py index d23be80de..1a8b3a713 100644 --- a/src/hyperion/external_interaction/callbacks/rotation/callback_collection.py +++ b/src/hyperion/external_interaction/callbacks/rotation/callback_collection.py @@ -15,6 +15,7 @@ from hyperion.external_interaction.callbacks.rotation.zocalo_callback import ( RotationZocaloCallback, ) +from hyperion.external_interaction.nexus.nexus_utils import create_i03_goniometer_axes if TYPE_CHECKING: from hyperion.parameters.plan_specific.rotation_scan_internal_params import ( @@ -25,7 +26,9 @@ @dataclass(frozen=True, order=True) class RotationCallbackCollection(AbstractPlanCallbackCollection): """Groups the callbacks for external interactions for a rotation scan. - Cast to a list to pass it to Bluesky.preprocessors.subs_decorator().""" + Cast to a list to pass it to Bluesky.preprocessors.subs_decorator(). + + Note: specific to i03.""" nexus_handler: RotationNexusFileCallback ispyb_handler: RotationISPyBCallback @@ -33,7 +36,9 @@ class RotationCallbackCollection(AbstractPlanCallbackCollection): @classmethod def from_params(cls, parameters: RotationInternalParameters): - nexus_handler = RotationNexusFileCallback() + nexus_handler = RotationNexusFileCallback( + create_goniometer_axes=create_i03_goniometer_axes + ) ispyb_handler = RotationISPyBCallback(parameters) zocalo_handler = RotationZocaloCallback( parameters.hyperion_params.zocalo_environment, ispyb_handler diff --git a/src/hyperion/external_interaction/callbacks/rotation/nexus_callback.py b/src/hyperion/external_interaction/callbacks/rotation/nexus_callback.py index 3135da361..d3858c610 100644 --- a/src/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +++ b/src/hyperion/external_interaction/callbacks/rotation/nexus_callback.py @@ -2,7 +2,10 @@ from bluesky.callbacks import CallbackBase -from hyperion.external_interaction.nexus.write_nexus import NexusWriter +from hyperion.external_interaction.nexus.write_nexus import ( + CreateGoniometerProtocol, + NexusWriter, +) from hyperion.log import LOGGER from hyperion.parameters.plan_specific.rotation_scan_internal_params import ( RotationInternalParameters, @@ -24,10 +27,11 @@ class RotationNexusFileCallback(CallbackBase): Usually used as part of a RotationCallbackCollection. """ - def __init__(self): + def __init__(self, create_goniometer_axes: CreateGoniometerProtocol): self.run_uid: str | None = None self.parameters: RotationInternalParameters | None = None self.writer: NexusWriter | None = None + self.create_goniometer_axes = create_goniometer_axes def start(self, doc: dict): if doc.get("subplan_name") == "rotation_scan_with_cleanup": @@ -42,5 +46,6 @@ def start(self, doc: dict): self.parameters, self.parameters.get_scan_points(), self.parameters.get_data_shape(), + create_goniometer_func=self.create_goniometer_axes, ) self.writer.create_nexus_file() diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py b/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py index b08f7b7e8..a92eb38eb 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/callback_collection.py @@ -16,6 +16,10 @@ from hyperion.external_interaction.callbacks.xray_centre.zocalo_callback import ( XrayCentreZocaloCallback, ) +from hyperion.external_interaction.nexus.nexus_utils import ( + create_i03_goniometer_axes, + create_vmxm_goniometer_axes, +) if TYPE_CHECKING: from hyperion.parameters.internal_parameters import InternalParameters @@ -25,7 +29,9 @@ class XrayCentreCallbackCollection(AbstractPlanCallbackCollection): """Groups the callbacks for external interactions in the fast grid scan, and connects the Zocalo and ISPyB handlers. Cast to a list to pass it to - Bluesky.preprocessors.subs_decorator().""" + Bluesky.preprocessors.subs_decorator(). + + Note: currently specific to i03 as it creates i03's goniometer axes.""" nexus_handler: GridscanNexusFileCallback ispyb_handler: GridscanISPyBCallback @@ -33,7 +39,7 @@ class XrayCentreCallbackCollection(AbstractPlanCallbackCollection): @classmethod def from_params(cls, parameters: InternalParameters): - nexus_handler = GridscanNexusFileCallback() + nexus_handler = GridscanNexusFileCallback(create_i03_goniometer_axes) ispyb_handler = GridscanISPyBCallback(parameters) zocalo_handler = XrayCentreZocaloCallback(parameters, ispyb_handler) callback_collection = cls( @@ -46,14 +52,16 @@ def from_params(cls, parameters: InternalParameters): @dataclass(frozen=True, order=True) class VmxmFastGridScanCallbackCollection(AbstractPlanCallbackCollection): - """Like XRayCentreCallbackCollection, but without zocalo.""" + """Like XRayCentreCallbackCollection, but without zocalo. + + Note: currently specific to VMXm as it creates VMXm's goniometer axes.""" nexus_handler: Gridscan2DNexusFileCallback ispyb_handler: GridscanISPyBCallback @classmethod def from_params(cls, parameters: InternalParameters): - nexus_handler = Gridscan2DNexusFileCallback() + nexus_handler = Gridscan2DNexusFileCallback(create_vmxm_goniometer_axes) ispyb_handler = GridscanISPyBCallback(parameters) callback_collection = cls( nexus_handler=nexus_handler, diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py b/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py index facd4a4b6..8d49c97a1 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py @@ -2,7 +2,10 @@ from bluesky.callbacks import CallbackBase -from hyperion.external_interaction.nexus.write_nexus import NexusWriter +from hyperion.external_interaction.nexus.write_nexus import ( + CreateGoniometerProtocol, + NexusWriter, +) from hyperion.log import LOGGER from hyperion.parameters.constants import ISPYB_HARDWARE_READ_PLAN from hyperion.parameters.plan_specific.gridscan_internal_params import ( @@ -29,11 +32,12 @@ class GridscanNexusFileCallback(CallbackBase): Usually used as part of an FGSCallbackCollection. """ - def __init__(self) -> None: + def __init__(self, create_goniometer_func: CreateGoniometerProtocol) -> None: self.parameters: GridscanInternalParameters | None = None self.run_start_uid: str | None = None self.nexus_writer_1: NexusWriter | None = None self.nexus_writer_2: NexusWriter | None = None + self.create_goniometer_func = create_goniometer_func def start(self, doc: dict): if doc.get("subplan_name") == "run_gridscan_move_and_tidy": @@ -57,9 +61,14 @@ def descriptor(self, doc): nexus_data_1 = self.parameters.get_nexus_info(1) LOGGER.info(f"Nexus data 1: {nexus_data_1}") nexus_data_2 = self.parameters.get_nexus_info(2) - self.nexus_writer_1 = NexusWriter(self.parameters, **nexus_data_1) + self.nexus_writer_1 = NexusWriter( + self.parameters, + create_goniometer_func=self.create_goniometer_func, + **nexus_data_1, + ) self.nexus_writer_2 = NexusWriter( self.parameters, + create_goniometer_func=self.create_goniometer_func, **nexus_data_2, vds_start_index=nexus_data_1["data_shape"][0], ) @@ -70,10 +79,11 @@ def descriptor(self, doc): class Gridscan2DNexusFileCallback(CallbackBase): """Similar to above, but for a 2D gridscan""" - def __init__(self) -> None: + def __init__(self, create_goniometer_func: CreateGoniometerProtocol) -> None: self.parameters: GridscanInternalParameters | None = None self.run_start_uid: str | None = None self.nexus_writer: NexusWriter | None = None + self.create_goniometer_func = create_goniometer_func def start(self, doc: dict): if doc.get("subplan_name") == "run_gridscan_move_and_tidy": @@ -85,7 +95,7 @@ def start(self, doc: dict): self.run_start_uid = doc.get("uid") def descriptor(self, doc): - if doc.get("name") == ISPYB_PLAN_NAME: + if doc.get("name") == ISPYB_HARDWARE_READ_PLAN: assert ( self.parameters is not None ), "Nexus callback did not receive parameters before being asked to write!" @@ -96,5 +106,9 @@ def descriptor(self, doc): LOGGER.info("Initialising nexus writer") nexus_data = self.parameters.get_nexus_info(1) LOGGER.info(f"Nexus data: {nexus_data}") - self.nexus_writer = NexusWriter(self.parameters, **nexus_data) + self.nexus_writer = NexusWriter( + self.parameters, + create_goniometer_func=self.create_goniometer_func, + **nexus_data, + ) self.nexus_writer.create_nexus_file() diff --git a/src/hyperion/external_interaction/callbacks/xray_centre/tests/test_nexus_handler.py b/src/hyperion/external_interaction/callbacks/xray_centre/tests/test_nexus_handler.py index 376ee401d..4f6380dc1 100644 --- a/src/hyperion/external_interaction/callbacks/xray_centre/tests/test_nexus_handler.py +++ b/src/hyperion/external_interaction/callbacks/xray_centre/tests/test_nexus_handler.py @@ -5,6 +5,7 @@ from hyperion.external_interaction.callbacks.xray_centre.nexus_callback import ( GridscanNexusFileCallback, ) +from hyperion.external_interaction.nexus.nexus_utils import create_i03_goniometer_axes from hyperion.parameters.constants import ISPYB_HARDWARE_READ_PLAN from hyperion.parameters.external_parameters import from_file as default_raw_params from hyperion.parameters.plan_specific.gridscan_internal_params import ( @@ -36,7 +37,7 @@ def test_writers_not_setup_on_plan_start_doc( nexus_writer: MagicMock, dummy_params: GridscanInternalParameters, ): - nexus_handler = GridscanNexusFileCallback() + nexus_handler = GridscanNexusFileCallback(create_i03_goniometer_axes) nexus_writer.assert_not_called() nexus_handler.start( { @@ -51,7 +52,7 @@ def test_writers_dont_create_on_init_but_do_on_ispyb_event( nexus_writer: MagicMock, dummy_params: GridscanInternalParameters, ): - nexus_handler = GridscanNexusFileCallback() + nexus_handler = GridscanNexusFileCallback(create_i03_goniometer_axes) assert nexus_handler.nexus_writer_1 is None assert nexus_handler.nexus_writer_2 is None @@ -85,7 +86,7 @@ def test_writers_do_create_one_file_each_on_start_doc_for_run_gridscan( ): nexus_writer.side_effect = [MagicMock(), MagicMock()] - nexus_handler = GridscanNexusFileCallback() + nexus_handler = GridscanNexusFileCallback(create_i03_goniometer_axes) nexus_handler.start( { "subplan_name": "run_gridscan_move_and_tidy", @@ -113,7 +114,7 @@ def test_writers_do_create_one_file_each_on_start_doc_for_run_gridscan( def test_sensible_error_if_writing_triggered_before_params_received( nexus_writer: MagicMock, dummy_params ): - nexus_handler = GridscanNexusFileCallback() + nexus_handler = GridscanNexusFileCallback(create_i03_goniometer_axes) with pytest.raises(AssertionError) as excinfo: nexus_handler.descriptor( { diff --git a/src/hyperion/external_interaction/nexus/nexus_utils.py b/src/hyperion/external_interaction/nexus/nexus_utils.py index 278cda946..3b9e71472 100644 --- a/src/hyperion/external_interaction/nexus/nexus_utils.py +++ b/src/hyperion/external_interaction/nexus/nexus_utils.py @@ -4,14 +4,11 @@ from datetime import datetime, timedelta from dodal.devices.detector import DetectorParams -from dodal.utils import get_beamline_name from nexgen.nxs_utils import Attenuator, Axis, Beam, Detector, EigerDetector, Goniometer from nexgen.nxs_utils.Axes import TransformationType from hyperion.external_interaction.ispyb.ispyb_dataclass import IspybParams -BL = get_beamline_name(None) - def create_i03_goniometer_axes( omega_start: float, @@ -72,7 +69,7 @@ def create_vmxm_goniometer_axes( omega_start: float, scan_points: dict | None, x_y_z_increments: tuple[float, float, float] = (0.0, 0.0, 0.0), -): +) -> Goniometer: """Returns a Nexgen 'Goniometer' object with the dependency chain of I03's Smargon goniometer. If scan points is provided these values will be used in preference to those from the params object. @@ -117,16 +114,6 @@ def create_vmxm_goniometer_axes( return Goniometer(gonio_axes, scan_points) -if BL == "i03": - create_goniometer_axes = create_i03_goniometer_axes -elif BL == "i02-1": - create_goniometer_axes = create_vmxm_goniometer_axes -else: - - def create_goniometer_axes(*a, **k): - raise ValueError("Cannot create goniometer axes on unknown beamline") - - def get_start_and_predicted_end_time(time_expected: float) -> tuple[str, str]: time_format = r"%Y-%m-%dT%H:%M:%SZ" start = datetime.utcfromtimestamp(time.time()) diff --git a/src/hyperion/external_interaction/nexus/write_nexus.py b/src/hyperion/external_interaction/nexus/write_nexus.py index 8c029aced..7c330bcfe 100644 --- a/src/hyperion/external_interaction/nexus/write_nexus.py +++ b/src/hyperion/external_interaction/nexus/write_nexus.py @@ -6,6 +6,7 @@ import math from pathlib import Path +from typing import Protocol from nexgen.nxs_utils import Detector, Goniometer, Source from nexgen.nxs_write.NXmxWriter import NXmxFileWriter @@ -13,18 +14,30 @@ from hyperion.external_interaction.nexus.nexus_utils import ( create_beam_and_attenuator_parameters, create_detector_parameters, - create_goniometer_axes, get_start_and_predicted_end_time, ) from hyperion.parameters.internal_parameters import InternalParameters +class CreateGoniometerProtocol(Protocol): + @staticmethod + def __call__( + omega_start: float, + scan_points: dict | None, + x_y_z_increments: tuple[float, float, float] = (0.0, 0.0, 0.0), + chi: float = 0.0, + phi: float = 0.0, + ) -> Goniometer: + ... + + class NexusWriter: def __init__( self, parameters: InternalParameters, scan_points: dict, data_shape: tuple[int, int, int], + create_goniometer_func: CreateGoniometerProtocol, omega_start: float | None = None, run_number: int | None = None, vds_start_index: int = 0, @@ -66,12 +79,11 @@ def __init__( self.master_file: Path = ( self.directory / f"{self.filename}_{self.run_number}_master.h5" ) - try: chi = parameters.experiment_params.chi_start except Exception: chi = 0.0 - self.goniometer: Goniometer = create_goniometer_axes( + self.goniometer: Goniometer = create_goniometer_func( self.omega_start, self.scan_points, chi=chi ) diff --git a/src/hyperion/external_interaction/unit_tests/test_write_nexus.py b/src/hyperion/external_interaction/unit_tests/test_write_nexus.py index ffc3d22b9..50c3eb31b 100644 --- a/src/hyperion/external_interaction/unit_tests/test_write_nexus.py +++ b/src/hyperion/external_interaction/unit_tests/test_write_nexus.py @@ -7,6 +7,7 @@ import pytest from dodal.devices.fast_grid_scan_common import GridAxis, GridScanParams +from hyperion.external_interaction.nexus.nexus_utils import create_i03_goniometer_axes from hyperion.external_interaction.nexus.write_nexus import NexusWriter from hyperion.parameters.plan_specific.gridscan_internal_params import ( GridscanInternalParameters, @@ -26,10 +27,15 @@ def assert_end_data_correct(nexus_writer: NexusWriter): @pytest.fixture def dummy_nexus_writers(test_fgs_params: GridscanInternalParameters): nexus_info_1 = test_fgs_params.get_nexus_info(1) - nexus_writer_1 = NexusWriter(test_fgs_params, **nexus_info_1) + nexus_writer_1 = NexusWriter( + test_fgs_params, + create_goniometer_func=create_i03_goniometer_axes, + **nexus_info_1, + ) nexus_info_2 = test_fgs_params.get_nexus_info(2) nexus_writer_2 = NexusWriter( test_fgs_params, + create_goniometer_func=create_i03_goniometer_axes, **nexus_info_2, vds_start_index=nexus_info_1["data_shape"][0], ) @@ -49,8 +55,16 @@ def create_nexus_writers_with_many_images(parameters: GridscanInternalParameters parameters.experiment_params.y_steps = y parameters.experiment_params.z_steps = z parameters.hyperion_params.detector_params.num_triggers = x * y + x * z - nexus_writer_1 = NexusWriter(parameters, **parameters.get_nexus_info(1)) - nexus_writer_2 = NexusWriter(parameters, **parameters.get_nexus_info(2)) + nexus_writer_1 = NexusWriter( + parameters, + create_goniometer_func=create_i03_goniometer_axes, + **parameters.get_nexus_info(1), + ) + nexus_writer_2 = NexusWriter( + parameters, + create_goniometer_func=create_i03_goniometer_axes, + **parameters.get_nexus_info(2), + ) yield nexus_writer_1, nexus_writer_2 @@ -71,7 +85,11 @@ def dummy_nexus_writers_with_more_images(test_fgs_params: GridscanInternalParame @pytest.fixture def single_dummy_file(test_fgs_params: GridscanInternalParameters): - nexus_writer = NexusWriter(test_fgs_params, **test_fgs_params.get_nexus_info(1)) + nexus_writer = NexusWriter( + test_fgs_params, + create_goniometer_func=create_i03_goniometer_axes, + **test_fgs_params.get_nexus_info(1), + ) yield nexus_writer for file in [nexus_writer.nexus_file, nexus_writer.master_file]: if os.path.isfile(file): diff --git a/src/hyperion/log.py b/src/hyperion/log.py index 645514278..1c2ebf47a 100755 --- a/src/hyperion/log.py +++ b/src/hyperion/log.py @@ -36,7 +36,12 @@ def set_up_logging_handlers( Mode defaults to production and can be switched to dev with the --dev flag on run. """ - handlers = setup_dodal_logging(logging_level, dev_mode, _get_logging_file_path(), file_handler_logging_level="DEBUG") + handlers = setup_dodal_logging( + logging_level, + dev_mode, + _get_logging_file_path(), + file_handler_logging_level="DEBUG", + ) dodal_logger.addFilter(dc_group_id_filter) LOGGER.addFilter(dc_group_id_filter) diff --git a/test_parameter_defaults.json b/test_parameter_defaults.json index f636be52d..1b9f541d4 100644 --- a/test_parameter_defaults.json +++ b/test_parameter_defaults.json @@ -4,6 +4,7 @@ "zocalo_environment": "dev_artemis", "beamline": "BL03S", "insertion_prefix": "SR03S", + "detector": "EIGER2_X_16M", "experiment_type": "flyscan_xray_centre", "detector_params": { "current_energy_ev": 100, From f3d0bd890974f4ce55682a68f522844758677428 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Fri, 17 Nov 2023 09:31:02 +0000 Subject: [PATCH 6/7] Set omega as part of plan --- src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py index b079ba5da..2e72dcd39 100755 --- a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -9,6 +9,7 @@ from blueapi.core import BlueskyContext, MsgGenerator from bluesky.run_engine import RunEngine from bluesky.utils import ProgressBarManager +from dodal.beamlines.vmxm.vmxm_sample_motors import VmxmSampleMotors from dodal.devices.backlight import VmxmBacklight from dodal.devices.eiger import EigerDetector from dodal.devices.fast_grid_scan_2d import FastGridScan2D @@ -45,6 +46,7 @@ class VmxmFlyScanXRayCentreComposite: backlight: VmxmBacklight eiger: EigerDetector fast_grid_scan: FastGridScan2D + sample_motors: VmxmSampleMotors synchrotron: Synchrotron zebra: Zebra @@ -93,6 +95,7 @@ def run_gridscan( # TODO: Check topup gate yield from set_flyscan_params(fgs_motors, parameters.experiment_params) + yield from bps.mv(fgs_composite.sample_motors.omega, parameters.get_omega_start(1)) yield from wait_for_gridscan_valid(fgs_motors) @bpp.set_run_key_decorator("do_fgs") From 35ee736c34742df5a55882a3ea05c57110cdeaac Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Fri, 24 Nov 2023 10:27:26 +0000 Subject: [PATCH 7/7] Changes from VMXm beamtime --- .../experiment_plans/experiment_registry.py | 65 ++++++++++--------- .../vmxm_flyscan_xray_centre_plan.py | 9 ++- .../callbacks/ispyb_callback_base.py | 6 +- .../external_interaction/nexus/nexus_utils.py | 8 ++- 4 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/hyperion/experiment_plans/experiment_registry.py b/src/hyperion/experiment_plans/experiment_registry.py index 2973a3dc7..ecb7ac986 100644 --- a/src/hyperion/experiment_plans/experiment_registry.py +++ b/src/hyperion/experiment_plans/experiment_registry.py @@ -53,42 +53,47 @@ def do_nothing(): EXPERIMENT_TYPES = Union[GridScanParams, RotationScanParams, SteppedGridScanParams] PLAN_REGISTRY: dict[str, dict[str, Callable]] = { - "flyscan_xray_centre": { - "setup": flyscan_xray_centre_plan.create_devices, - "internal_param_type": GridscanInternalParameters, - "experiment_param_type": GridScanParams, - "callback_collection_type": XrayCentreCallbackCollection, - }, + # FIXME - HACK - plans commented out here because VMXm complains about no + # aperture_scatterguard which is needed for some plans. + # Need a way to either mark plans as being relevant only on some beamlines, or + # generally allow hyperion to *start* even if some plans reference nonexistent + # devices (and then fail at runtime if that plan is actually used). + # "flyscan_xray_centre": { + # "setup": flyscan_xray_centre_plan.create_devices, + # "internal_param_type": GridscanInternalParameters, + # "experiment_param_type": GridScanParams, + # "callback_collection_type": XrayCentreCallbackCollection, + # }, "vmxm_flyscan_xray_centre": { "setup": vmxm_flyscan_xray_centre_plan.create_devices, "internal_param_type": GridscanInternalParameters, "experiment_param_type": GridScanParams, "callback_collection_type": VmxmFastGridScanCallbackCollection, }, - "grid_detect_then_xray_centre": { - "setup": grid_detect_then_xray_centre_plan.create_devices, - "internal_param_type": GridScanWithEdgeDetectInternalParameters, - "experiment_param_type": GridScanWithEdgeDetectParams, - "callback_collection_type": NullPlanCallbackCollection, - }, - "rotation_scan": { - "setup": rotation_scan_plan.create_devices, - "internal_param_type": RotationInternalParameters, - "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, - "internal_param_type": SteppedGridScanInternalParameters, - "experiment_param_type": SteppedGridScanParams, - "callback_collection_type": NullPlanCallbackCollection, - }, + # "grid_detect_then_xray_centre": { + # "setup": grid_detect_then_xray_centre_plan.create_devices, + # "internal_param_type": GridScanWithEdgeDetectInternalParameters, + # "experiment_param_type": GridScanWithEdgeDetectParams, + # "callback_collection_type": NullPlanCallbackCollection, + # }, + # "rotation_scan": { + # "setup": rotation_scan_plan.create_devices, + # "internal_param_type": RotationInternalParameters, + # "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, + # "internal_param_type": SteppedGridScanInternalParameters, + # "experiment_param_type": SteppedGridScanParams, + # "callback_collection_type": NullPlanCallbackCollection, + # }, } EXPERIMENT_NAMES = list(PLAN_REGISTRY.keys()) EXPERIMENT_TYPE_LIST = [p["experiment_param_type"] for p in PLAN_REGISTRY.values()] diff --git a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py index 2e72dcd39..8d33e0335 100755 --- a/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py +++ b/src/hyperion/experiment_plans/vmxm_flyscan_xray_centre_plan.py @@ -9,7 +9,6 @@ from blueapi.core import BlueskyContext, MsgGenerator from bluesky.run_engine import RunEngine from bluesky.utils import ProgressBarManager -from dodal.beamlines.vmxm.vmxm_sample_motors import VmxmSampleMotors from dodal.devices.backlight import VmxmBacklight from dodal.devices.eiger import EigerDetector from dodal.devices.fast_grid_scan_2d import FastGridScan2D @@ -18,6 +17,7 @@ ) from dodal.devices.synchrotron import Synchrotron from dodal.devices.vmxm.vmxm_attenuator import VmxmAttenuator +from dodal.devices.vmxm.vmxm_sample_motors import VmxmSampleMotors from dodal.devices.zebra import ( Zebra, ) @@ -89,6 +89,11 @@ def run_gridscan( ): fgs_motors = fgs_composite.fast_grid_scan + yield from bps.mv( + fgs_composite.attenuator, + parameters.hyperion_params.ispyb_params.transmission_fraction, + ) + yield from bps.create(name=ISPYB_HARDWARE_READ_PLAN) yield from bps.read(fgs_composite.synchrotron.machine_status.synchrotron_mode) yield from bps.save() @@ -118,8 +123,6 @@ def do_fgs(): with TRACER.start_span("do_fgs"): yield from do_fgs() - yield from bps.abs_set(fgs_motors.z_steps, 0, wait=False) - def setup_vmxm_zebra_for_gridscan( zebra: Zebra, group="setup_zebra_for_gridscan", wait=False diff --git a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py index 114dc2d11..134247f75 100644 --- a/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py +++ b/src/hyperion/external_interaction/callbacks/ispyb_callback_base.py @@ -78,9 +78,9 @@ def event(self, doc: dict): "flux_flux_reading", 0.0 ) - LOGGER.info("Creating ispyb entry.") - self.ispyb_ids = self.ispyb.begin_deposition() - LOGGER.info(f"Recieved ISPYB IDs: {self.ispyb_ids}") + LOGGER.info("Creating ispyb entry.") + self.ispyb_ids = self.ispyb.begin_deposition() + LOGGER.info(f"Recieved ISPYB IDs: {self.ispyb_ids}") def stop(self, doc: dict): """Subclasses must check that they are recieving a stop document for the correct diff --git a/src/hyperion/external_interaction/nexus/nexus_utils.py b/src/hyperion/external_interaction/nexus/nexus_utils.py index 3b9e71472..fdb3bce8d 100644 --- a/src/hyperion/external_interaction/nexus/nexus_utils.py +++ b/src/hyperion/external_interaction/nexus/nexus_utils.py @@ -69,6 +69,8 @@ def create_vmxm_goniometer_axes( omega_start: float, scan_points: dict | None, x_y_z_increments: tuple[float, float, float] = (0.0, 0.0, 0.0), + chi: float = 0, + phi: float = 0, ) -> Goniometer: """Returns a Nexgen 'Goniometer' object with the dependency chain of I03's Smargon goniometer. If scan points is provided these values will be used in preference to @@ -85,7 +87,7 @@ def create_vmxm_goniometer_axes( is provided. """ gonio_axes = [ - Axis("omega", ".", TransformationType.ROTATION, (-1.0, 0.0, 0.0), omega_start), + Axis("omega", ".", TransformationType.ROTATION, (0.0, 1.0, 0.0), omega_start), Axis( name="sam_z", depends="omega", @@ -132,8 +134,10 @@ def create_detector_parameters(detector_params: DetectorParams) -> Detector: """ detector_pixels = detector_params.get_detector_size_pizels() + # FIXME - Make generic enough for both VMXm and i03 (and other beamlines) + # get these from detector_params probably. eiger_params = EigerDetector( - "Eiger 16M", (detector_pixels.height, detector_pixels.width), "Si", 46051, 0 + "Eiger 9M", (detector_pixels.height, detector_pixels.width), "CdTe", 2418770, 0 ) detector_axes = [