Skip to content

Commit c718d33

Browse files
authored
Merge pull request #22 from jennmald/sprint2
Sprint 2: HDCM, HPM, VPM, and screens
2 parents e0429b7 + dd1c3d0 commit c718d33

4 files changed

Lines changed: 349 additions & 46 deletions

File tree

src/cditools/motors.py

Lines changed: 136 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
11
from __future__ import annotations
22

3+
from typing import ClassVar
4+
5+
import numpy as np
36
from ophyd import Component as Cpt # type: ignore[import-not-found]
4-
from ophyd import Device, EpicsMotor
7+
from ophyd import Device, EpicsMotor, PseudoPositioner, PseudoSingle
58
from ophyd import DynamicDeviceComponent as DDC
9+
from ophyd.pseudopos import (
10+
pseudo_position_argument,
11+
real_position_argument,
12+
)
13+
14+
15+
class EpicsMotorRO(EpicsMotor):
16+
def __init__(self, *args, **kwargs):
17+
super().__init__(*args, **kwargs)
18+
19+
def move(self, *args, **kwargs): # noqa: ARG002
20+
msg = f"{self.name} is read-only and cannot be moved."
21+
raise PermissionError(msg)
22+
23+
def stop(self, *args, **kwargs): # noqa: ARG002
24+
msg = f"{self.name} is read-only and cannot be stopped manually."
25+
raise PermissionError(msg)
26+
27+
def set(self, *args, **kwargs): # noqa: ARG002
28+
msg = f"{self.name} is read-only and cannot be set."
29+
raise PermissionError(msg)
30+
31+
def set_position(self, *args, **kwargs): # noqa: ARG002
32+
msg = f"{self.name} is read-only and its position cannot be set."
33+
raise PermissionError(msg)
34+
35+
def _readonly_put(self, *args, **kwargs): # noqa: ARG002
36+
msg = f"{self.name} is read-only and cannot write PVs."
37+
raise PermissionError(msg)
638

739

840
class DM1(Device):
@@ -23,12 +55,100 @@ class DM1(Device):
2355
filt = Cpt(EpicsMotor, "Fltr:DM1-Ax:Y}Mtr")
2456

2557

58+
class DMM(Device):
59+
h = Cpt(EpicsMotor, "Mono:DMM-Ax:TX}Mtr")
60+
v = Cpt(EpicsMotor, "Mono:DMM-Ax:TY}Mtr")
61+
bragg = Cpt(EpicsMotor, "Mono:DMM-Ax:Bragg}Mtr")
62+
mlm1 = DDC(
63+
{
64+
"r": (EpicsMotor, "Mono:DMM-Ax:Roll}Mtr", {}),
65+
"fr": (EpicsMotor, "Mono:DMM-Ax:FR}Mtr", {}),
66+
}
67+
)
68+
mgap = Cpt(EpicsMotor, "Mono:DMM-Ax:HG}Mtr")
69+
mlm2 = DDC(
70+
{
71+
"p": (EpicsMotor, "Mono:DMM-Ax:Pitch}Mtr", {}),
72+
"fp": (EpicsMotor, "Mono:DMM-Ax:FP}Mtr", {}),
73+
}
74+
)
75+
zoff = Cpt(EpicsMotor, "Mono:DMM-Ax:TZ}Mtr")
76+
77+
78+
class DCMBase(Device):
79+
pitch = Cpt(EpicsMotor, "Mono:HDCM-Ax:Pitch}Mtr")
80+
fine: ClassVar[dict] = {
81+
"fpitch": Cpt(EpicsMotor, "Mono:HDCM-Ax:FP}Mtr"),
82+
"roll": Cpt(EpicsMotor, "Mono:HDCM-Ax:Roll}Mtr"),
83+
}
84+
h = Cpt(EpicsMotor, "Mono:HDCM-Ax:TX}Mtr")
85+
v = Cpt(EpicsMotor, "Mono:HDCM-Ax:TY}Mtr")
86+
87+
88+
class Energy(PseudoPositioner):
89+
bragg = Cpt(EpicsMotor, "Mono:HDCM-Ax:Bragg}Mtr")
90+
cgap = Cpt(EpicsMotor, "Mono:HDCM-Ax:HG}Mtr")
91+
# Synthetic Axis
92+
energy = Cpt(PseudoSingle, egu="KeV")
93+
94+
# Energy "limits"
95+
_low = 5.0 # TODO: CHECK THIS VALUE
96+
_high = 15.0 # TODO: CHECK THIS VALUE
97+
98+
# Set up constants
99+
Xoffset = 20.0 # mm
100+
d_111 = 3.1286911960950756
101+
ANG_OVER_KEV = 12.3984
102+
103+
def __init__(self, *args, **kwargs):
104+
super().__init__(*args, **kwargs)
105+
self.energy.readback.name = "energy"
106+
self.energy.setpoint.name = "energy_setpoint"
107+
108+
def energy_to_positions(self, target_energy: float):
109+
"""Compute undulator and mono positions given a target energy
110+
111+
Parameters
112+
----------
113+
target_energy : float
114+
Target energy in keV
115+
116+
Returns
117+
-------
118+
bragg : float
119+
The angle to set the monocromotor in radians
120+
gap : float
121+
The gap position in millimeters
122+
"""
123+
124+
# Calculate Bragg RBV
125+
bragg = np.arcsin((self.ANG_OVER_KEV / target_energy) / (2 * self.d_111))
126+
127+
# Calculate C2X
128+
gap = self.Xoffset / 2 / np.cos(bragg)
129+
130+
return bragg, gap
131+
132+
@pseudo_position_argument
133+
def forward(self, p_pos):
134+
energy = p_pos.energy # energy assumed in keV
135+
bragg, gap = self.energy_to_positions(energy)
136+
return self.RealPosition(bragg=np.rad2deg(bragg), cgap=gap)
137+
138+
@real_position_argument
139+
def inverse(self, r_pos):
140+
bragg = np.deg2rad(r_pos.bragg)
141+
e = self.ANG_OVER_KEV / (2 * self.d_111 * np.sin(bragg))
142+
return self.PseudoPosition(energy=float(e))
143+
144+
26145
class VPM(Device):
27146
fs = DDC(
28147
{
29148
"y": (EpicsMotor, "FS:VPM-Ax:Y}Mtr", {}),
30149
}
31150
)
151+
32152
slit = DDC(
33153
{
34154
"hg": (EpicsMotor, "Slt:VPM-Ax:HG}Mtr", {}),
@@ -45,11 +165,14 @@ class VPM(Device):
45165
"p": (EpicsMotor, "Mir:VPM-Ax:Pitch}Mtr", {}),
46166
"r": (EpicsMotor, "Mir:VPM-Ax:Roll}Mtr", {}),
47167
"y": (EpicsMotor, "Mir:VPM-Ax:TY}Mtr", {}),
48-
"x": (EpicsMotor, "Mir:VPM-Ax:TX}Mtr", {}),
49-
"us_b": (EpicsMotor, "Mir:VPM-Ax:UB}Mtr", {}),
50-
"ds_b": (EpicsMotor, "Mir:VPM-Ax:DB}Mtr", {}),
51-
"bend": (EpicsMotor, "Mir:VPM-Ax:Bnd}Mtr", {}),
52-
"bend_off": (EpicsMotor, "Mir:VPM-Ax:BndOff}Mtr", {}),
168+
"x": (EpicsMotorRO, "Mir:VPM-Ax:TX}Mtr", {}),
169+
"yaw": (EpicsMotorRO, "Mir:VPM-Ax:Yaw}Mtr", {}),
170+
"us_lt": (EpicsMotorRO, "Mir:VPM-Ax:XU}Mtr", {}),
171+
"ds_lt": (EpicsMotorRO, "Mir:VPM-Ax:XD}Mtr", {}),
172+
"us_b": (EpicsMotorRO, "Mir:VPM-Ax:UB}Mtr", {}),
173+
"ds_b": (EpicsMotorRO, "Mir:VPM-Ax:DB}Mtr", {}),
174+
"bend": (EpicsMotorRO, "Mir:VPM-Ax:Bnd}Mtr", {}),
175+
"bend_off": (EpicsMotorRO, "Mir:VPM-Ax:BndOff}Mtr", {}),
53176
}
54177
)
55178

@@ -60,6 +183,7 @@ class HPM(Device):
60183
"y": (EpicsMotor, "FS:HPM-Ax:Y}Mtr", {}),
61184
}
62185
)
186+
63187
slit = DDC(
64188
{
65189
"hg": (EpicsMotor, "Slt:HPM-Ax:HG}Mtr", {}),
@@ -76,14 +200,16 @@ class HPM(Device):
76200
"p": (EpicsMotor, "Mir:HPM-Ax:Pitch}Mtr", {}),
77201
"r": (EpicsMotor, "Mir:HPM-Ax:Roll}Mtr", {}),
78202
"y": (EpicsMotor, "Mir:HPM-Ax:TY}Mtr", {}),
79-
"us_b": (EpicsMotor, "Mir:HPM-Ax:UB}Mtr", {}),
80-
"ds_b": (EpicsMotor, "Mir:HPM-Ax:DB}Mtr", {}),
81203
"bend": (EpicsMotor, "Mir:HPM-Ax:Bnd}Mtr", {}),
82204
"bend_off": (EpicsMotor, "Mir:HPM-Ax:BndOff}Mtr", {}),
83205
"us_x": (EpicsMotor, "Mir:HPM-Ax:XU}Mtr", {}),
84206
"ds_x": (EpicsMotor, "Mir:HPM-Ax:XD}Mtr", {}),
85207
"x": (EpicsMotor, "Mir:HPM-Ax:TX}Mtr", {}),
86-
"yaw": (EpicsMotor, "Mir:HPM-Ax:Yaw}Mtr", {}),
208+
"yaw": (EpicsMotorRO, "Mir:HPM-Ax:Yaw}Mtr", {}),
209+
"us_lt": (EpicsMotorRO, "Mir:HPM-Ax:XU}Mtr", {}),
210+
"ds_lt": (EpicsMotorRO, "Mir:HPM-Ax:XD}Mtr", {}),
211+
"us_b": (EpicsMotorRO, "Mir:HPM-Ax:UB}Mtr", {}),
212+
"ds_b": (EpicsMotorRO, "Mir:HPM-Ax:DB}Mtr", {}),
87213
}
88214
)
89215

@@ -97,40 +223,6 @@ class DM2(Device):
97223
foil = Cpt(EpicsMotor, "IM:DM2-Ax:Y}Mtr")
98224

99225

100-
class DMM(Device):
101-
h = Cpt(EpicsMotor, "Mono:DMM-Ax:TX}Mtr")
102-
v = Cpt(EpicsMotor, "Mono:DMM-Ax:TY}Mtr")
103-
bragg = Cpt(EpicsMotor, "Mono:DMM-Ax:Bragg}Mtr")
104-
mlm1 = DDC(
105-
{
106-
"r": (EpicsMotor, "Mono:DMM-Ax:Roll}Mtr", {}),
107-
"fr": (EpicsMotor, "Mono:DMM-Ax:FR}Mtr", {}),
108-
}
109-
)
110-
mgap = Cpt(EpicsMotor, "Mono:DMM-Ax:HG}Mtr")
111-
mlm2 = DDC(
112-
{
113-
"p": (EpicsMotor, "Mono:DMM-Ax:Pitch}Mtr", {}),
114-
"fp": (EpicsMotor, "Mono:DMM-Ax:FP}Mtr", {}),
115-
}
116-
)
117-
zoff = Cpt(EpicsMotor, "Mono:DMM-Ax:TZ}Mtr")
118-
119-
120-
class DCM(Device):
121-
h = Cpt(EpicsMotor, "Mono:HDCM-Ax:TX}Mtr")
122-
v = Cpt(EpicsMotor, "Mono:HDCM-Ax:TY}Mtr")
123-
bragg = Cpt(EpicsMotor, "Mono:HDCM-Ax:Bragg}Mtr")
124-
cgap = Cpt(EpicsMotor, "Mono:HDCM-Ax:HG}Mtr")
125-
c2 = DDC(
126-
{
127-
"p": (EpicsMotor, "Mono:HDCM-Ax:Pitch}Mtr", {}),
128-
"r": (EpicsMotor, "Mono:HDCM-Ax:Roll}Mtr", {}),
129-
"fp": (EpicsMotor, "Mono:HDCM-Ax:FP}Mtr", {}),
130-
}
131-
)
132-
133-
134226
class DM3(Device):
135227
slit = DDC(
136228
{
@@ -182,7 +274,7 @@ class VKB(Device):
182274
)
183275
fs = DDC(
184276
{
185-
"y": (EpicsMotor, "FS:KBv-Ax:FS}Mtr", {}),
277+
"y": (EpicsMotor, "Mir:KBv-Ax:FS}Mtr", {}),
186278
}
187279
)
188280

0 commit comments

Comments
 (0)