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

Refactor and create ophyd-async FGS devices #422

Merged
merged 20 commits into from
Jun 11, 2024
Merged

Conversation

olliesilvester
Copy link
Collaborator

@olliesilvester olliesilvester commented Apr 5, 2024

Fixes #413

See associated Hyperion change at: DiamondLightSource/hyperion#1294

Combines the FGS parameters and devices for panda and zebra scans, and converts them to ophyd-async.

Instructions to reviewer on how to test:

  1. Check overall logic is unchanged
  2. Run tests

Checks for reviewer

  • Would the PR title make sense to a scientist on a set of release notes
  • If a new device has been added does it follow the standards

Copy link

codecov bot commented Apr 5, 2024

Codecov Report

Attention: Patch coverage is 98.00000% with 2 lines in your changes are missing coverage. Please review.

Project coverage is 93.24%. Comparing base (be80792) to head (ca768c8).
Report is 3 commits behind head on main.

❗ Current head ca768c8 differs from pull request most recent head b852925. Consider uploading reports for the commit b852925 to get more accurate results

Files Patch % Lines
src/dodal/devices/fast_grid_scan.py 97.97% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #422      +/-   ##
==========================================
+ Coverage   93.05%   93.24%   +0.19%     
==========================================
  Files          90       89       -1     
  Lines        3325     3243      -82     
==========================================
- Hits         3094     3024      -70     
+ Misses        231      219      -12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@olliesilvester olliesilvester marked this pull request as ready for review April 10, 2024 12:06
@olliesilvester olliesilvester marked this pull request as ready for review May 2, 2024 10:10
Copy link
Contributor

@DominicOram DominicOram left a comment

Choose a reason for hiding this comment

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

Couple of comments but ran out of time for a full review, sorry

src/dodal/devices/fast_grid_scan.py Outdated Show resolved Hide resolved
src/dodal/devices/fast_grid_scan.py Outdated Show resolved Hide resolved
Comment on lines 343 to 346
def set_fast_grid_scan_params(scan: FastGridScanCommon, params: GridScanParamsCommon):
assert set(params.get_param_positions().keys()) == set(
scan.movable_params.keys()
), "Scan parameters don't match the scan device"
Copy link
Contributor

Choose a reason for hiding this comment

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

Could: We can get the type checker to check this.

ParamType = TypeVar("ParamType", bound=GridScanParamsCommon)

class FastGridScanCommon(Generic[ParamType], StandardReadable, ABC):
    ...
    
class ZebraFastGridScan(FastGridScanCommon[ZebraGridScanParams]):
    ....
   
class PandAFastGridScan(FastGridScanCommon[PandAGridScanParams]):
    ....
    
def set_fast_grid_scan_params(scan: FastGridScanCommon[ParamType], params: ParamType):
   ....

Which will make the type checker highlight code like

    assert isinstance(parameters.experiment_params, PandAGridScanParams)
    yield from set_flyscan_params(
        fgs_composite.zebra_fast_grid_scan, parameters.experiment_params
    )

I would be interested to see if @callumforrester or @dperl-dls have thoughts on using the type system like this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I like it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nice!

Copy link
Contributor

@rtuck99 rtuck99 left a comment

Choose a reason for hiding this comment

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

Approved as my comments addressed

Copy link
Contributor

@DominicOram DominicOram left a comment

Choose a reason for hiding this comment

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

Great, thanks, almost there. Couple of comments. Also, most of the tests only run against the zebra version, maybe just parameterize a couple to run against the panda version too?


def set_fast_grid_scan_params(
scan: FastGridScanCommon[ParamType], params: GridScanParamsCommon
Copy link
Contributor

Choose a reason for hiding this comment

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

Must: params should be of type ParamType. This means we can then remove the assert and just trust the type system to catch when the params don't match

Comment on lines 222 to 230
self.parent = parent

async def get_value(self):
x = int(await self.parent.x_steps.get_value()) # type: ignore
y = int(await self.parent.y_steps.get_value()) # type: ignore
z = int(await self.parent.z_steps.get_value()) # type: ignore
first_grid = x * y
second_grid = x * z
return first_grid + second_grid
Copy link
Contributor

Choose a reason for hiding this comment

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

Should: I think if you specify the type of the parent you can fix these type issues:

Suggested change
self.parent = parent
async def get_value(self):
x = int(await self.parent.x_steps.get_value()) # type: ignore
y = int(await self.parent.y_steps.get_value()) # type: ignore
z = int(await self.parent.z_steps.get_value()) # type: ignore
first_grid = x * y
second_grid = x * z
return first_grid + second_grid
self.parent: "FastGridScanCommon" = parent
async def get_value(self):
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

status.wait()
assert status.exception() is None
@pytest.fixture
async def panda_fast_grid_scan(request):
Copy link
Contributor

Choose a reason for hiding this comment

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

Should: Why do we need request here and in the other fixture? They don't seem to be used

@olliesilvester
Copy link
Collaborator Author

olliesilvester commented Jun 10, 2024

Great, thanks, almost there. Couple of comments. Also, most of the tests only run against the zebra version, maybe just parameterize a couple to run against the panda version too?

I'll add a few now - the tests will change once DiamondLightSource/hyperion#1432 is done anyway

actually, these tests won't change!

Copy link
Contributor

@DominicOram DominicOram left a comment

Choose a reason for hiding this comment

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

Great, thanks! Minor nit comment if you want to take it, up to you

Comment on lines 45 to 56
@pytest.mark.parametrize(
"use_pgs",
[(False), (True)],
)
async def test_given_settings_valid_when_kickoff_then_run_started(
use_pgs,
zebra_fast_grid_scan: ZebraFastGridScan,
panda_fast_grid_scan: PandAFastGridScan,
):
set_mock_value(zebra_fast_grid_scan.scan_invalid, False)
set_mock_value(zebra_fast_grid_scan.position_counter, 0)
set_mock_value(zebra_fast_grid_scan.status, 1)
grid_scan: ZebraFastGridScan | PandAFastGridScan = (
panda_fast_grid_scan if use_pgs else zebra_fast_grid_scan
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: So I think you could be clever here with fixtures (https://engineeringfordatascience.com/posts/pytest_fixtures_with_parameterize/#using-requestgetfixturevalue-) and do something like:

@pytest.mark.parametrize(
    "grid_scan",
    ["zebra_fast_grid_scan", "panda_fast_grid_scan"],
)
async def test_given_settings_valid_when_kickoff_then_run_started(
    grid_scan_type: str, request
):
    grid_scan: ZebraFastGridScan | PandAFastGridScan = request.getfixturevalue(grid_scan_type)

But I appreciate that this may cause more confusion/wackiness down the line so leave it up to you if you think it's nicer or not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I just had a play with this, I think there are some issues with using request.getfixturevalue with asyncio fixtures. There's probably ways around this, but I'll leave it for now

@olliesilvester olliesilvester merged commit 44bdecc into main Jun 11, 2024
11 checks passed
@olliesilvester olliesilvester deleted the 413_make_common_FGS branch June 11, 2024 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make a common ophyd-async FastGridScan device
4 participants