
Optimizing Synchrotron Scanning Control with Async and Ophyd
Explore the journey of developing ophyd-async to enhance synchrotron scanning control, with a focus on goals, history, and comparison with ophyd sync. Learn about the async threaded asyncio implementation and the streamlined high-voltage set process. Dive into the ChanneltronHV class comparison and contrast for improved PV positioning. Discover the step scan, fly scan, and async handling for smoother operation.
Uploaded on | 1 Views
Download Presentation

Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.
E N D
Presentation Transcript
Contents History Goals Compare and contrast with ophyd sync oWarning: contains code... Status of features oWarning: contains predictions about the future... Should I use it?
Some history DLS needed to modernize their scanning stack Could already do flyscanning but needed more flexibility Wrote a prototype implementing bluesky Device interface Started development of ophyd.v2 package Moved it out to ophyd-async to decouple release cycles
Goals Async simplify parallel control of multiple PVs Support for CA, PVA, Tango Clean Device Definition Ease the implementation of flyscanning Parity and interoperability with ophyd sync Parity with the flyscanning that pymalcolm can do
Step scan: Software EXPOSE... MOVE... SETTLE...
Fly scan: Stepped motion EXPOSE... MOVE... SETTLE...
Fly scan: Continuous motion EXPOSE... MOVE...
Async Threaded Asyncio def set_signal_thread(signal, value): t = Thread(signal.set, value) t.start() return t async def run(): await asyncio.gather( signal1.set(value1), signal2.set(value2) ) value = await signal3.get_value() def run(): t1 = set_signal_thread(signal1, value1) t2 = set_signal_thread(signal2, value2) t1.join() t2.join() value = signal3.get_value()
Set high voltage Example: A ChannelTron Set acquire interval Start acquiring Wait until not busy Read counts
Compare and contrast class ChanneltronHV(PVPositionerComparator): setpoint = Cpt(EpicsSignal, "-SP", kind="normal") readback = Cpt(EpicsSignalRO, "-RB", kind="hinted") atol = 1 # tolerance before we set done (in um) def __init__(self, prefix, **kwargs): super().__init__(prefix, **kwargs) self.readback.name = self.name def done_comparator(self, readback, setpoint): return abs(setpoint - readback) < self.atol
Compare and contrast from typing import Annotated as A class ChanneltronHV(StandardReadable, EpicsDevice): setpoint: A[SignalRW[float], PvSuffix("-SP"), Format.CHILD] readback: A[SignalR[float], PvSuffix("-RB"), Format.HINTED_SIGNAL] def set_name(self, name, *, child_name_separator=None): super().set_name(name, child_name_separator=child_name_separator) self.readback.set_name(name) @AsyncStatus.wrap async def set(self, value: float): await self.setpoint.set(value) async for new_value in observe_value(self.readback): if abs(new_value - value) < 1: break # within 1um, close enough But this should probably be a feature of Signal...
Compare and contrast class Channeltron(Device): # Config hv = Cpt(ChanneltronHV, "HighVoltage", kind="config") acq_mode = Cpt(EpicsSignal, "Mode-SP", string=True, kind="config") interval = Cpt(EpicsSignal, "Inter-RB", write_pv="Inter-SP", kind="config") # Read count = Cpt(EpicsSignalRO, "Counter-RB", kind="hinted") # Internal trigger_cmd = Cpt(EpicsSignal, "Trigger", put_complete=True, kind="omitted") busy = Cpt(EpicsSignalRO, "ActualBusy", kind="omitted") def trigger(self): def check_value(*, old_value, value, **kwargs): return value == 0 self.trigger_cmd.set(1) status = SubscriptionStatus(self.busy, check_value) return status
Compare and contrast class Channeltron(StandardReadable, Device): def __init__(self, prefix: str, name=""): # Config with self.add_children_as_readables(Format.CONFIG_SIGNAL): self.hv = ChanneltronHV(prefix + "HighVoltage") self.acq_mode = epics_signal_rw(ChanneltronMode, "Mode-SP") self.interval = epics_signal_rw(float, "Inter-RB", write_pv="Inter-SP") # Read with self.add_children_as_readables(Format.HINTED_SIGNAL): self.count = epics_signal_r(int, "Counter-RB") # Internal self.trigger_cmd = epics_signal_x("Trigger") self.busy = epics_signal_r(bool, "ActualBusy") super().__init__(name=name) class ChanneltronMode(StrictEnum): STATIC = "Static" DYNAMIC = "Dynamic" @AsyncStatus.wrap async def trigger(self): await self.trigger_cmd.trigger() await wait_for_value(self.busy, False, timeout=5)
Status at Diamond Beamline Group Deployment Devices VMXM I03 I04 I23 I24 I10 I13-1 I19 I22 I18 I20-1 Various Test Rigs MX MX MX MX MX Magnetic Materials Imaging and Microscopy Crystallography SCM Spectroscopy Spectroscopy SSCC In Progress Planned Deployed In Progress In Progress Planned Deployed In Progress Deployed Deployed Deployed Deployed 1 23 21 1 14 38 4 7 21 10 2 24
Status of features Feature How complete 90% 80% 70% 80% 90% 30% 10% How likely to need changes 5% 10% 20% 50% 50% 90% 90% Device creation and signal interactions Detector creation Software flyscanning Time resolved triggering of detectors Linear motor flyscanning Trajectory motor flyscanning Software kinematics
Should I use it? If you interact directly with ophyd devices in an iPython terminal oThen no If you are adding features to an existing ophyd device oThen probably not If you are doing something new oThen probably yes Device API should be considered stable Flyscanning API is not stable 1.0 release when flyscanning API stable, probably end of year
Conclusion Async gives more predictable concurrency Ophyd-async and ophyd can coexist Device API now stable, ready to be used in new projects Flyscanning under heavy development Thanks to all the contributors! Thank you for listening