Skip to content

Implement EPICS-backed Polynomial ID Device for I06#2016

Open
Relm-Arrowny wants to merge 41 commits intomainfrom
2003-add-apple2-energy-device-to-i06
Open

Implement EPICS-backed Polynomial ID Device for I06#2016
Relm-Arrowny wants to merge 41 commits intomainfrom
2003-add-apple2-energy-device-to-i06

Conversation

@Relm-Arrowny
Copy link
Copy Markdown
Contributor

@Relm-Arrowny Relm-Arrowny commented Apr 17, 2026

Fixes #2003

  • Introduced I06EpicsPolynomialDevice to construct EnergyMotorLookup tables dynamically from EPICS PVs.
  • Added an optional inverse EnergyMotorConverter to Apple2Controller for bidirectional gap/energy calculations.
  • Renamed EnergyMotorConverter parameters from energy to value to better support generic bidirectional conversions (e.g., Motor ↔ Energy).

Instructions to reviewer on how to test:

  1. Do thing x
  2. Confirm thing y happens

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
  • If changing the API for a pre-existing device, ensure that any beamlines using this device have updated their Bluesky plans accordingly
  • Have the connection tests for the relevant beamline(s) been run via dodal connect ${BEAMLINE}

@Relm-Arrowny Relm-Arrowny linked an issue Apr 17, 2026 that may be closed by this pull request
7 tasks
Copy link
Copy Markdown
Contributor

@oliwenmandiamond oliwenmandiamond left a comment

Choose a reason for hiding this comment

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

This looks good. Just think the variable name needs to be changed + need to add the config

Comment thread src/dodal/devices/insertion_device/apple2_controller.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

❌ Patch coverage is 99.27536% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 99.09%. Comparing base (257b180) to head (896da8c).

Files with missing lines Patch % Lines
...ices/beamlines/i06_shared/i06_apple2_controller.py 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2016      +/-   ##
==========================================
- Coverage   99.09%   99.09%   -0.01%     
==========================================
  Files         326      328       +2     
  Lines       12507    12600      +93     
==========================================
+ Hits        12394    12486      +92     
- Misses        113      114       +1     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Relm-Arrowny Relm-Arrowny changed the title add gap to energy coverter to controller add i06 apple2 and energy controllers Apr 23, 2026
@Relm-Arrowny Relm-Arrowny changed the title add i06 apple2 and energy controllers Implement EPICS-backed Polynomial ID Device for I06 Apr 23, 2026
@Relm-Arrowny Relm-Arrowny marked this pull request as ready for review April 23, 2026 16:15
@Relm-Arrowny Relm-Arrowny requested a review from a team as a code owner April 23, 2026 16:15
Copy link
Copy Markdown
Contributor

@oliwenmandiamond oliwenmandiamond left a comment

Choose a reason for hiding this comment

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

Thanks, is a good first start but I think the design needs a bit more work. Please see my comments

Comment on lines +37 to +39
self.gap_energy_motor_lut = gap_energy_motor_lut
self.phase_energy_motor_lut = phase_energy_motor_lut
self.inverse_gap_energy_motor_lut = inverse_gap_energy_motor_lut
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this needed? These are never used anywhere and parent class will keep the method to find the lut

Comment on lines +49 to +58
def _get_apple2_value(self, gap: float, phase: float, pol: Pol) -> Apple2Val:
return Apple2Val(
gap=gap,
phase=Apple2PhasesVal(
top_outer=phase,
top_inner=0.0,
btm_inner=phase,
btm_outer=0.0,
),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This shares the same return as Apple2EnforceLHMoveController. I wonder if there is a better way we could do this or just accept the duplication. Maybe create a another controller called FourArmApple2Controller or UnlockedApple2Controller which this and Apple2EnforceLHMoveController inherit from.

# Insertion Device
# ------------- Downstream Insertion Device --------------------
@devices.factory()
def i06_idd_epics_polynomial_device() -> I06EpicsPolynomialDevice:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
def i06_idd_epics_polynomial_device() -> I06EpicsPolynomialDevice:
def idd_polynomial() -> I06EpicsPolynomialDevice:

We don't need beamline, epics and device on the instance


# -------------------- Upstream Insertion Device -------------------
@devices.factory()
def i06_idu_epics_polynomial_device() -> I06EpicsPolynomialDevice:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
def i06_idu_epics_polynomial_device() -> I06EpicsPolynomialDevice:
def idu_polynomial() -> I06EpicsPolynomialDevice:

}


class I06EpicsPolynomialDevice(Device, Triggerable):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This class seems confused to me. This should be inheriting from EnergyMotorLookup.

I think this possibly needs to be restructured so that we have three classes of this, one for EnergyGapEpicsMotorLookup, EnergyPhaseEpicsMotorLookup, and GapMotorEpicsEnergyLookup and then create an instance of each one. This way each logic is more focused and smaller.

Then we can also update base class method EnergyMotorLookup.update_lookup to be async and then create each one to then give to the controller. We could even make it automatically update by adding a monitor and then forcing an update table if detects a change.

energy=self._energy,
derived_units=units,
)
self._energy = soft_signal_rw(float, initial_value=100)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why have we given a random value of 100 as the initial value. Where are the units?

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.

Add apple2 energy device to i06

2 participants