Example 4-circle diffractometer custom Python class#
It’s always possible to define your own subclass of
DiffractometerBase()
when you need more control than
provided by creator()
.
Here’s a brief example of a 4-circle diffractometer with a custom Python class. Add many additional axes, both in real (rotation angle) space and in reciprocal (pseudo) space.
import hklpy2
from hklpy2.diffract import Hklpy2PseudoAxis
from ophyd import Component as Cpt
from ophyd import EpicsMotor
from ophyd import Kind
from ophyd import SoftPositioner
NORMAL_HINTED = Kind.hinted | Kind.normal
class Fourc(hklpy2.DiffractometerBase):
"""Test case."""
# Pseudo-space axes, in order expected by hkl_soleil E4CV, engine="hkl"
h = Cpt(Hklpy2PseudoAxis, "", kind=NORMAL_HINTED) # noqa: E741
k = Cpt(Hklpy2PseudoAxis, "", kind=NORMAL_HINTED) # noqa: E741
l = Cpt(Hklpy2PseudoAxis, "", kind=NORMAL_HINTED) # noqa: E741
# Real-space axes, in our own order..
# Use different names than the solver for some axes
ttheta = Cpt(EpicsMotor, "m29", kind=NORMAL_HINTED)
theta = Cpt(EpicsMotor, "m30", kind=NORMAL_HINTED)
chi = Cpt(EpicsMotor, "m31", kind=NORMAL_HINTED)
phi = Cpt(EpicsMotor, "m32", kind=NORMAL_HINTED)
# Pseudo-space extra axes used in a couple modes.
h2 = Cpt(Hklpy2PseudoAxis, "", kind=NORMAL_HINTED) # noqa: E741
k2 = Cpt(Hklpy2PseudoAxis, "", kind=NORMAL_HINTED) # noqa: E741
l2 = Cpt(Hklpy2PseudoAxis, "", kind=NORMAL_HINTED) # noqa: E741
# real-space extra axis used in a couple modes
psi = Cpt(SoftPositioner, limits=(-170, 170), init_pos=0, kind=NORMAL_HINTED)
# another Component, not used (yet)
energy = Cpt(SoftPositioner, limits=(5, 35), init_pos=12.4, kind=NORMAL_HINTED)
# Just the axes in expected order by hkl_soleil E4CV.
_pseudo = "h k l".split()
_real = "theta chi phi ttheta".split()
def __init__(self, *args, **kwargs):
super().__init__(
*args,
solver="hkl_soleil",
geometry="E4CV",
solver_kwargs=dict(engine="hkl"),
pseudos="h k l".split(),
reals="theta chi phi ttheta".split(),
**kwargs,
)
fourc = Fourc("gp:", name="fourc")
fourc.wait_for_connection() # Recommended when connecting to control system.
fourc
Fourc(prefix='gp:', name='fourc', settle_time=0.0, timeout=None, egu='', limits=(0, 0), source='computed', read_attrs=['h', 'h.readback', 'h.setpoint', 'k', 'k.readback', 'k.setpoint', 'l', 'l.readback', 'l.setpoint', 'ttheta', 'ttheta.user_readback', 'ttheta.user_setpoint', 'theta', 'theta.user_readback', 'theta.user_setpoint', 'chi', 'chi.user_readback', 'chi.user_setpoint', 'phi', 'phi.user_readback', 'phi.user_setpoint', 'h2', 'h2.readback', 'h2.setpoint', 'k2', 'k2.readback', 'k2.setpoint', 'l2', 'l2.readback', 'l2.setpoint', 'psi', 'energy'], configuration_attrs=['geometry', 'solver', 'wavelength', 'h', 'k', 'l', 'ttheta', 'ttheta.user_offset', 'ttheta.user_offset_dir', 'ttheta.velocity', 'ttheta.acceleration', 'ttheta.motor_egu', 'theta', 'theta.user_offset', 'theta.user_offset_dir', 'theta.velocity', 'theta.acceleration', 'theta.motor_egu', 'chi', 'chi.user_offset', 'chi.user_offset_dir', 'chi.velocity', 'chi.acceleration', 'chi.motor_egu', 'phi', 'phi.user_offset', 'phi.user_offset_dir', 'phi.velocity', 'phi.acceleration', 'phi.motor_egu', 'h2', 'k2', 'l2'], concurrent=True)
print("Brief 'where' report:")
fourc.wh()
Brief 'where' report:
h=0, k=0, l=0
wavelength=1.0
theta=0, chi=0, phi=0, ttheta=0
print("Full 'where' report:")
fourc.wh(full=True)
Full 'where' report:
diffractometer='fourc'
HklSolver(name='hkl_soleil', version='5.1.2', geometry='E4CV', engine_name='hkl', mode='bissector')
Sample(name='sample', lattice=Lattice(a=1, system='cubic'))
Orienting reflections: []
U=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]
UB=[[6.283185307179586, 0.0, 0.0], [0.0, 6.283185307179586, 0.0], [0.0, 0.0, 6.283185307179586]]
constraint: -180.0 <= theta <= 180.0
constraint: -180.0 <= chi <= 180.0
constraint: -180.0 <= phi <= 180.0
constraint: -180.0 <= ttheta <= 180.0
h=0, k=0, l=0
wavelength=1.0
theta=0, chi=0, phi=0, ttheta=0
Use the lower level methods to compute forward()
and inverse()
transformations.
fourc.forward(1, 0, 0) # Shows the default choice.
FourcRealPos(theta=29.999992418917, chi=1.3051987e-05, phi=89.999997321112, ttheta=59.999984837834)
fourc.core.forward(dict(h=1, k=0, l=0)) # Shows ALL the possibilities.
[FourcRealPos(theta=29.999992418917, chi=1.3051987e-05, phi=89.999997321112, ttheta=59.999984837834),
FourcRealPos(theta=-29.999992418917, chi=-1.3051987e-05, phi=-90.000002678888, ttheta=-59.999984837834),
FourcRealPos(theta=-150.000007581083, chi=-1.3051987e-05, phi=-90.000002678888, ttheta=59.999984837834),
FourcRealPos(theta=29.999992418917, chi=179.999986948013, phi=-90.000002678888, ttheta=59.999984837834),
FourcRealPos(theta=-29.999992418917, chi=-179.999986948013, phi=89.999997321112, ttheta=-59.999984837834),
FourcRealPos(theta=-150.000007581083, chi=-179.999986948013, phi=89.999997321112, ttheta=59.999984837834)]
fourc.core.inverse(dict(ttheta=60, theta=30, chi=0, phi=90))
{'h': 1.000000229316, 'k': -2.28495e-07, 'l': -4.7217e-08}