11from __future__ import annotations
22
3+ from typing import ClassVar
4+
5+ import numpy as np
36from ophyd import Component as Cpt # type: ignore[import-not-found]
4- from ophyd import Device , EpicsMotor
7+ from ophyd import Device , EpicsMotor , PseudoPositioner , PseudoSingle
58from 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
840class 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+
26145class 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-
134226class 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