Diffractometer Extra Motors and/or Pseudos#
Sometimes, it is desired to add additional ophyd components to a diffractometer object. Such components could include additional motor axes, azimuthal reference vectors, temperature, etc.
Objective
Add one or more real positioners to the standard positioners of the 2-circle diffractometer (tth_tth, TH TTH Q geometry). Use simulated motors for the example (no EPICS required).
Standard 2-circle#
First, we start with the setup of a 2-circle ($\theta:2\theta$) diffractometer.
axis |
space |
---|---|
th |
real |
tth |
real |
q |
pseudo |
Create the diffractometer object.
import hklpy2
th2th = hklpy2.creator(name="th2th", geometry="TH TTH Q", solver="th_tth")
Show the diffractometer configuration and all the ophyd components.
th2th.wh()
print(f"{th2th.component_names=}")
q=0
wavelength=1.0
th=0, tth=0
th2th.component_names=('geometry', 'solver', 'wavelength', 'q', 'th', 'tth')
Add additional positioner#
We can use the hklpy2.creator()
for the additional positioner. Since we are not using the default reals
, we’ll provide a Python dictionary that defines each real axis, in order, and whether it uses a simulated motor or an EPICS PV. None
means to use a simulated motor.
th2th = hklpy2.creator(
name="th2th",
geometry="TH TTH Q",
solver="th_tth",
reals=dict(th=None, tth=None, spinner=None)
)
th2th.wh()
print(f"{th2th.component_names=}")
q=0
wavelength=1.0
th=0, tth=0
th2th.component_names=('geometry', 'solver', 'wavelength', 'q', 'th', 'tth', 'spinner')
Compare these results. The new result adds the spinner
axis.
Set (and show) the limits on the spinner:
th2th.spinner._limits = -10_000, 10_000
th2th.spinner.limits
(-10000, 10000)
Can we add other pseudo axes?#
Q: With this capability to add additional real positioners, can we add axes to the pseudo positioners?
A: Yes. See this example. It defines two pseudo axes: Q
and d
. As shown in aliases
, the Q
axis is mapped to the solver q
axis.
th2th = hklpy2.creator(
name="th2th",
geometry="TH TTH Q",
solver="th_tth",
pseudos=["Q", "d"], #
reals=dict(sample=None, detector=None, spinner=None),
aliases=dict(pseudos=["Q"], reals=["sample", "detector"]),
)
th2th.wh()
Q=0
wavelength=1.0
sample=0, detector=0
Add additional Signals and Devices#
Finally, we add additional Signals and Component Devices as a demonstration.
The {func}function]~hklpy2.diffract.creator()
has its limits. The creator()
relies on a {func}function]~hklpy2.diffract.diffractometer_class_factory()
.
Let’s skip the factory function and show how to build a structure directly.
Demonstrate a variety of additional components.
from hklpy2.diffract import Hklpy2PseudoAxis
from ophyd import Component, Device, Signal, SoftPositioner
from ophyd.signal import SignalRO
class XYStage(Device):
x = Component(SoftPositioner, kind="hinted", limits=(-20, 105), init_pos=0)
y = Component(SoftPositioner, kind="hinted", limits=(-20, 105), init_pos=0)
solenoid_lock = Component(Signal, value=True, kind="normal")
class MyTwoCircle(hklpy2.DiffractometerBase):
_real = ["th", "tth"]
q = Component(Hklpy2PseudoAxis, "", kind="hinted")
th = Component(
SoftPositioner, kind="hinted", limits=(-180, 180), egu="degrees", init_pos=0
)
tth = Component(
SoftPositioner, kind="hinted", limits=(-180, 180), egu="degrees", init_pos=0
)
spinner = Component(
SoftPositioner,
kind="hinted",
limits=(-10000, 10000),
egu="rotations",
init_pos=0,
)
atth = Component(
SoftPositioner, kind="hinted", limits=(-180, 180), egu="degrees", init_pos=0
)
temperature = Component(SignalRO, value=25, kind="normal")
xy = Component(XYStage, kind="normal")
def __init__(self, *args, **kwargs):
super().__init__(
*args,
solver="th_tth", # solver name
geometry="TH TTH Q", # solver geometry
**kwargs,
)
th2tth = MyTwoCircle(name="th2tth")
th2tth.wh() # brief report of diffractometer position
# th2th.summary() # show the full ophyd structure summary
th2tth.read()
q=0
wavelength=1.0
th=0, tth=0
OrderedDict([('th2tth_q', {'value': 0, 'timestamp': 1743011146.56252}),
('th2tth_q_setpoint',
{'value': 0, 'timestamp': 1743011146.5625339}),
('th2tth_th', {'value': 0, 'timestamp': 1743011146.5971136}),
('th2tth_tth', {'value': 0, 'timestamp': 1743011146.5971181}),
('th2tth_spinner', {'value': 0, 'timestamp': 1743011146.5971215}),
('th2tth_atth', {'value': 0, 'timestamp': 1743011146.5971239}),
('th2tth_temperature',
{'value': 25, 'timestamp': 1743011146.5629923}),
('th2tth_xy_x', {'value': 0, 'timestamp': 1743011146.5971382}),
('th2tth_xy_y', {'value': 0, 'timestamp': 1743011146.597141}),
('th2tth_xy_solenoid_lock',
{'value': True, 'timestamp': 1743011146.563218})])