Skip to content
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

Fix getting expected images from a gridscan #1035

Merged
merged 4 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 13 additions & 18 deletions src/dodal/devices/fast_grid_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
AsyncStatus,
Device,
Signal,
SignalR,
SignalRW,
SoftSignalBackend,
StandardReadable,
wait_for_value,
)
Expand All @@ -24,6 +22,7 @@
from pydantic import field_validator
from pydantic.dataclasses import dataclass

from dodal.common.signal_utils import create_hardware_backed_soft_signal
from dodal.log import LOGGER
from dodal.parameters.experiment_parameter_base import AbstractExperimentWithBeamParams

Expand Down Expand Up @@ -170,21 +169,6 @@ def __init__(self, prefix: str, name: str = "") -> None:
self.program_number = epics_signal_r(float, prefix + "CS1:PROG_NUM")


class ExpectedImages(SignalR[int]):
def __init__(self, parent: "FastGridScanCommon") -> None:
super().__init__(SoftSignalBackend(int))
self.parent = parent

async def get_value(self, cached: bool | None = None):
assert isinstance(self.parent, FastGridScanCommon)
x = await self.parent.x_steps.get_value()
y = await self.parent.y_steps.get_value()
z = await self.parent.z_steps.get_value()
first_grid = x * y
second_grid = x * z
return first_grid + second_grid


class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
"""Device for a general fast grid scan

Expand Down Expand Up @@ -217,7 +201,9 @@ def __init__(self, prefix: str, smargon_prefix: str, name: str = "") -> None:
self.run_cmd = epics_signal_x(f"{prefix}RUN.PROC")
self.status = epics_signal_r(int, f"{prefix}SCAN_STATUS")

self.expected_images = ExpectedImages(parent=self)
self.expected_images = create_hardware_backed_soft_signal(
float, self._calculate_expected_images
)

self.motion_program = MotionProgram(smargon_prefix)

Expand All @@ -243,6 +229,15 @@ def __init__(self, prefix: str, smargon_prefix: str, name: str = "") -> None:
}
super().__init__(name)

async def _calculate_expected_images(self):
x = await self.x_steps.get_value()
y = await self.y_steps.get_value()
z = await self.z_steps.get_value()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could: Can this be parallelized? If not then may be worth putting a comment explaining why to avoid tripping someone up in future.

Suggested change
x = await self.x_steps.get_value()
y = await self.y_steps.get_value()
z = await self.z_steps.get_value()
x, y, z = await asyncio.gather(self.x_steps.get_value(), self.y_steps.get_value(), self.z_steps.get_value()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it can be parallelised. I have tended not to bother with parallel reads because they're normally very short anyway but I will add it in.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, my concern is mainly that someone in the future might wonder why it wasn't done and go down an archaeological rabbit hole.

LOGGER.info(f"Reading num of images found {x, y, z} images in each axis")
first_grid = x * y
second_grid = x * z
return first_grid + second_grid

@AsyncStatus.wrap
async def kickoff(self):
curr_prog = await self.motion_program.program_number.get_value()
Expand Down
6 changes: 5 additions & 1 deletion tests/devices/unit_tests/test_gridscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ async def test_given_different_step_numbers_then_expected_images_correct(
set_mock_value(zebra_fast_grid_scan.y_steps, steps[1])
set_mock_value(zebra_fast_grid_scan.z_steps, steps[2])

assert await zebra_fast_grid_scan.expected_images.get_value() == expected_images
RE = RunEngine(call_returns_result=True)

result = RE(bps.rd(zebra_fast_grid_scan.expected_images))

assert result.plan_result == expected_images # type: ignore


@pytest.mark.parametrize(
Expand Down
Loading