Kinetix + PandA detector modules (promoted from hex-ob) - #77
Kinetix + PandA detector modules (promoted from hex-ob)#77Anthony Sligar (sligara7) wants to merge 11 commits into
Conversation
…async version. Add classes for Filters and Monochromater. Add plan for changing energies, with auto-tuning of the second crystal pitch
- detectors/kinetix.py: HEXKinetixDetector (+ make_kinetix for Det:1/Det:3) with the HEX workarounds baked in: SWMR off, no flush-now (locks the plugin at HEX), 60 s HDF close for network storage, live-view stop on stage / restart on unstage, NumCaptured-reset race fix at prepare. HEXProcPluginIO adds the recursive-filter (frame averaging) and flat-field fields to Proc1:. SettablePathProvider / set_output_dir let plans retarget the output directory per scan. - detectors/panda.py: make_panda (HDFPanda, settable path provider). - tests/detectors/test_kinetix.py: device tests under mock. Existing modules (germ, phantom) untouched. The tomography plan family that drives these devices follows as its own PR.
00c88f1 to
7294985
Compare
There was a problem hiding this comment.
Pull request overview
This PR promotes HEX beamline device-layer components (photon delivery system devices/plans plus Kinetix & PandA detector modules) and updates the project/CI packaging workflow to use pixi-based environments.
Changes:
- Add new ophyd-async device implementations for the photon delivery system (shutter, filters, DCLM) and a
change_energyplan. - Add new detector modules for Kinetix (HEX-specific workarounds + settable path provider) and PandA (HDFPanda + settable path provider), plus related/updated detector tests.
- Switch CI/build/publish workflows and environment management to pixi (add
pixi.toml, update GitHub Actions, adjust pytest ignores/warnings).
Reviewed changes
Copilot reviewed 19 out of 22 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_utils.py |
Adds unit coverage for is_running_in_ci(). |
tests/test_photon_delivery_system.py |
Adds end-to-end mock tests for shutter, filters, DCLM, and change_energy (incl. auto-tune). |
tests/detectors/test_phantom.py |
Updates Phantom tests for the newer writer/acquire logic integration. |
tests/detectors/test_kinetix.py |
Adds mock device tests for the new HEX Kinetix module and path retargeting. |
tests/conftest.py |
Forces CI-mode env var in tests (autouse fixture). |
src/hextools/utils.py |
Adds general utilities (CI detection, RE initialization, device auto-connect, prompt). |
src/hextools/py.typed |
Marks package as typed. |
src/hextools/profiles/collection.py |
Adds a beamline profile collection script wiring up devices for interactive use. |
src/hextools/photon_delivery_system.py |
Introduces PDS device classes and change_energy plan. |
src/hextools/motors.py |
Adds motor utilities and a velocity-respecting mock. |
src/hextools/detectors/phantom.py |
Updates Phantom detector integration to newer ophyd-async writer/acquire APIs. |
src/hextools/detectors/panda.py |
Adds make_panda() helper using a settable path provider. |
src/hextools/detectors/kinetix.py |
Adds HEX Kinetix device/factory and HEX-specific HDF/data-logic workarounds. |
src/hextools/_version.pyi |
Removes a version stub file. |
pyproject.toml |
Updates runtime deps and pytest configuration (incl. ignoring profiles). |
pixi.toml |
Adds pixi environment definition and tasks for dev/CI/profile usage. |
.github/workflows/publish-pypi.yml |
Switches build/install/verify to pixi-based steps. |
.github/workflows/pixi-update.yml |
Adds scheduled workflow to update pixi lockfile via PRs. |
.github/workflows/ci.yml |
Updates lint/typecheck/tests to run via pixi environments per Python version. |
.github/dependabot.yml |
Adjusts update cadence/groups; removes uv lockfile updates. |
Suppressed comments (1)
src/hextools/profiles/collection.py:70
- This uses
Monochromator(...), but the corresponding device class inhextools.photon_delivery_systemisDCLM. After fixing the import, update the constructor call accordingly.
dclm = Monochromator("XF:27IDA-OP:1{Mono:DCLM-Ax:", name="dclm")
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return RunEngine( | ||
| { | ||
| "data_session": "pass-123456", | ||
| "cycle": ( | ||
| f"{datetime.today().year}-{int(datetime.today().month / 4) + 1}" | ||
| ), | ||
| } | ||
| ) |
|
|
||
| def change_energy( | ||
| dclm: DCLM, | ||
| energy: float = 0.0, |
| dclm.cooled_beam_stop, dclm.beam_stop_in, | ||
| dclm.flourescence_screen, fluo_y, | ||
| ) | ||
| # fmt: on`` |
| def _get_description(self, in_pos: int, motor_pos: float) -> str: | ||
| """Get the description of the current filter setting based on motor position. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| in_pos : int | ||
| Indicates whether the filter is in position. | ||
| motor_pos : float |
| from hextools.photon_delivery_system import ( | ||
| Filter, | ||
| FilterPosition, | ||
| FilterSetting, | ||
| Monochromator, | ||
| Shutter, | ||
| ) |
| kinetix1 = KinetixDetector( | ||
| "XF:27ID1-ES{Kinetix:1}", ADWriterFactory.hdf(path_provider), name="kinetix1" | ||
| ) | ||
| kinetix2 = KinetixDetector( | ||
| "XF:27ID1-ES{Kinetix:1}", ADWriterFactory.hdf(path_provider), name="kinetix2" | ||
| ) | ||
| kinetix3 = KinetixDetector( | ||
| "XF:27ID1-ES{Kinetix:1}", ADWriterFactory.hdf(path_provider), name="kinetix3" | ||
| ) | ||
| kinetix4 = KinetixDetector( | ||
| "XF:27ID1-ES{Kinetix:1}", ADWriterFactory.hdf(path_provider), name="kinetix4" | ||
| ) |
| "algotom", | ||
| "ipython", | ||
| "numpy", | ||
| "ophyd-async[ca, pva]", |
…module it was ported from (from PR review)
|
On the other seven review comments (utils.py cycle-quarter math, photon_delivery_system.py energy default / stray backticks / in_pos typing, profiles/collection.py Monochromator import and kinetix prefixes, pyproject ophyd-async floor): those lines all come from the stacked #76 base — this PR adds only detectors/kinetix.py, detectors/panda.py, and the kinetix tests, and deliberately doesn't touch those files. They're worth addressing on #76's side; the diff here collapses to this PR's three files once #76 merges. |
Stacked on #76 — branched from
pyepics-migrationso it builds against the pixi + ophyd-async 0.21 environment (the kinetix module uses the released 0.19/0.21 detector-assembly API, which the currentmainlock at 0.17a4 predates). The diff shows #76's commits until #76 merges, after which it collapses to this PR's own 3 files (+393 lines). No existing module is modified — germ and phantom are untouched.Promotes the proven kinetix-generation device work from NSLS2/hex-ob, where it was developed and tested against the mock and simulated beamline tiers:
detectors/kinetix.py—HEXKinetixDetector(+make_kinetixfactory for Det:1/Det:3) with the HEX workarounds baked in: SWMR off, no flush-now (locks the plugin at HEX), 60 s HDF close for network storage, live-view stop on stage / restart on unstage, and the NumCaptured-reset race fix at prepare.HEXProcPluginIOadds the recursive-filter (frame averaging) and flat-field fields toProc1:.SettablePathProvider/set_output_dirlet plans retarget the output directory per scan.detectors/panda.py—make_panda(HDFPanda, settable path provider; the tomography PandA).tests/detectors/test_kinetix.py— device tests under mock (construction, path-provider retargeting, count end-to-end, live-view restore).Observed on the base branch (pre-existing, not from this PR):
pytestat the repo root fails collection becausetestpathsincludessrcanddetectors/germ.pyimportsDetectorArmLogic, which the 0.21 line doesn't ship.pytest tests/is unaffected.