-
Notifications
You must be signed in to change notification settings - Fork 12
Create beamsize devices #1704
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Create beamsize devices #1704
Changes from all commits
e23a0a0
25181b5
aeb7256
d8cb85e
46096ff
2a57c7b
a58c05d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from typing import Annotated | ||
|
|
||
| from ophyd_async.core import SignalR, StandardReadable | ||
| from ophyd_async.core import StandardReadableFormat as Format | ||
|
|
||
|
|
||
| class BeamsizeBase(StandardReadable): | ||
| x_um: Annotated[SignalR[float], Format.HINTED_SIGNAL] | ||
| y_um: Annotated[SignalR[float], Format.HINTED_SIGNAL] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| from ophyd_async.core import Reference, derived_signal_r | ||
|
|
||
| from dodal.devices.aperturescatterguard import ApertureScatterguard | ||
| from dodal.devices.beamsize.beamsize import BeamsizeBase | ||
| from dodal.devices.i03.constants import BeamsizeConstants | ||
|
|
||
|
|
||
| class Beamsize(BeamsizeBase): | ||
| def __init__(self, aperture_scatterguard: ApertureScatterguard, name=""): | ||
| super().__init__(name=name) | ||
| self._aperture_scatterguard_ref = Reference(aperture_scatterguard) | ||
|
|
||
| self.x_um = derived_signal_r( | ||
| self._get_beamsize_x, | ||
| aperture_radius=self._aperture_scatterguard_ref().radius, | ||
| derived_units="µm", | ||
| ) | ||
| self.y_um = derived_signal_r( | ||
| self._get_beamsize_y, | ||
| aperture_radius=self._aperture_scatterguard_ref().radius, | ||
| derived_units="µm", | ||
| ) | ||
|
|
||
| def _get_beamsize_x( | ||
| self, | ||
| aperture_radius: float, | ||
| ) -> float: | ||
| return min(aperture_radius, BeamsizeConstants.BEAM_WIDTH_UM) | ||
|
|
||
| def _get_beamsize_y( | ||
| self, | ||
| aperture_radius: float, | ||
| ) -> float: | ||
| return min(aperture_radius, BeamsizeConstants.BEAM_HEIGHT_UM) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| from dataclasses import dataclass | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class BeamsizeConstants: | ||
| BEAM_WIDTH_UM = 80.0 | ||
| BEAM_HEIGHT_UM = 20.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| from ophyd_async.core import Reference, derived_signal_r | ||
|
|
||
| from dodal.devices.aperturescatterguard import ApertureScatterguard | ||
| from dodal.devices.beamsize.beamsize import BeamsizeBase | ||
| from dodal.devices.i04.transfocator import Transfocator | ||
|
|
||
|
|
||
| class Beamsize(BeamsizeBase): | ||
| def __init__( | ||
| self, | ||
| transfocator: Transfocator, | ||
| aperture_scatterguard: ApertureScatterguard, | ||
| name="", | ||
| ): | ||
| super().__init__(name=name) | ||
| self._transfocator_ref = Reference(transfocator) | ||
| self._aperture_scatterguard_ref = Reference(aperture_scatterguard) | ||
|
|
||
| self.x_um = derived_signal_r( | ||
| self._get_beamsize_x, | ||
| transfocator_size_x=self._transfocator_ref().current_horizontal_size_rbv, | ||
| aperture_radius=self._aperture_scatterguard_ref().radius, | ||
| derived_units="µm", | ||
| ) | ||
| self.y_um = derived_signal_r( | ||
| self._get_beamsize_y, | ||
| transfocator_size_y=self._transfocator_ref().current_vertical_size_rbv, | ||
| aperture_radius=self._aperture_scatterguard_ref().radius, | ||
| derived_units="µm", | ||
| ) | ||
|
|
||
| def _get_beamsize_x( | ||
| self, | ||
| transfocator_size_x: float, | ||
| aperture_radius: float, | ||
| ) -> float: | ||
| return min(transfocator_size_x, aperture_radius) | ||
|
|
||
| def _get_beamsize_y( | ||
| self, | ||
| transfocator_size_y: float, | ||
| aperture_radius: float, | ||
| ) -> float: | ||
| return min(transfocator_size_y, aperture_radius) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import asyncio | ||
| from collections.abc import AsyncGenerator | ||
|
|
||
| import pytest | ||
| from ophyd_async.core import init_devices | ||
|
|
||
| from dodal.common.beamlines.beamline_parameters import GDABeamlineParameters | ||
| from dodal.devices.aperturescatterguard import ( | ||
| AperturePosition, | ||
| ApertureScatterguard, | ||
| ApertureValue, | ||
| load_positions_from_beamline_parameters, | ||
| ) | ||
| from dodal.testing import patch_all_motors | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def aperture_positions() -> dict[ApertureValue, AperturePosition]: | ||
| return load_positions_from_beamline_parameters( | ||
| GDABeamlineParameters( | ||
| params={ | ||
| "miniap_x_LARGE_APERTURE": 2.389, | ||
| "miniap_y_LARGE_APERTURE": 40.986, | ||
| "miniap_z_LARGE_APERTURE": 15.8, | ||
| "sg_x_LARGE_APERTURE": 5.25, | ||
| "sg_y_LARGE_APERTURE": 4.43, | ||
| "miniap_x_MEDIUM_APERTURE": 2.384, | ||
| "miniap_y_MEDIUM_APERTURE": 44.967, | ||
| "miniap_z_MEDIUM_APERTURE": 15.8, | ||
| "sg_x_MEDIUM_APERTURE": 5.285, | ||
| "sg_y_MEDIUM_APERTURE": 0.46, | ||
| "miniap_x_SMALL_APERTURE": 2.430, | ||
| "miniap_y_SMALL_APERTURE": 48.974, | ||
| "miniap_z_SMALL_APERTURE": 15.8, | ||
| "sg_x_SMALL_APERTURE": 5.3375, | ||
| "sg_y_SMALL_APERTURE": -3.55, | ||
| "miniap_x_ROBOT_LOAD": 2.386, | ||
| "miniap_y_ROBOT_LOAD": 31.40, | ||
| "miniap_z_ROBOT_LOAD": 15.8, | ||
| "sg_x_ROBOT_LOAD": 5.25, | ||
| "sg_y_ROBOT_LOAD": 4.43, | ||
| "miniap_x_MANUAL_LOAD": -4.91, | ||
| "miniap_y_MANUAL_LOAD": -48.70, | ||
| "miniap_z_MANUAL_LOAD": -10.0, | ||
| "sg_x_MANUAL_LOAD": -4.7, | ||
| "sg_y_MANUAL_LOAD": 1.8, | ||
| } | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def aperture_tolerances(): | ||
| return AperturePosition.tolerances_from_gda_params( | ||
| GDABeamlineParameters( | ||
| { | ||
| "miniap_x_tolerance": 0.004, | ||
| "miniap_y_tolerance": 0.1, | ||
| "miniap_z_tolerance": 0.1, | ||
| "sg_x_tolerance": 0.1, | ||
| "sg_y_tolerance": 0.1, | ||
| } | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| async def ap_sg( | ||
| aperture_positions: dict[ApertureValue, AperturePosition], | ||
| aperture_tolerances: AperturePosition, | ||
| ) -> AsyncGenerator[ApertureScatterguard]: | ||
| async with init_devices(mock=True): | ||
| ap_sg = ApertureScatterguard( | ||
| aperture_prefix="-MO-MAPT-01:", | ||
| scatterguard_prefix="-MO-SCAT-01:", | ||
| name="test_ap_sg", | ||
| loaded_positions=aperture_positions, | ||
| tolerances=aperture_tolerances, | ||
| ) | ||
|
|
||
| with patch_all_motors(ap_sg): | ||
| yield ap_sg | ||
|
|
||
|
|
||
| async def set_to_position( | ||
| aperture_scatterguard: ApertureScatterguard, position: AperturePosition | ||
| ): | ||
| aperture_x, aperture_y, aperture_z, scatterguard_x, scatterguard_y = position.values | ||
|
|
||
| await asyncio.gather( | ||
| aperture_scatterguard.aperture.x.set(aperture_x), | ||
| aperture_scatterguard.aperture.y.set(aperture_y), | ||
| aperture_scatterguard.aperture.z.set(aperture_z), | ||
| aperture_scatterguard.scatterguard.x.set(scatterguard_x), | ||
| aperture_scatterguard.scatterguard.y.set(scatterguard_y), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| from math import inf | ||
| from unittest.mock import AsyncMock | ||
|
|
||
| import pytest | ||
| from ophyd_async.testing import set_mock_value | ||
|
|
||
| from dodal.devices.aperturescatterguard import ( | ||
| ApertureScatterguard, | ||
| ) | ||
| from dodal.devices.i03.beamsize import Beamsize | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "aperture_radius, expected_beamsize", | ||
| [ | ||
| (10.0, (10.0, 10.0)), | ||
| (50, (50.0, 20.0)), | ||
| (90, (80.0, 20.0)), | ||
| (inf, (80.0, 20.0)), | ||
| ], | ||
| ) | ||
| async def test_beamsize_gives_min_of_aperture_and_beam_width_and_height( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should: I think this is testing more of the internal logic of the aperture than you need. You should be able to just do
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I tried that initially but it doesn't work with derived signals. I'll raise an issue and mock
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| aperture_radius: float, | ||
| expected_beamsize: tuple[float, float], | ||
| ap_sg: ApertureScatterguard, | ||
| ): | ||
| set_mock_value(ap_sg.aperture.medium, 1) | ||
|
|
||
| ap_sg.radius.read = AsyncMock( | ||
| return_value={ | ||
| "test_ap_sg-radius": { | ||
| "value": aperture_radius, | ||
| "timestamp": 1763051436.7372239, | ||
| "alarm_severity": 0, | ||
| } | ||
| } | ||
| ) # see https://github.com/bluesky/ophyd-async/issues/1132 | ||
|
|
||
| beamsize = Beamsize(aperture_scatterguard=ap_sg) | ||
|
|
||
| beamsize_x = await beamsize.x_um.get_value() | ||
| beamsize_y = await beamsize.y_um.get_value() | ||
| assert (beamsize_x, beamsize_y) == expected_beamsize | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import pytest | ||
| from ophyd_async.core import init_devices | ||
|
|
||
| from dodal.devices.i04.transfocator import Transfocator | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| async def fake_transfocator() -> Transfocator: | ||
| async with init_devices(mock=True): | ||
| transfocator = Transfocator(prefix="", name="transfocator") | ||
| return transfocator |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| from math import inf | ||
| from unittest.mock import AsyncMock | ||
|
|
||
| import pytest | ||
| from ophyd_async.testing import set_mock_value | ||
|
|
||
| from dodal.devices.aperturescatterguard import ( | ||
| ApertureScatterguard, | ||
| ) | ||
| from dodal.devices.i04.beamsize import Beamsize | ||
| from dodal.devices.i04.transfocator import Transfocator | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "aperture_radius, transfocator_sizes, expected_beamsize", | ||
| [ | ||
| (10.0, (50.0, 60.0), (10.0, 10.0)), | ||
| (20.0, (10.0, 30.0), (10.0, 20.0)), | ||
| (20.0, (30.0, 10.0), (20.0, 10.0)), | ||
| (100.0, (50.0, 60.0), (50.0, 60.0)), | ||
| (inf, (50.0, 60.0), (50.0, 60.0)), | ||
| ], | ||
| ) | ||
| async def test_beamsize_gives_min_of_aperture_and_transfocator_width_and_height( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should: As on the other test re internal device logic |
||
| aperture_radius: float, | ||
| transfocator_sizes: tuple[float, float], | ||
| expected_beamsize: tuple[float, float], | ||
| fake_transfocator: Transfocator, | ||
| ap_sg: ApertureScatterguard, | ||
| ): | ||
| set_mock_value(ap_sg.aperture.medium, 1) | ||
|
|
||
| ap_sg.radius.read = AsyncMock( | ||
| return_value={ | ||
| "test_ap_sg-radius": { | ||
| "value": aperture_radius, | ||
| "timestamp": 1763051436.7372239, | ||
| "alarm_severity": 0, | ||
| } | ||
| } | ||
| ) # see https://github.com/bluesky/ophyd-async/issues/1132 | ||
|
|
||
| set_mock_value(fake_transfocator.current_horizontal_size_rbv, transfocator_sizes[0]) | ||
| set_mock_value(fake_transfocator.current_vertical_size_rbv, transfocator_sizes[1]) | ||
|
|
||
| beamsize = Beamsize(transfocator=fake_transfocator, aperture_scatterguard=ap_sg) | ||
|
|
||
| beamsize_x = await beamsize.x_um.get_value() | ||
| beamsize_y = await beamsize.y_um.get_value() | ||
| assert (beamsize_x, beamsize_y) == expected_beamsize | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only in reading this that I've realised that this isn't the radius at all, it's the diameter. Would you be able to write an issue to fix it in the
ApertureScattergaurdand downstream?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that completely went over my head too lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#1710