Bluesky hardware abstraction library with EPICS control system integration for scientific instrument automation.
npx @tessl/cli install tessl/pypi-ophyd@1.11.00
# Ophyd
1
2
Ophyd is a comprehensive Python library that provides a high-level hardware abstraction layer for experiment orchestration and data acquisition systems, with particular emphasis on EPICS (Experimental Physics and Industrial Control System) control systems. It enables scientists and engineers to interact with diverse hardware devices through a unified interface featuring standardized methods like `trigger()`, `read()`, and `set()`, while abstracting away control system specifics.
3
4
## Package Information
5
6
- **Package Name**: ophyd
7
- **Language**: Python
8
- **Installation**: `pip install ophyd`
9
- **Documentation**: https://blueskyproject.io/ophyd
10
- **Version**: 1.11.0
11
12
## Core Imports
13
14
```python
15
import ophyd
16
```
17
18
Common imports for device creation and control:
19
20
```python
21
from ophyd import Device, Component, EpicsSignal, EpicsMotor
22
from ophyd import Signal, SignalRO, DerivedSignal, EpicsSignalNoValidation
23
from ophyd import ALL_COMPONENTS, Kind
24
from ophyd import setup_ophyd, set_handler
25
from ophyd.status import StatusBase, wait
26
```
27
28
For area detector functionality:
29
30
```python
31
from ophyd.areadetector import AreaDetector, SimDetector
32
from ophyd.areadetector.plugins import HDF5Plugin, ImagePlugin
33
```
34
35
## Basic Usage
36
37
```python
38
from ophyd import Device, Component, EpicsSignal, EpicsMotor
39
from ophyd.status import wait
40
41
# Define a custom device using Components
42
class MyDevice(Device):
43
temperature = Component(EpicsSignal, 'TEMP:PV')
44
pressure = Component(EpicsSignal, 'PRES:PV')
45
46
# Create device instance
47
device = MyDevice('MY:DEVICE:', name='my_device')
48
49
# Connect to hardware
50
device.wait_for_connection()
51
52
# Read all device values
53
reading = device.read()
54
print(reading)
55
56
# Use a positioner (motor)
57
motor = EpicsMotor('XF:28IDC:MOTOR1', name='motor1')
58
motor.wait_for_connection()
59
60
# Move motor and wait for completion
61
status = motor.set(10.0) # Move to position 10.0
62
wait(status) # Wait for move to complete
63
64
# Read motor position
65
current_pos = motor.position
66
print(f"Current position: {current_pos}")
67
```
68
69
## Architecture
70
71
Ophyd's design is built around several key concepts:
72
73
- **Device**: High-level objects that group related hardware components and provide coordinated operations
74
- **Signal**: Individual readable/writable values representing hardware channels or computed values
75
- **Component**: Building blocks that define the structure of devices and their relationships
76
- **Status**: Objects that track the progress of asynchronous operations like moves or acquisitions
77
- **Control Layer**: Abstraction over EPICS backends (pyepics, caproto, dummy) for different deployment needs
78
79
This architecture enables the creation of reusable, composable device definitions that can be shared across facilities and experiments while maintaining compatibility with the broader Bluesky ecosystem for automated data collection.
80
81
## Capabilities
82
83
### Core Framework
84
85
The foundational classes and interfaces that form the backbone of ophyd's device abstraction system, including device composition, signal management, and status tracking.
86
87
```python { .api }
88
class Device:
89
def __init__(self, prefix='', *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs): ...
90
def read(self): ...
91
def describe(self): ...
92
def configure(self, d): ...
93
def read_configuration(self): ...
94
def describe_configuration(self): ...
95
def trigger(self): ...
96
def stage(self): ...
97
def unstage(self): ...
98
99
class Signal:
100
def __init__(self, *, name, value=0, timestamp=None, parent=None, kind='normal', tolerance=None, rtolerance=None, **kwargs): ...
101
def get(self, **kwargs): ...
102
def put(self, value, **kwargs): ...
103
def set(self, value, **kwargs): ...
104
def read(self): ...
105
def describe(self): ...
106
107
class Component:
108
def __init__(self, cls, suffix='', *, lazy=False, trigger_value=None, add_prefix=None, doc=None, kind='normal', **kwargs): ...
109
110
class StatusBase:
111
def __init__(self, *, timeout=None, settle_time=None): ...
112
@property
113
def done(self): ...
114
@property
115
def success(self): ...
116
def wait(self, timeout=None): ...
117
118
def wait(status, timeout=None, poll_period=0.05): ...
119
```
120
121
[Core Framework](./core-framework.md)
122
123
### EPICS Integration
124
125
Hardware control through EPICS process variables, providing the primary interface to laboratory instruments and control systems in scientific facilities.
126
127
```python { .api }
128
class EpicsSignal(Signal):
129
def __init__(self, read_pv, write_pv=None, *, pv_kw=None, put_complete=False, string=False, limits=False, auto_monitor=None, name=None, **kwargs): ...
130
131
class EpicsSignalRO(Signal):
132
def __init__(self, read_pv, *, pv_kw=None, string=False, limits=False, auto_monitor=None, name=None, **kwargs): ...
133
134
def set_cl(control_layer=None, *, pv_telemetry=False): ...
135
def get_cl(): ...
136
```
137
138
[EPICS Integration](./epics-integration.md)
139
140
### Motors and Positioners
141
142
Positioning devices including EPICS motors, software positioners, and pseudo positioners for coordinate transformations and multi-axis control.
143
144
```python { .api }
145
class EpicsMotor(Device):
146
def __init__(self, prefix='', *, name, settle_time=0.0, timeout=None, **kwargs): ...
147
def move(self, position, **kwargs): ...
148
def home(self, direction, **kwargs): ...
149
def stop(self, **kwargs): ...
150
@property
151
def position(self): ...
152
153
class PseudoPositioner(Device):
154
def __init__(self, prefix='', *, configuration_attrs=None, read_attrs=None, **kwargs): ...
155
def forward(self, pseudo_pos): ...
156
def inverse(self, real_pos): ...
157
158
class SoftPositioner(Device):
159
def __init__(self, *, name=None, **kwargs): ...
160
def move(self, position, **kwargs): ...
161
```
162
163
[Motors and Positioners](./motors-positioners.md)
164
165
### Area Detectors
166
167
Comprehensive support for 2D area detectors including cameras, plugins for data processing, file I/O, and triggering strategies for coordinated data acquisition.
168
169
```python { .api }
170
class AreaDetector(Device):
171
def __init__(self, prefix, *, name, **kwargs): ...
172
def stage(self): ...
173
def unstage(self): ...
174
def trigger(self): ...
175
176
class DetectorBase(Device):
177
def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwargs): ...
178
179
# Camera classes for specific detector types
180
class SimDetectorCam(CamBase): ...
181
class AndorCam(CamBase): ...
182
class PerkinElmerCam(CamBase): ...
183
184
# Plugin classes for data processing
185
class HDF5Plugin(FilePlugin): ...
186
class TIFFPlugin(FilePlugin): ...
187
class ImagePlugin(PluginBase): ...
188
class StatsPlugin(PluginBase): ...
189
```
190
191
[Area Detectors](./area-detectors.md)
192
193
### Specialized Devices
194
195
Pre-built device classes for common laboratory instruments including scalers, multi-channel analyzers, and electrometers.
196
197
```python { .api }
198
class EpicsScaler(Device):
199
def __init__(self, prefix, *, name, **kwargs): ...
200
def stage(self): ...
201
def trigger(self): ...
202
203
class EpicsMCA(Device):
204
def __init__(self, prefix, *, name, **kwargs): ...
205
def erase(self): ...
206
def start(self): ...
207
def stop(self): ...
208
209
class QuadEM(Device):
210
def __init__(self, prefix, *, name, **kwargs): ...
211
def stage(self): ...
212
def trigger(self): ...
213
```
214
215
[Specialized Devices](./specialized-devices.md)
216
217
### Simulation and Testing
218
219
Mock devices and signals for testing, development, and offline work without requiring actual hardware connections.
220
221
```python { .api }
222
class SynSignal(Signal):
223
def __init__(self, *, name, func=None, **kwargs): ...
224
225
class SynAxis(Device):
226
def __init__(self, *, name, **kwargs): ...
227
def move(self, position, **kwargs): ...
228
229
class SynGauss(Device):
230
def __init__(self, name, motor, motor_field, center, Imax, sigma=1, noise='poisson', noise_multiplier=1, random_state=None, **kwargs): ...
231
232
class FakeEpicsSignal(Signal):
233
def __init__(self, read_pv, write_pv=None, **kwargs): ...
234
```
235
236
[Simulation and Testing](./simulation-testing.md)
237
238
### Flyers and Continuous Scanning
239
240
Interfaces for continuous scanning and fly scanning where data is collected while motors are in motion.
241
242
```python { .api }
243
class FlyerInterface:
244
def kickoff(self): ...
245
def complete(self): ...
246
def collect(self): ...
247
def describe_collect(self): ...
248
249
class MonitorFlyerMixin:
250
def kickoff(self): ...
251
def complete(self): ...
252
def collect(self): ...
253
```
254
255
[Flyers and Continuous Scanning](./flyers-continuous-scanning.md)
256
257
### Units and Conversions
258
259
Support for unit-aware signals and unit conversions.
260
261
```python { .api }
262
class UnitConversionDerivedSignal(DerivedSignal):
263
def __init__(self, derived_from, original_units, derived_units, **kwargs): ...
264
```
265
266
### Callbacks and Event Publishing
267
268
Event publishing for integration with data acquisition systems.
269
270
```python { .api }
271
class UidPublish:
272
def __init__(self, RE, stream_name='primary'): ...
273
274
class LastUidPublish:
275
def __init__(self, RE): ...
276
```
277
278
## Utility Functions
279
280
Device management and configuration utilities for connection handling, logging, and instance management.
281
282
```python { .api }
283
# Connection management
284
def wait_for_lazy_connection(): ...
285
def do_not_wait_for_lazy_connection(): ...
286
287
# Instance management
288
def register_instances_in_weakset(device_class): ...
289
def register_instances_keyed_on_name(device_class): ...
290
def select_version(name, version): ...
291
292
# Setup and configuration
293
def setup_ophyd(logger=None): ...
294
def set_handler(handler=None): ...
295
296
# Component utilities
297
def kind_context(kind): ...
298
ALL_COMPONENTS: frozenset # Set of all component classes
299
```
300
301
## Error Handling
302
303
Ophyd defines several exception types for different error conditions:
304
305
```python { .api }
306
class OpException(Exception): ...
307
class ReadOnlyError(OpException): ...
308
class LimitError(OpException): ...
309
class DisconnectedError(OpException): ...
310
class StatusTimeoutError(Exception): ...
311
class WaitTimeoutError(Exception): ...
312
```
313
314
Common error handling patterns:
315
316
```python
317
from ophyd.utils.errors import DisconnectedError, StatusTimeoutError
318
319
try:
320
status = device.set(new_value)
321
wait(status, timeout=10.0)
322
except DisconnectedError:
323
print("Device is not connected")
324
except StatusTimeoutError:
325
print("Operation timed out")
326
```
327
328
## Types
329
330
```python { .api }
331
from enum import IntFlag
332
333
class Kind(IntFlag):
334
"""Flags for indicating the kind of reading."""
335
omitted = 0b000
336
normal = 0b001
337
config = 0b010
338
hinted = 0b101
339
340
class Staged(Enum):
341
"""Enum for device staging states."""
342
no = 'no'
343
yes = 'yes'
344
partially = 'partially'
345
346
# Type aliases
347
Reading = Dict[str, Dict[str, Any]] # {'signal_name': {'value': val, 'timestamp': ts}}
348
Configuration = Dict[str, Dict[str, Any]] # Same structure as Reading
349
Description = Dict[str, Dict[str, Any]] # {'signal_name': {'dtype': 'number', 'shape': []}}
350
```