mx-bluesky 0.0.2__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/__main__.py +1 -2
- mx_bluesky/_version.py +14 -2
- mx_bluesky/beamlines/i04/__init__.py +3 -0
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +45 -0
- mx_bluesky/beamlines/i04/thawing_plan.py +85 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +49 -0
- mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +12 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/dcid.py +53 -41
- mx_bluesky/{I24 → beamlines/i24}/serial/extruder/EX-gui-edm/DetStage.edl +3 -4
- mx_bluesky/{I24 → beamlines/i24}/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +28 -32
- mx_bluesky/{I24 → beamlines/i24}/serial/extruder/EX-gui-edm/microdrop_alignment.edl +0 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +516 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +3 -4
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -4
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +273 -223
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +12 -13
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/microdrop_alignment.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/nudgechip.edl +0 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +273 -143
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/short1-laser.png +0 -0
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/short2-laser.png +0 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/ft_utils.py +24 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +808 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +377 -416
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +34 -40
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +328 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/i24ssx_moveonclick.py +66 -48
- mx_bluesky/{I24 → beamlines/i24}/serial/log.py +66 -19
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +15 -0
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +47 -0
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +103 -0
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +9 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/parameters/fixed_target/cs/motor_direction.txt +1 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/minichip-oxford.pvar +1 -1
- mx_bluesky/beamlines/i24/serial/parameters/utils.py +42 -0
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +19 -0
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +22 -0
- mx_bluesky/beamlines/i24/serial/run_serial.py +36 -0
- mx_bluesky/{I24 → beamlines/i24}/serial/set_visit_directory.sh +6 -1
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/pv.py +1 -62
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/pv_abstract.py +6 -7
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/setup_beamline.py +90 -269
- mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/setup_detector.py +47 -40
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +459 -0
- mx_bluesky/beamlines/i24/serial/start_blueapi.sh +28 -0
- mx_bluesky/beamlines/i24/serial/write_nexus.py +105 -0
- mx_bluesky/example.py +4 -4
- mx_bluesky/hyperion/__init__.py +1 -0
- mx_bluesky/hyperion/__main__.py +374 -0
- mx_bluesky/hyperion/device_setup_plans/__init__.py +0 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +134 -0
- mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +110 -0
- mx_bluesky/hyperion/device_setup_plans/position_detector.py +16 -0
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +60 -0
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +87 -0
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +210 -0
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +214 -0
- mx_bluesky/hyperion/device_setup_plans/smargon.py +25 -0
- mx_bluesky/hyperion/device_setup_plans/utils.py +44 -0
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +93 -0
- mx_bluesky/hyperion/exceptions.py +47 -0
- mx_bluesky/hyperion/experiment_plans/__init__.py +30 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +84 -0
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +528 -0
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +209 -0
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +173 -0
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +81 -0
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +463 -0
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +119 -0
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +164 -0
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +322 -0
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +436 -0
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +68 -0
- mx_bluesky/hyperion/external_interaction/__init__.py +9 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +10 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +148 -0
- mx_bluesky/hyperion/external_interaction/callbacks/aperture_change_callback.py +22 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +70 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +203 -0
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +20 -0
- mx_bluesky/hyperion/external_interaction/callbacks/logging_callback.py +29 -0
- mx_bluesky/hyperion/external_interaction/callbacks/plan_reactive_callback.py +101 -0
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +174 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +17 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +102 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +269 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_mapping.py +53 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +95 -0
- mx_bluesky/hyperion/external_interaction/callbacks/zocalo_callback.py +92 -0
- mx_bluesky/hyperion/external_interaction/config_server.py +35 -0
- mx_bluesky/hyperion/external_interaction/exceptions.py +13 -0
- mx_bluesky/hyperion/external_interaction/ispyb/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/ispyb/data_model.py +95 -0
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +125 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +276 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +29 -0
- mx_bluesky/hyperion/external_interaction/nexus/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +148 -0
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +114 -0
- mx_bluesky/hyperion/log.py +99 -0
- mx_bluesky/hyperion/parameters/__init__.py +2 -0
- mx_bluesky/hyperion/parameters/cli.py +68 -0
- mx_bluesky/hyperion/parameters/components.py +253 -0
- mx_bluesky/hyperion/parameters/constants.py +158 -0
- mx_bluesky/hyperion/parameters/gridscan.py +216 -0
- mx_bluesky/hyperion/parameters/rotation.py +160 -0
- mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +964 -0
- mx_bluesky/hyperion/tracing.py +28 -0
- mx_bluesky/hyperion/utils/context.py +84 -0
- mx_bluesky/hyperion/utils/utils.py +25 -0
- mx_bluesky/hyperion/utils/validation.py +196 -0
- mx_bluesky/jupyter_example.ipynb +3 -2
- {mx_bluesky-0.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/METADATA +53 -32
- mx_bluesky-1.1.0.dist-info/RECORD +136 -0
- {mx_bluesky-0.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/WHEEL +1 -1
- mx_bluesky-1.1.0.dist-info/entry_points.txt +8 -0
- mx_bluesky/I24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +0 -476
- mx_bluesky/I24/serial/fixed_target/FT-gui-edm/ME14E-motors.edl +0 -1874
- mx_bluesky/I24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +0 -706
- mx_bluesky/I24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +0 -463
- mx_bluesky/I24/serial/parameters/__init__.py +0 -5
- mx_bluesky/I24/serial/parameters/constants.py +0 -39
- mx_bluesky/I24/serial/parameters/fixed_target/cs/cs_maker.json +0 -9
- mx_bluesky/I24/serial/parameters/fixed_target/cs/fiducial_1.txt +0 -4
- mx_bluesky/I24/serial/parameters/fixed_target/cs/fiducial_2.txt +0 -4
- mx_bluesky/I24/serial/parameters/fixed_target/litemaps/currentchip.map +0 -81
- mx_bluesky/I24/serial/parameters/fixed_target/parameters.txt +0 -13
- mx_bluesky/I24/serial/run_serial.py +0 -52
- mx_bluesky/I24/serial/write_nexus.py +0 -113
- mx_bluesky-0.0.2.dist-info/RECORD +0 -58
- mx_bluesky-0.0.2.dist-info/entry_points.txt +0 -4
- /mx_bluesky/{I24 → beamlines}/__init__.py +0 -0
- /mx_bluesky/{I24/serial → beamlines/i24}/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/extruder/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/fixed_target/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/oxford.pvar +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/run_ssx.sh +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/__init__.py +0 -0
- /mx_bluesky/{I24 → beamlines/i24}/serial/setup_beamline/ca.py +0 -0
- {mx_bluesky-0.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-0.0.2.dist-info → mx_bluesky-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from pydantic.dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from mx_bluesky._version import version
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class HyperionArgs:
|
|
10
|
+
dev_mode: bool = False
|
|
11
|
+
use_external_callbacks: bool = False
|
|
12
|
+
verbose_event_logging: bool = False
|
|
13
|
+
skip_startup_connection: bool = False
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _add_callback_relevant_args(parser: argparse.ArgumentParser) -> None:
|
|
17
|
+
"""adds arguments relevant to hyperion-callbacks."""
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--dev",
|
|
20
|
+
action="store_true",
|
|
21
|
+
help="Use dev options, such as local graylog instances and S03",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_callback_dev_mode_arg() -> bool:
|
|
26
|
+
"""Returns the bool representing the 'dev_mode' argument."""
|
|
27
|
+
parser = argparse.ArgumentParser()
|
|
28
|
+
_add_callback_relevant_args(parser)
|
|
29
|
+
args = parser.parse_args()
|
|
30
|
+
return args.dev
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def parse_cli_args() -> HyperionArgs:
|
|
34
|
+
"""Parses all arguments relevant to hyperion. Returns an HyperionArgs dataclass with
|
|
35
|
+
the fields: (verbose_event_logging: bool,
|
|
36
|
+
dev_mode: bool,
|
|
37
|
+
skip_startup_connection: bool,
|
|
38
|
+
external_callbacks: bool)"""
|
|
39
|
+
parser = argparse.ArgumentParser()
|
|
40
|
+
_add_callback_relevant_args(parser)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--verbose-event-logging",
|
|
43
|
+
action="store_true",
|
|
44
|
+
help="Log all bluesky event documents to graylog",
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--skip-startup-connection",
|
|
48
|
+
action="store_true",
|
|
49
|
+
help="Skip connecting to EPICS PVs on startup",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--external-callbacks",
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="Run the external hyperion-callbacks service and publish events over ZMQ",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--version",
|
|
58
|
+
help="Print hyperion version string",
|
|
59
|
+
action="version",
|
|
60
|
+
version=version,
|
|
61
|
+
)
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
return HyperionArgs(
|
|
64
|
+
verbose_event_logging=args.verbose_event_logging or False,
|
|
65
|
+
dev_mode=args.dev or False,
|
|
66
|
+
skip_startup_connection=args.skip_startup_connection or False,
|
|
67
|
+
use_external_callbacks=args.external_callbacks or False,
|
|
68
|
+
)
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import SupportsInt, TypeVar
|
|
10
|
+
|
|
11
|
+
from dodal.devices.aperturescatterguard import ApertureValue
|
|
12
|
+
from dodal.devices.detector import (
|
|
13
|
+
DetectorParams,
|
|
14
|
+
TriggerMode,
|
|
15
|
+
)
|
|
16
|
+
from pydantic import (
|
|
17
|
+
BaseModel,
|
|
18
|
+
ConfigDict,
|
|
19
|
+
Field,
|
|
20
|
+
field_serializer,
|
|
21
|
+
field_validator,
|
|
22
|
+
model_validator,
|
|
23
|
+
)
|
|
24
|
+
from scanspec.core import AxesPoints
|
|
25
|
+
from semver import Version
|
|
26
|
+
|
|
27
|
+
from mx_bluesky.hyperion.external_interaction.config_server import FeatureFlags
|
|
28
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
29
|
+
|
|
30
|
+
T = TypeVar("T")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ParameterVersion(Version):
|
|
34
|
+
@classmethod
|
|
35
|
+
def _parse(cls, version):
|
|
36
|
+
if isinstance(version, cls):
|
|
37
|
+
return version
|
|
38
|
+
return cls.parse(version)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
PARAMETER_VERSION = ParameterVersion.parse("5.1.0")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RotationAxis(StrEnum):
|
|
45
|
+
OMEGA = "omega"
|
|
46
|
+
PHI = "phi"
|
|
47
|
+
CHI = "chi"
|
|
48
|
+
KAPPA = "kappa"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class XyzAxis(StrEnum):
|
|
52
|
+
X = "sam_x"
|
|
53
|
+
Y = "sam_y"
|
|
54
|
+
Z = "sam_z"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class IspybExperimentType(StrEnum):
|
|
58
|
+
# Enum values from ispyb column data type
|
|
59
|
+
SAD = "SAD" # at or slightly above the peak
|
|
60
|
+
SAD_INVERSE_BEAM = "SAD - Inverse Beam"
|
|
61
|
+
OSC = "OSC" # "native" (in the absence of a heavy atom)
|
|
62
|
+
COLLECT_MULTIWEDGE = (
|
|
63
|
+
"Collect - Multiwedge" # "poorly determined" ~ EDNA complex strategy???
|
|
64
|
+
)
|
|
65
|
+
MAD = "MAD"
|
|
66
|
+
HELICAL = "Helical"
|
|
67
|
+
MULTI_POSITIONAL = "Multi-positional"
|
|
68
|
+
MESH = "Mesh"
|
|
69
|
+
BURN = "Burn"
|
|
70
|
+
MAD_INVERSE_BEAM = "MAD - Inverse Beam"
|
|
71
|
+
CHARACTERIZATION = "Characterization"
|
|
72
|
+
DEHYDRATION = "Dehydration"
|
|
73
|
+
TOMO = "tomo"
|
|
74
|
+
EXPERIMENT = "experiment"
|
|
75
|
+
EM = "EM"
|
|
76
|
+
PDF = "PDF"
|
|
77
|
+
PDF_BRAGG = "PDF+Bragg"
|
|
78
|
+
BRAGG = "Bragg"
|
|
79
|
+
SINGLE_PARTICLE = "single particle"
|
|
80
|
+
SERIAL_FIXED = "Serial Fixed"
|
|
81
|
+
SERIAL_JET = "Serial Jet"
|
|
82
|
+
STANDARD = "Standard" # Routine structure determination experiment
|
|
83
|
+
TIME_RESOLVED = "Time Resolved" # Investigate the change of a system over time
|
|
84
|
+
DLS_ANVIL_HP = "Diamond Anvil High Pressure" # HP sample environment pressure cell
|
|
85
|
+
CUSTOM = "Custom" # Special or non-standard data collection
|
|
86
|
+
XRF_MAP = "XRF map"
|
|
87
|
+
ENERGY_SCAN = "Energy scan"
|
|
88
|
+
XRF_SPECTRUM = "XRF spectrum"
|
|
89
|
+
XRF_MAP_XAS = "XRF map xas"
|
|
90
|
+
MESH_3D = "Mesh3D"
|
|
91
|
+
SCREENING = "Screening"
|
|
92
|
+
STILL = "Still"
|
|
93
|
+
SSX_CHIP = "SSX-Chip"
|
|
94
|
+
SSX_JET = "SSX-Jet"
|
|
95
|
+
|
|
96
|
+
# Aliases for historic hyperion experiment type mapping
|
|
97
|
+
ROTATION = "SAD"
|
|
98
|
+
GRIDSCAN_2D = "mesh"
|
|
99
|
+
GRIDSCAN_3D = "Mesh3D"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class HyperionParameters(BaseModel):
|
|
103
|
+
model_config = ConfigDict(
|
|
104
|
+
arbitrary_types_allowed=True,
|
|
105
|
+
extra="allow",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def __hash__(self) -> int:
|
|
109
|
+
return self.json().__hash__()
|
|
110
|
+
|
|
111
|
+
features: FeatureFlags = Field(default=FeatureFlags())
|
|
112
|
+
parameter_model_version: ParameterVersion
|
|
113
|
+
|
|
114
|
+
@field_serializer("parameter_model_version")
|
|
115
|
+
def serialize_parameter_version(self, version: ParameterVersion):
|
|
116
|
+
return str(version)
|
|
117
|
+
|
|
118
|
+
@field_validator("parameter_model_version", mode="before")
|
|
119
|
+
@classmethod
|
|
120
|
+
def _validate_version(cls, version_str: str):
|
|
121
|
+
version = ParameterVersion.parse(version_str)
|
|
122
|
+
assert (
|
|
123
|
+
version >= ParameterVersion(major=PARAMETER_VERSION.major)
|
|
124
|
+
), f"Parameter version too old! This version of hyperion uses {PARAMETER_VERSION}"
|
|
125
|
+
assert (
|
|
126
|
+
version <= ParameterVersion(major=PARAMETER_VERSION.major + 1)
|
|
127
|
+
), f"Parameter version too new! This version of hyperion uses {PARAMETER_VERSION}"
|
|
128
|
+
return version
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def from_json(cls, input: str | None):
|
|
132
|
+
assert input is not None
|
|
133
|
+
return cls(**json.loads(input))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class WithSnapshot(BaseModel):
|
|
137
|
+
snapshot_directory: Path
|
|
138
|
+
snapshot_omegas_deg: list[float] | None = None
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def take_snapshots(self) -> bool:
|
|
142
|
+
return bool(self.snapshot_omegas_deg)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class DiffractionExperiment(HyperionParameters, WithSnapshot):
|
|
146
|
+
"""For all experiments which use beam"""
|
|
147
|
+
|
|
148
|
+
visit: str = Field(min_length=1)
|
|
149
|
+
file_name: str
|
|
150
|
+
exposure_time_s: float = Field(gt=0)
|
|
151
|
+
comment: str = Field(default="")
|
|
152
|
+
beamline: str = Field(default=CONST.I03.BEAMLINE, pattern=r"BL\d{2}[BIJS]")
|
|
153
|
+
insertion_prefix: str = Field(
|
|
154
|
+
default=CONST.I03.INSERTION_PREFIX, pattern=r"SR\d{2}[BIJS]"
|
|
155
|
+
)
|
|
156
|
+
det_dist_to_beam_converter_path: str = Field(
|
|
157
|
+
default=CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
|
|
158
|
+
)
|
|
159
|
+
zocalo_environment: str = Field(default=CONST.ZOCALO_ENV)
|
|
160
|
+
trigger_mode: TriggerMode = Field(default=TriggerMode.FREE_RUN)
|
|
161
|
+
detector_distance_mm: float | None = Field(default=None, gt=0)
|
|
162
|
+
demand_energy_ev: float | None = Field(default=None, gt=0)
|
|
163
|
+
run_number: int | None = Field(default=None, ge=0)
|
|
164
|
+
selected_aperture: ApertureValue | None = Field(default=None)
|
|
165
|
+
transmission_frac: float = Field(default=0.1)
|
|
166
|
+
ispyb_experiment_type: IspybExperimentType
|
|
167
|
+
storage_directory: str
|
|
168
|
+
|
|
169
|
+
@model_validator(mode="before")
|
|
170
|
+
@classmethod
|
|
171
|
+
def validate_snapshot_directory(cls, values):
|
|
172
|
+
snapshot_dir = values.get(
|
|
173
|
+
"snapshot_directory", Path(values["storage_directory"], "snapshots")
|
|
174
|
+
)
|
|
175
|
+
values["snapshot_directory"] = (
|
|
176
|
+
snapshot_dir if isinstance(snapshot_dir, Path) else Path(snapshot_dir)
|
|
177
|
+
)
|
|
178
|
+
return values
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def visit_directory(self) -> Path:
|
|
182
|
+
return (
|
|
183
|
+
Path(CONST.I03.BASE_DATA_DIR) / str(datetime.date.today().year) / self.visit
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def num_images(self) -> int:
|
|
188
|
+
return 0
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
@abstractmethod
|
|
192
|
+
def detector_params(self) -> DetectorParams: ...
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class WithScan(BaseModel):
|
|
196
|
+
"""For experiments where the scan is known"""
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
@abstractmethod
|
|
200
|
+
def scan_points(self) -> AxesPoints: ...
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
@abstractmethod
|
|
204
|
+
def num_images(self) -> int: ...
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class SplitScan(BaseModel):
|
|
208
|
+
@property
|
|
209
|
+
@abstractmethod
|
|
210
|
+
def scan_indices(self) -> Sequence[SupportsInt]:
|
|
211
|
+
"""Should return the first index of each scan (i.e. for each nexus file)"""
|
|
212
|
+
...
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class WithSample(BaseModel):
|
|
216
|
+
sample_id: int
|
|
217
|
+
sample_puck: int | None = None
|
|
218
|
+
sample_pin: int | None = None
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class DiffractionExperimentWithSample(DiffractionExperiment, WithSample): ...
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class WithOavCentring(BaseModel):
|
|
225
|
+
oav_centring_file: str = Field(default=CONST.I03.OAV_CENTRING_FILE)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class OptionalXyzStarts(BaseModel):
|
|
229
|
+
x_start_um: float | None = None
|
|
230
|
+
y_start_um: float | None = None
|
|
231
|
+
z_start_um: float | None = None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class XyzStarts(BaseModel):
|
|
235
|
+
x_start_um: float
|
|
236
|
+
y_start_um: float
|
|
237
|
+
z_start_um: float
|
|
238
|
+
|
|
239
|
+
def _start_for_axis(self, axis: XyzAxis) -> float:
|
|
240
|
+
match axis:
|
|
241
|
+
case XyzAxis.X:
|
|
242
|
+
return self.x_start_um
|
|
243
|
+
case XyzAxis.Y:
|
|
244
|
+
return self.y_start_um
|
|
245
|
+
case XyzAxis.Z:
|
|
246
|
+
return self.z_start_um
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class OptionalGonioAngleStarts(BaseModel):
|
|
250
|
+
omega_start_deg: float | None = None
|
|
251
|
+
phi_start_deg: float | None = None
|
|
252
|
+
chi_start_deg: float | None = None
|
|
253
|
+
kappa_start_deg: float | None = None
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
from dodal.devices.aperturescatterguard import ApertureValue
|
|
5
|
+
from dodal.devices.detector import EIGER2_X_16M_SIZE
|
|
6
|
+
from pydantic.dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
TEST_MODE = os.environ.get("HYPERION_TEST_MODE")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class SimConstants:
|
|
13
|
+
BEAMLINE = "BL03S"
|
|
14
|
+
INSERTION_PREFIX = "SR03S"
|
|
15
|
+
ZOCALO_ENV = "dev_artemis"
|
|
16
|
+
# this one is for unit tests
|
|
17
|
+
ISPYB_CONFIG = "tests/test_data/test_config.cfg"
|
|
18
|
+
# this one is for system tests
|
|
19
|
+
DEV_ISPYB_DATABASE_CFG = "/dls_sw/dasc/mariadb/credentials/ispyb-hyperion-dev.cfg"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class PlanNameConstants:
|
|
24
|
+
# Robot load subplan
|
|
25
|
+
ROBOT_LOAD = "robot_load"
|
|
26
|
+
# Gridscan
|
|
27
|
+
GRID_DETECT_AND_DO_GRIDSCAN = "grid_detect_and_do_gridscan"
|
|
28
|
+
GRID_DETECT_INNER = "grid_detect"
|
|
29
|
+
GRIDSCAN_OUTER = "run_gridscan_move_and_tidy"
|
|
30
|
+
GRIDSCAN_AND_MOVE = "run_gridscan_and_move"
|
|
31
|
+
GRIDSCAN_MAIN = "run_gridscan"
|
|
32
|
+
DO_FGS = "do_fgs"
|
|
33
|
+
# Rotation scan
|
|
34
|
+
ROTATION_MULTI = "multi_rotation_wrapper"
|
|
35
|
+
ROTATION_OUTER = "rotation_scan_with_cleanup"
|
|
36
|
+
ROTATION_MAIN = "rotation_scan_main"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class PlanGroupCheckpointConstants:
|
|
41
|
+
# For places to synchronise / stop and wait in plans, use as bluesky group names
|
|
42
|
+
GRID_READY_FOR_DC = "grid_ready_for_data_collection"
|
|
43
|
+
ROTATION_READY_FOR_DC = "rotation_ready_for_data_collection"
|
|
44
|
+
MOVE_GONIO_TO_START = "move_gonio_to_start"
|
|
45
|
+
READY_FOR_OAV = "ready_for_oav"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class DocDescriptorNames:
|
|
50
|
+
# Robot load event descriptor
|
|
51
|
+
ROBOT_LOAD = "robot_load"
|
|
52
|
+
# For callbacks to use
|
|
53
|
+
OAV_ROTATION_SNAPSHOT_TRIGGERED = "rotation_snapshot_triggered"
|
|
54
|
+
OAV_GRID_SNAPSHOT_TRIGGERED = "snapshot_to_ispyb"
|
|
55
|
+
HARDWARE_READ_PRE = "read_hardware_for_callbacks_pre_collection"
|
|
56
|
+
HARDWARE_READ_DURING = "read_hardware_for_callbacks_during_collection"
|
|
57
|
+
ZOCALO_HW_READ = "zocalo_read_hardware_plan"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class HardwareConstants:
|
|
62
|
+
OAV_REFRESH_DELAY = 0.3
|
|
63
|
+
PANDA_FGS_RUN_UP_DEFAULT = 0.17
|
|
64
|
+
CRYOJET_MARGIN_MM = 0.2
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class TriggerConstants:
|
|
69
|
+
ZOCALO = "trigger_zocalo_on"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class GridscanParamConstants:
|
|
74
|
+
WIDTH_UM = 600.0
|
|
75
|
+
EXPOSURE_TIME_S = 0.004
|
|
76
|
+
USE_ROI = True
|
|
77
|
+
BOX_WIDTH_UM = 20.0
|
|
78
|
+
OMEGA_1 = 0.0
|
|
79
|
+
OMEGA_2 = 90.0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class RotationParamConstants:
|
|
84
|
+
DEFAULT_APERTURE_POSITION = ApertureValue.LARGE
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class DetectorParamConstants:
|
|
89
|
+
BEAM_XY_LUT_PATH = (
|
|
90
|
+
"tests/test_data/test_det_dist_converter.txt"
|
|
91
|
+
if TEST_MODE
|
|
92
|
+
else "/dls_sw/i03/software/daq_configuration/lookup/DetDistToBeamXYConverter.txt"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass(frozen=True)
|
|
97
|
+
class ExperimentParamConstants:
|
|
98
|
+
DETECTOR = DetectorParamConstants()
|
|
99
|
+
GRIDSCAN = GridscanParamConstants()
|
|
100
|
+
ROTATION = RotationParamConstants()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
_test_oav_file = "tests/test_data/test_OAVCentring.json"
|
|
104
|
+
_live_oav_file = "/dls_sw/i03/software/daq_configuration/json/OAVCentring_hyperion.json"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class I03Constants:
|
|
109
|
+
BASE_DATA_DIR = "/tmp/dls/i03/data/" if TEST_MODE else "/dls/i03/data/"
|
|
110
|
+
BEAMLINE = "BL03S" if TEST_MODE else "BL03I"
|
|
111
|
+
DETECTOR = EIGER2_X_16M_SIZE
|
|
112
|
+
INSERTION_PREFIX = "SR03S" if TEST_MODE else "SR03I"
|
|
113
|
+
OAV_CENTRING_FILE = _test_oav_file if TEST_MODE else _live_oav_file
|
|
114
|
+
SHUTTER_TIME_S = 0.06
|
|
115
|
+
USE_PANDA_FOR_GRIDSCAN = False
|
|
116
|
+
USE_GPU_FOR_GRIDSCAN_ANALYSIS = False
|
|
117
|
+
THAWING_TIME = 20
|
|
118
|
+
USE_CPU_AND_GPU_ZOCALO = False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True)
|
|
122
|
+
class HyperionConstants:
|
|
123
|
+
HARDWARE = HardwareConstants()
|
|
124
|
+
I03 = I03Constants()
|
|
125
|
+
PARAM = ExperimentParamConstants()
|
|
126
|
+
PLAN = PlanNameConstants()
|
|
127
|
+
WAIT = PlanGroupCheckpointConstants()
|
|
128
|
+
SIM = SimConstants()
|
|
129
|
+
TRIGGER = TriggerConstants()
|
|
130
|
+
CALLBACK_0MQ_PROXY_PORTS = (5577, 5578)
|
|
131
|
+
DESCRIPTORS = DocDescriptorNames()
|
|
132
|
+
CONFIG_SERVER_URL = (
|
|
133
|
+
"http://fake-url-not-real"
|
|
134
|
+
if TEST_MODE
|
|
135
|
+
else "https://daq-config.diamond.ac.uk/api"
|
|
136
|
+
)
|
|
137
|
+
GRAYLOG_PORT = 12232
|
|
138
|
+
PARAMETER_SCHEMA_DIRECTORY = "src/hyperion/parameters/schemas/"
|
|
139
|
+
ZOCALO_ENV = "dev_artemis" if TEST_MODE else "artemis"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
CONST = HyperionConstants()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class Actions(Enum):
|
|
146
|
+
START = "start"
|
|
147
|
+
STOP = "stop"
|
|
148
|
+
SHUTDOWN = "shutdown"
|
|
149
|
+
STATUS = "status"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class Status(Enum):
|
|
153
|
+
WARN = "Warn"
|
|
154
|
+
FAILED = "Failed"
|
|
155
|
+
SUCCESS = "Success"
|
|
156
|
+
BUSY = "Busy"
|
|
157
|
+
ABORTING = "Aborting"
|
|
158
|
+
IDLE = "Idle"
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from dodal.devices.aperturescatterguard import ApertureValue
|
|
6
|
+
from dodal.devices.detector import (
|
|
7
|
+
DetectorParams,
|
|
8
|
+
)
|
|
9
|
+
from dodal.devices.fast_grid_scan import (
|
|
10
|
+
PandAGridScanParams,
|
|
11
|
+
ZebraGridScanParams,
|
|
12
|
+
)
|
|
13
|
+
from pydantic import Field, PrivateAttr
|
|
14
|
+
from scanspec.core import Path as ScanPath
|
|
15
|
+
from scanspec.specs import Line, Static
|
|
16
|
+
|
|
17
|
+
from mx_bluesky.hyperion.parameters.components import (
|
|
18
|
+
DiffractionExperimentWithSample,
|
|
19
|
+
IspybExperimentType,
|
|
20
|
+
OptionalGonioAngleStarts,
|
|
21
|
+
SplitScan,
|
|
22
|
+
WithOavCentring,
|
|
23
|
+
WithScan,
|
|
24
|
+
XyzStarts,
|
|
25
|
+
)
|
|
26
|
+
from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GridCommon(
|
|
30
|
+
DiffractionExperimentWithSample, OptionalGonioAngleStarts, WithOavCentring
|
|
31
|
+
):
|
|
32
|
+
grid_width_um: float = Field(default=CONST.PARAM.GRIDSCAN.WIDTH_UM)
|
|
33
|
+
exposure_time_s: float = Field(default=CONST.PARAM.GRIDSCAN.EXPOSURE_TIME_S)
|
|
34
|
+
use_roi_mode: bool = Field(default=CONST.PARAM.GRIDSCAN.USE_ROI)
|
|
35
|
+
panda_runup_distance_mm: float = Field(
|
|
36
|
+
default=CONST.HARDWARE.PANDA_FGS_RUN_UP_DEFAULT
|
|
37
|
+
)
|
|
38
|
+
use_panda: bool = Field(default=CONST.I03.USE_PANDA_FOR_GRIDSCAN)
|
|
39
|
+
use_gpu: bool = Field(default=CONST.I03.USE_GPU_FOR_GRIDSCAN_ANALYSIS)
|
|
40
|
+
use_cpu_and_gpu_zocalo: bool = Field(default=CONST.I03.USE_CPU_AND_GPU_ZOCALO)
|
|
41
|
+
ispyb_experiment_type: IspybExperimentType = Field(
|
|
42
|
+
default=IspybExperimentType.GRIDSCAN_3D
|
|
43
|
+
)
|
|
44
|
+
selected_aperture: ApertureValue | None = Field(default=ApertureValue.SMALL)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def detector_params(self):
|
|
48
|
+
self.det_dist_to_beam_converter_path = (
|
|
49
|
+
self.det_dist_to_beam_converter_path
|
|
50
|
+
or CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
|
|
51
|
+
)
|
|
52
|
+
optional_args = {}
|
|
53
|
+
if self.run_number:
|
|
54
|
+
optional_args["run_number"] = self.run_number
|
|
55
|
+
assert (
|
|
56
|
+
self.detector_distance_mm is not None
|
|
57
|
+
), "Detector distance must be filled before generating DetectorParams"
|
|
58
|
+
os.makedirs(self.storage_directory, exist_ok=True)
|
|
59
|
+
return DetectorParams(
|
|
60
|
+
detector_size_constants=I03Constants.DETECTOR,
|
|
61
|
+
expected_energy_ev=self.demand_energy_ev,
|
|
62
|
+
exposure_time=self.exposure_time_s,
|
|
63
|
+
directory=self.storage_directory,
|
|
64
|
+
prefix=self.file_name,
|
|
65
|
+
detector_distance=self.detector_distance_mm,
|
|
66
|
+
omega_start=self.omega_start_deg or 0,
|
|
67
|
+
omega_increment=0,
|
|
68
|
+
num_images_per_trigger=1,
|
|
69
|
+
num_triggers=self.num_images,
|
|
70
|
+
use_roi_mode=self.use_roi_mode,
|
|
71
|
+
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
72
|
+
trigger_mode=self.trigger_mode,
|
|
73
|
+
enable_dev_shm=self.use_gpu,
|
|
74
|
+
**optional_args,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class GridScanWithEdgeDetect(GridCommon): ...
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class PinTipCentreThenXrayCentre(GridCommon):
|
|
82
|
+
tip_offset_um: float = 0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class RobotLoadThenCentre(GridCommon):
|
|
86
|
+
thawing_time: float = Field(default=CONST.I03.THAWING_TIME)
|
|
87
|
+
|
|
88
|
+
def pin_centre_then_xray_centre_params(self):
|
|
89
|
+
my_params = self.model_dump()
|
|
90
|
+
del my_params["thawing_time"]
|
|
91
|
+
return PinTipCentreThenXrayCentre(**my_params)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class SpecifiedGridScan(GridCommon, XyzStarts, WithScan):
|
|
95
|
+
"""A specified grid scan is one which has defined values for the start position,
|
|
96
|
+
grid and box sizes, etc., as opposed to parameters for a plan which will create
|
|
97
|
+
those parameters at some point (e.g. through optical pin detection)."""
|
|
98
|
+
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ThreeDGridScan(SpecifiedGridScan, SplitScan):
|
|
103
|
+
"""Parameters representing a so-called 3D grid scan, which consists of doing a
|
|
104
|
+
gridscan in X and Y, followed by one in X and Z."""
|
|
105
|
+
|
|
106
|
+
demand_energy_ev: float | None = Field(default=None)
|
|
107
|
+
grid1_omega_deg: float = Field(default=CONST.PARAM.GRIDSCAN.OMEGA_1) # type: ignore
|
|
108
|
+
grid2_omega_deg: float = Field(default=CONST.PARAM.GRIDSCAN.OMEGA_2)
|
|
109
|
+
x_step_size_um: float = Field(default=CONST.PARAM.GRIDSCAN.BOX_WIDTH_UM)
|
|
110
|
+
y_step_size_um: float = Field(default=CONST.PARAM.GRIDSCAN.BOX_WIDTH_UM)
|
|
111
|
+
z_step_size_um: float = Field(default=CONST.PARAM.GRIDSCAN.BOX_WIDTH_UM)
|
|
112
|
+
y2_start_um: float
|
|
113
|
+
z2_start_um: float
|
|
114
|
+
x_steps: int = Field(gt=0)
|
|
115
|
+
y_steps: int = Field(gt=0)
|
|
116
|
+
z_steps: int = Field(gt=0)
|
|
117
|
+
_set_stub_offsets: bool = PrivateAttr(default_factory=lambda: False)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def FGS_params(self) -> ZebraGridScanParams:
|
|
121
|
+
return ZebraGridScanParams(
|
|
122
|
+
x_steps=self.x_steps,
|
|
123
|
+
y_steps=self.y_steps,
|
|
124
|
+
z_steps=self.z_steps,
|
|
125
|
+
x_step_size=self.x_step_size_um,
|
|
126
|
+
y_step_size=self.y_step_size_um,
|
|
127
|
+
z_step_size=self.z_step_size_um,
|
|
128
|
+
x_start=self.x_start_um,
|
|
129
|
+
y1_start=self.y_start_um,
|
|
130
|
+
z1_start=self.z_start_um,
|
|
131
|
+
y2_start=self.y2_start_um,
|
|
132
|
+
z2_start=self.z2_start_um,
|
|
133
|
+
set_stub_offsets=self.features.set_stub_offsets,
|
|
134
|
+
dwell_time_ms=self.exposure_time_s * 1000,
|
|
135
|
+
transmission_fraction=self.transmission_frac,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def panda_FGS_params(self) -> PandAGridScanParams:
|
|
140
|
+
if self.y_steps % 2 and self.z_steps > 0:
|
|
141
|
+
raise OddYStepsException(
|
|
142
|
+
"The number of Y steps must be even for a PandA gridscan"
|
|
143
|
+
)
|
|
144
|
+
return PandAGridScanParams(
|
|
145
|
+
x_steps=self.x_steps,
|
|
146
|
+
y_steps=self.y_steps,
|
|
147
|
+
z_steps=self.z_steps,
|
|
148
|
+
x_step_size=self.x_step_size_um,
|
|
149
|
+
y_step_size=self.y_step_size_um,
|
|
150
|
+
z_step_size=self.z_step_size_um,
|
|
151
|
+
x_start=self.x_start_um,
|
|
152
|
+
y1_start=self.y_start_um,
|
|
153
|
+
z1_start=self.z_start_um,
|
|
154
|
+
y2_start=self.y2_start_um,
|
|
155
|
+
z2_start=self.z2_start_um,
|
|
156
|
+
set_stub_offsets=self.features.set_stub_offsets,
|
|
157
|
+
run_up_distance_mm=self.panda_runup_distance_mm,
|
|
158
|
+
transmission_fraction=self.transmission_frac,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def do_set_stub_offsets(self, value: bool):
|
|
162
|
+
self._set_stub_offsets = value
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def grid_1_spec(self):
|
|
166
|
+
x_end = self.x_start_um + self.x_step_size_um * (self.x_steps - 1)
|
|
167
|
+
y1_end = self.y_start_um + self.y_step_size_um * (self.y_steps - 1)
|
|
168
|
+
grid_1_x = Line("sam_x", self.x_start_um, x_end, self.x_steps)
|
|
169
|
+
grid_1_y = Line("sam_y", self.y_start_um, y1_end, self.y_steps)
|
|
170
|
+
grid_1_z = Static("sam_z", self.z_start_um)
|
|
171
|
+
return grid_1_y.zip(grid_1_z) * ~grid_1_x
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def grid_2_spec(self):
|
|
175
|
+
x_end = self.x_start_um + self.x_step_size_um * (self.x_steps - 1)
|
|
176
|
+
z2_end = self.z2_start_um + self.z_step_size_um * (self.z_steps - 1)
|
|
177
|
+
grid_2_x = Line("sam_x", self.x_start_um, x_end, self.x_steps)
|
|
178
|
+
grid_2_z = Line("sam_z", self.z2_start_um, z2_end, self.z_steps)
|
|
179
|
+
grid_2_y = Static("sam_y", self.y2_start_um)
|
|
180
|
+
return grid_2_z.zip(grid_2_y) * ~grid_2_x
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def scan_indices(self):
|
|
184
|
+
"""The first index of each gridscan, useful for writing nexus files/VDS"""
|
|
185
|
+
return [
|
|
186
|
+
0,
|
|
187
|
+
len(ScanPath(self.grid_1_spec.calculate()).consume().midpoints["sam_x"]),
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def scan_spec(self):
|
|
192
|
+
"""A fully specified ScanSpec object representing both grids, with x, y, z and
|
|
193
|
+
omega positions."""
|
|
194
|
+
return self.grid_1_spec.concat(self.grid_2_spec)
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def scan_points(self):
|
|
198
|
+
"""A list of all the points in the scan_spec."""
|
|
199
|
+
return ScanPath(self.scan_spec.calculate()).consume().midpoints
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def scan_points_first_grid(self):
|
|
203
|
+
"""A list of all the points in the first grid scan."""
|
|
204
|
+
return ScanPath(self.grid_1_spec.calculate()).consume().midpoints
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def scan_points_second_grid(self):
|
|
208
|
+
"""A list of all the points in the second grid scan."""
|
|
209
|
+
return ScanPath(self.grid_2_spec.calculate()).consume().midpoints
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def num_images(self) -> int:
|
|
213
|
+
return len(self.scan_points["sam_x"])
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class OddYStepsException(Exception): ...
|