dls-dodal 1.63.0__py3-none-any.whl → 1.64.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dls-dodal
3
- Version: 1.63.0
3
+ Version: 1.64.0
4
4
  Summary: Ophyd devices and other utils that could be used across DLS beamlines
5
5
  Author-email: Dominic Oram <dominic.oram@diamond.ac.uk>, Joseph Ware <joseph.ware@diamond.ac.uk>, Oliver Silvester <Oliver.Silvester@diamond.ac.uk>, Noemi Frisina <noemi.frisina@diamond.ac.uk>
6
6
  License: Apache License
@@ -215,7 +215,7 @@ Description-Content-Type: text/markdown
215
215
  License-File: LICENSE
216
216
  Requires-Dist: click
217
217
  Requires-Dist: ophyd
218
- Requires-Dist: ophyd-async[ca,pva]>=0.13.2
218
+ Requires-Dist: ophyd-async[ca,pva]>=0.13.5
219
219
  Requires-Dist: bluesky>=1.14.5
220
220
  Requires-Dist: pyepics
221
221
  Requires-Dist: dataclasses-json
@@ -245,7 +245,7 @@ Requires-Dist: pipdeptree; extra == "dev"
245
245
  Requires-Dist: pre-commit; extra == "dev"
246
246
  Requires-Dist: psutil; extra == "dev"
247
247
  Requires-Dist: pydata-sphinx-theme>=0.12; extra == "dev"
248
- Requires-Dist: pyright; extra == "dev"
248
+ Requires-Dist: pyright==1.1.406; extra == "dev"
249
249
  Requires-Dist: pytest; extra == "dev"
250
250
  Requires-Dist: pytest-asyncio; extra == "dev"
251
251
  Requires-Dist: pytest-cov; extra == "dev"
@@ -1,8 +1,8 @@
1
- dls_dodal-1.63.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1
+ dls_dodal-1.64.0.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
2
2
  dodal/__init__.py,sha256=Ksms_WJF8LTkbm38gEpm1jBpGqcQ8NGvmb2ZJlOE1j8,198
3
3
  dodal/__main__.py,sha256=kP2S2RPitnOWpNGokjZ1Yq-1umOtp5sNOZk2B3tBPLM,111
4
- dodal/_version.py,sha256=Tsj3Gv5EK3mUIrqNTysAbLuokwxjHkq88TyXB8FbM30,706
5
- dodal/cli.py,sha256=HzGJEnFzAPRiCTLVXu8R2PP4q2C-kbPsGDbcid9oEMU,4184
4
+ dodal/_version.py,sha256=_zrl8M91Rnv6DVBIBxk-gZepOz8nYyzbsC1kzAgyxxY,706
5
+ dodal/cli.py,sha256=NJopu78ebawAL3qNSvOoxMpyzcejVRCh9qw2lRpEJZw,4199
6
6
  dodal/log.py,sha256=UckmmyY_SdZePyi5lHnjh-DVw6qvnat3ANa_5-y80Og,9877
7
7
  dodal/utils.py,sha256=abGitd4FLpLnmckF7lUqOKYUL88r5Ex_NGSVgO4gOf4,19305
8
8
  dodal/beamline_specific_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -24,23 +24,24 @@ dodal/beamlines/i03.py,sha256=-CkU60NXyXVjTjDqwkSWugu6MbG4r8mkwF6ax7WVP-I,16561
24
24
  dodal/beamlines/i04.py,sha256=QyprATHFxnw_cP2gak1i2_ywj-i26vrGlvOYM6RmyTY,13758
25
25
  dodal/beamlines/i05.py,sha256=v4QKd8-neh4Og205oovm6NDRnAU6Oktu1WrxalXsI40,656
26
26
  dodal/beamlines/i05_1.py,sha256=R6JFFg8Bj-Izw355mx3mOd4IDvJb5ipB4p7_S0I_4Z0,670
27
- dodal/beamlines/i09.py,sha256=VYr-cPmaSoLs0dkuP9jDBtiP-lunuggQVpIEziJG9ZM,1839
28
- dodal/beamlines/i09_1.py,sha256=5GLvhiaSmbHOvAx4pyuE82Y4seizz9hIAC9tReGKWho,1521
29
- dodal/beamlines/i09_2.py,sha256=lyYO1rOaIsXNuVOL39Psh-8jQjkhQBXEQMpbXa-Zcw0,713
27
+ dodal/beamlines/i07.py,sha256=So3qlieBwLufsy-yei-Fj4zLKPhm6swUN3aHDQQWzpM,634
28
+ dodal/beamlines/i09.py,sha256=OOf7Yf4q3Pu0q_MsczxTCdNkr5A-pqNRsrQb6BxZyA8,1839
29
+ dodal/beamlines/i09_1.py,sha256=0aoUgirXT6qNtc8fN7FzG0fwuQcVmGH8dDtoLWVj_RM,1521
30
+ dodal/beamlines/i09_2.py,sha256=iut2-ADQn7r4rpHo4ycCsE4dczm4VKYxabzwWQNvcwg,1358
30
31
  dodal/beamlines/i10.py,sha256=LC0ii13rVyZj1ziatBAyRoyuutdyyTCkpSvICGv4IGY,4059
31
32
  dodal/beamlines/i10_1.py,sha256=FxnWjr1u55XgIvnuyTfknnj-xPMNJOAtxObq1y9k518,1062
32
- dodal/beamlines/i10_optics.py,sha256=5g7Xx_NwVshy-KiJ8fyDH6e69Q9I2Wlk9AuUvCgcQGU,5508
33
+ dodal/beamlines/i10_optics.py,sha256=u0eDHQDGteh1IXGOesDP66WfMsYn2mCXerIbYpB1bE4,5888
33
34
  dodal/beamlines/i11.py,sha256=hzW5Lh74zDzUD0vqXQzekoFVWDp8qEIc0OPRm0Qepqs,3643
34
35
  dodal/beamlines/i13_1.py,sha256=VYVqMN8-njy7YSI08gskRccT-K2paRC9edAx0ah-Cwo,1602
35
36
  dodal/beamlines/i15.py,sha256=lmUhh1jt5CtD4P_BzkwNufu814aT3uaQlLyDMY_B5AE,5725
36
37
  dodal/beamlines/i15_1.py,sha256=EKg6BCr3wpHxdmG23kSTZYcNnGQxZbkzzulBLwz_Kew,3903
37
- dodal/beamlines/i17.py,sha256=A3flivdYV-XH5mjTab5t9gp9YKUY-2C9Bk2cpgYxh2o,2361
38
+ dodal/beamlines/i17.py,sha256=HwQyD15gztKWbwjszE_Vv8cm44IKc7vwEq_oY3RzpJQ,2487
38
39
  dodal/beamlines/i18.py,sha256=JAGk4Z1Q0fFHIClWDGGQSKcIhZZ14oKF6gyCC-Xspe0,3741
39
40
  dodal/beamlines/i19_1.py,sha256=4-jU-JiF1J_aunoMW5GsHXE-R2l1xaXxmPH_89_D3IM,3093
40
41
  dodal/beamlines/i19_2.py,sha256=8dehUVBjOe_jHBP_il-RZpMc8hZsPWHx9aE_zYCRJ4s,3630
41
42
  dodal/beamlines/i19_optics.py,sha256=fNdAFRJYFA045l4giGI3V68qijDFE1C8gdvD6fncAio,1181
42
43
  dodal/beamlines/i20_1.py,sha256=Zsr1lsH7ySbOgK7RhMVMWzNWZAV-fuYW0iAjSEJZicY,2625
43
- dodal/beamlines/i21.py,sha256=5v6iiTlY4kWlWvQ_uNidJSotvkdNF3qdjR49l7sIYPc,728
44
+ dodal/beamlines/i21.py,sha256=_XiMajHF739yfLOnme9hPyV_JfmA_iTY0iG2jeirgNI,1345
44
45
  dodal/beamlines/i22.py,sha256=3WYXequNzEkGIMEFgnI5axIYTH_kNcENVmoTj1mvyyI,8426
45
46
  dodal/beamlines/i23.py,sha256=ZXvPEiMA4mPbRTXOxvL1NcoVWDg4Deyl8k57cveDg90,3060
46
47
  dodal/beamlines/i24.py,sha256=0ZjJCUYIUWIQTPrXFqKviiTCZiyuJh4MkmqMYd9JZ9o,7454
@@ -70,12 +71,12 @@ dodal/common/beamlines/device_helpers.py,sha256=8sasAIFRDwo6ElHqLrXnpj_v7xcEg-29
70
71
  dodal/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
72
  dodal/devices/aperture.py,sha256=S7AoIoQg_kFxaQk7HUMijbm1kYkOEi2csbdj76kp1ys,427
72
73
  dodal/devices/aperturescatterguard.py,sha256=V3RFNq5BFeFPRK9ZEi4w2zEDpopkKvqBsdjY59PrIg0,13988
73
- dodal/devices/apple2_undulator.py,sha256=WFjoKFOMxi7XHcMLpwGET1ernWQm0dzrqXMdHqEikeY,25876
74
+ dodal/devices/apple2_undulator.py,sha256=zwSbioULT7R8D2wfpcq08nkI9ljqVgBG0-0h1sFT2EA,25952
74
75
  dodal/devices/backlight.py,sha256=y40kR6N3qSFVLRaGU8CjJJpwv_hq2QALf_85fLQqras,1415
75
76
  dodal/devices/baton.py,sha256=315I_0V73_DYYVT0PBs0luVy4CMqdPo0kLvHBi12MIU,606
76
77
  dodal/devices/bimorph_mirror.py,sha256=OGe6aCczG0gVco4OvIRLJVxn2kw5F2QG1e06uqhFLTw,4609
77
78
  dodal/devices/collimation_table.py,sha256=64HunSPJH-L2gZdfIj_RYdOlOuwRFEfMHfLHzu4BAKI,1681
78
- dodal/devices/common_dcm.py,sha256=dQAWsCIbXynTbVByH4g4j4JO7NFLNP1XWUsMr0fho7E,5288
79
+ dodal/devices/common_dcm.py,sha256=BmvwnT5mbCmSQI8aGl3GKEfpe7N1axUANEyG-NYgC98,5288
79
80
  dodal/devices/controllers.py,sha256=W_Ras1c6xLjcOMKPSHN8Z1eCUCH-ktaah2hzQXyuFLk,652
80
81
  dodal/devices/cryostream.py,sha256=2FxCGioEZNMHItsGm_rsnkRnHjwMUIwRMAX_x8odKIw,4678
81
82
  dodal/devices/diamond_filter.py,sha256=hySd7HnLdplpPNvBrLddLjO_3LqgD8-99Zr__Sy_GbI,689
@@ -125,7 +126,7 @@ dodal/devices/attenuator/filter_selections.py,sha256=r0TsT3Cylx0wQiBoCsU0Kk8eOup
125
126
  dodal/devices/b07/__init__.py,sha256=Zw4VkH-68MLoDveswDpR-lTlzK1-IeNz2W4qtkCO3Hs,109
126
127
  dodal/devices/b07/enums.py,sha256=GmS6YZEbFH2wEwS1Ni4VNG-2wT98v7NnCzbHqFKo5Pg,1415
127
128
  dodal/devices/b07_1/__init__.py,sha256=yInDRarid492g7unaL8zIrWSNpRXhgsj8z7TRTRJfjI,281
128
- dodal/devices/b07_1/ccmc.py,sha256=ZjbOjJN3Ckbkp4ZZLE48-j9CGXcfidujgdrSROr6TQM,2554
129
+ dodal/devices/b07_1/ccmc.py,sha256=SyxfR8Sd8C7Bx2q0cvItdxP2DOP-ZhbMuAfSvBrbSio,2554
129
130
  dodal/devices/b07_1/enums.py,sha256=TREiXDSAGFRXR3iUkKADYfm-CmhLmlpGHKZUOQ2Z1as,723
130
131
  dodal/devices/b16/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
132
  dodal/devices/b16/detector.py,sha256=saI2aFBhT0oTisam5b1mlaMJTRwGT7bHbqPGBP7SDnk,840
@@ -168,20 +169,24 @@ dodal/devices/i02_1/sample_motors.py,sha256=fAHAyeuP4hjOnYsp2x5VQNrTh8Di35ezJV1s
168
169
  dodal/devices/i02_2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
170
  dodal/devices/i03/__init__.py,sha256=Kvukapy4a5lUQ20qaCqYCJzKNaqJn2DfXP5nKZ_Pec8,118
170
171
  dodal/devices/i03/dcm.py,sha256=qK9qcPCedjOVMvKZ0TZaWNeyt7CJRsC-Wpjy44OQf4E,2336
171
- dodal/devices/i03/undulator_dcm.py,sha256=L7dr1QcwJ1SiQWkHm0a4yQVJFtPiAJQyDLEXok64Ab8,2857
172
+ dodal/devices/i03/undulator_dcm.py,sha256=HyeqOdq7qA7bqxw5giY4XvX3SLTOMwzbEKdTc5FQXzQ,2857
172
173
  dodal/devices/i04/__init__.py,sha256=Kvukapy4a5lUQ20qaCqYCJzKNaqJn2DfXP5nKZ_Pec8,118
173
174
  dodal/devices/i04/constants.py,sha256=_Dw28NeXldwRYH-h6YP6OHnyj7h0z4NQs_-RysNby5Q,281
174
175
  dodal/devices/i04/murko_results.py,sha256=6VtYDm0HXIBSyd-_v2O1G_6C1D-5fQ45FKMlNMDW--Y,9682
175
176
  dodal/devices/i04/transfocator.py,sha256=sVI4Bgv-2-DH4-F1nIXMp5Aktevrm3agZnCA-WgjmW8,3780
176
177
  dodal/devices/i05/__init__.py,sha256=v0Axsf-vMyLyxcbqS8lljJc0BQ5Tw8yVBTWcO3tG_4g,67
177
178
  dodal/devices/i05/enums.py,sha256=9FHBIexkSfZSTHxUnGupravfG9sVriHcrB15jemkQOQ,191
179
+ dodal/devices/i07/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
+ dodal/devices/i07/dcm.py,sha256=AJpBiLX6t_mAjGKw9LmjXIFG5IIe5Ploe5LxS-ZckT8,1250
178
181
  dodal/devices/i09/__init__.py,sha256=5AGY_SKtqrU7NeEbzu9ROazPD_C1fEci1LJfdcuz2VY,135
179
182
  dodal/devices/i09/enums.py,sha256=Rfs6OgEHSSxcH2UiVmlR_5wLwxDmcHonXds0UDVRfCc,551
180
183
  dodal/devices/i09_1/__init__.py,sha256=dTazun87AkMDXh7J8gIZYJxJth0XUs72qahJ-VvhRBI,72
181
184
  dodal/devices/i09_1/enums.py,sha256=FwhsvKnEcm9M22U4gcAJi6FE2RaUBZH0jRjNgJUKRqc,1319
185
+ dodal/devices/i09_1_shared/__init__.py,sha256=rHFCmNUYbca82BQ9lbcSvINFkwncUDxZciqa286-6is,137
186
+ dodal/devices/i09_1_shared/hard_undulator_functions.py,sha256=U5-xzejtseZZs9NbT_J-tDOOP_4OR1tWaGvttzmQeZc,4018
182
187
  dodal/devices/i10/__init__.py,sha256=Pa7gI_ulcPO3dN2xpw6tBN1E3CpV3Lj82kmGwIVU7TA,575
183
188
  dodal/devices/i10/diagnostics.py,sha256=ULSHpaRPVWcP-HgKAJW2rsdPoYZs_MgF6HJoPj39MwU,6339
184
- dodal/devices/i10/i10_apple2.py,sha256=iJSxqbTgZREkRyXShssaMaBSfQg0dUlPtQJa6iRPGFg,15953
189
+ dodal/devices/i10/i10_apple2.py,sha256=K2Xq6t5gxsvhYx85tWv43nDnnSdPBqsadaeXLn6TOA4,15970
185
190
  dodal/devices/i10/i10_setting_data.py,sha256=69XWgE-YNTiW7C3t67MNcTL5JDDhOo7h-X7DCTpFE5g,164
186
191
  dodal/devices/i10/mirrors.py,sha256=Zofd0g2f3YvFi_KIHQE_w4dFZNglhmh--65tRnWLuyY,504
187
192
  dodal/devices/i10/slits.py,sha256=pBstXJnA6j3ftDqei0EZyAPU--M_heqo6hm2DEOqPdc,5040
@@ -199,7 +204,7 @@ dodal/devices/i13_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
199
204
  dodal/devices/i13_1/merlin.py,sha256=mgTFSMJftRzLL-HXAUuJkOYxtyA3Rp8YX0L46JCb30Y,1019
200
205
  dodal/devices/i13_1/merlin_controller.py,sha256=myfmByOEXyMrlJZfsjOxDHeGQVwZGfsRtzrfSy2001o,1495
201
206
  dodal/devices/i15/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
202
- dodal/devices/i15/dcm.py,sha256=NvDj0zne1MFISy8xfyx3-oPT0eKVlaAn7gBrG5YfLo4,1337
207
+ dodal/devices/i15/dcm.py,sha256=S9kZaImMUUYDDycP_a7qJWE6UzU1JUq2kcbH_-jmuZU,1337
203
208
  dodal/devices/i15/focussing_mirror.py,sha256=E6T_c7M2osgHLa7u8eUfQJlXlJbZHtOp95FhP3qZ4gY,1739
204
209
  dodal/devices/i15/jack.py,sha256=VafCNx-uqkIy0LxbBAhSm_tuC8_SbGCrnTbvQCExAzA,962
205
210
  dodal/devices/i15/laue.py,sha256=H0nLPH8gqJejBZtZeY0lv84EaE2lqdL3CmXvT9iHhpk,496
@@ -225,7 +230,7 @@ dodal/devices/i20_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
225
230
  dodal/devices/i21/__init__.py,sha256=1H0Ov9s8K7nu6e20WtQDH39wgSKWz2ChRVAUzytIyzQ,67
226
231
  dodal/devices/i21/enums.py,sha256=asy90S84fBBydYwz89jNeocz04JXpbHn8ynaPzW353c,141
227
232
  dodal/devices/i22/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
228
- dodal/devices/i22/dcm.py,sha256=PfDFJOQ2K3KXS2vFnTcLlwxSbsnsOVhnYbkpD-LcsAc,4551
233
+ dodal/devices/i22/dcm.py,sha256=abs6U6DOUO8_g2adpynfoc9lQYbLC3tgObSwI9AhKZg,4551
229
234
  dodal/devices/i22/fswitch.py,sha256=kpgegs4Wv_weBSzbrlXLXqXOOZdzTn3X9k5PlEN5F6c,2881
230
235
  dodal/devices/i22/nxsas.py,sha256=M6hhiPmHO4WDBsFoSOhKAG_yPF6vTpLRPbB2UW3-aMs,6122
231
236
  dodal/devices/i24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -267,7 +272,7 @@ dodal/devices/temperture_controller/lakeshore/lakeshore_io.py,sha256=OSvJv-vtWIo
267
272
  dodal/devices/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
268
273
  dodal/devices/util/adjuster_plans.py,sha256=c40PFZpXFw0YmJLh9jU4VIb8vRxHyafZlmvprTKAOhM,824
269
274
  dodal/devices/util/epics_util.py,sha256=4useFL8ngsVF08fhOn48BlnO4oh0T4sEKqjdS6mjvG0,4687
270
- dodal/devices/util/lookup_tables.py,sha256=jH9f_D8JbTSqzL-RKHUWOORLt8lEoNQL3o9HpXE98TY,3476
275
+ dodal/devices/util/lookup_tables.py,sha256=8rip4fQI3YaYNcnUPiWF5Bhq9UXJSxFFfqJaFU4a_LU,3706
271
276
  dodal/devices/xspress3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
272
277
  dodal/devices/xspress3/xspress3.py,sha256=OerapEy-IuK7EFz13B5z0BzBmESVl6pYUlqAWHIwJck,4555
273
278
  dodal/devices/xspress3/xspress3_channel.py,sha256=w8tAx2lz5kJ_LeJ_eb_4o--Dtt8MRijsYNgDG6oEIVg,1626
@@ -291,7 +296,7 @@ dodal/plans/bimorph.py,sha256=JxDmZDiEvZnz5f22tlaoyivpnaNGiX8kSL82qz5uvMM,11738
291
296
  dodal/plans/configure_arm_trigger_and_disarm_detector.py,sha256=mzvno7ikEQkVfY1-vlrINxga2sOYAprRaf1LtySCfCk,6186
292
297
  dodal/plans/save_panda.py,sha256=X-zR5GysBPp3M7ZpEYEqCUSc4nJYzHJBA44e52uQ6F4,3099
293
298
  dodal/plans/scanspec.py,sha256=Q0AcvTKRT401iGMRDSqK-D523UX5_ofiVMZ_rNXKOx8,2074
294
- dodal/plans/verify_undulator_gap.py,sha256=o6TcV4GtJ6HLl0ufxKbnWjwrfBzpFdWhEpHraSwC0ZQ,614
299
+ dodal/plans/verify_undulator_gap.py,sha256=0YlkAPKH7A2auHbdhoxA9Bo50nGQ2xbNumDrAa6gjtg,628
295
300
  dodal/plans/wrapped.py,sha256=BPMw__RcWvk9v5XnhMsi9_k4KsDEbmXogzD2n1ecbUg,2098
296
301
  dodal/plans/preprocessors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
297
302
  dodal/plans/preprocessors/verify_undulator_gap.py,sha256=cBZEGq8TW1jrXFXB00iClQVXSEaE_jP_rHMY9WTgYyY,1813
@@ -299,8 +304,11 @@ dodal/testing/__init__.py,sha256=AUYZKAvVOs7ZvxO1dVhL0pDTleRO34FQlO5MNe_cwgU,96
299
304
  dodal/testing/setup.py,sha256=8cQnrzE5MQD4Etf0eqMarmtr-opsUOMQww-k1V7DzIQ,2442
300
305
  dodal/testing/electron_analyser/__init__.py,sha256=-lc1opD2dCv0x678-J-ApOhHtvEvcslfOQ7E613U8-Y,118
301
306
  dodal/testing/electron_analyser/device_factory.py,sha256=tkMY6fW3iI02DTD1XXHi4lH6sjo8RHHZBGDHSuTdmNU,2243
302
- dls_dodal-1.63.0.dist-info/METADATA,sha256=nCJBYDZcn5fcAK4UligXdmoOEGp4_wTHMN2CI4oRlfw,16941
303
- dls_dodal-1.63.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
304
- dls_dodal-1.63.0.dist-info/entry_points.txt,sha256=0IO1Bjlnv0vJSSFdGEZ-S_pqQNkE2FnPTA6f0-aTBs8,87
305
- dls_dodal-1.63.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
306
- dls_dodal-1.63.0.dist-info/RECORD,,
307
+ dodal/testing/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
308
+ dodal/testing/fixtures/run_engine.py,sha256=X_yrNQAGAZwclLds9lBTIyoI_DljNzFk-sYhWF1vYrI,1218
309
+ dodal/testing/fixtures/utils.py,sha256=jy3mfAAn_TFQ7gqCec-OiOlZkaNLUH3TESW2oohvNgo,1861
310
+ dls_dodal-1.64.0.dist-info/METADATA,sha256=Y0fRfaZjeMen-3IPUFnOBPKKrUbHu0zshyMlyrDrGFc,16950
311
+ dls_dodal-1.64.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
312
+ dls_dodal-1.64.0.dist-info/entry_points.txt,sha256=0IO1Bjlnv0vJSSFdGEZ-S_pqQNkE2FnPTA6f0-aTBs8,87
313
+ dls_dodal-1.64.0.dist-info/top_level.txt,sha256=xIozdmZk_wmMV4wugpq9-6eZs0vgADNUKz3j2UAwlhc,6
314
+ dls_dodal-1.64.0.dist-info/RECORD,,
dodal/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.63.0'
32
- __version_tuple__ = version_tuple = (1, 63, 0)
31
+ __version__ = version = '1.64.0'
32
+ __version_tuple__ = version_tuple = (1, 64, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
dodal/beamlines/i07.py ADDED
@@ -0,0 +1,21 @@
1
+ from dodal.common.beamlines.beamline_utils import device_factory
2
+ from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
3
+ from dodal.devices.i07.dcm import DCM
4
+ from dodal.log import set_beamline as set_log_beamline
5
+ from dodal.utils import BeamlinePrefix, get_beamline_name
6
+
7
+ BL = get_beamline_name("i07")
8
+ set_log_beamline(BL)
9
+ set_utils_beamline(BL)
10
+ PREFIX = BeamlinePrefix(BL)
11
+
12
+
13
+ @device_factory()
14
+ def dcm() -> DCM:
15
+ """Instantiate DCM using two PV bases"""
16
+ dcm = DCM(
17
+ f"{PREFIX.beamline_prefix}-MO-DCM-01:",
18
+ f"{PREFIX.beamline_prefix}-DI-DCM-01:",
19
+ "dcm",
20
+ )
21
+ return dcm
dodal/beamlines/i09.py CHANGED
@@ -43,7 +43,7 @@ def dcm() -> DoubleCrystalMonochromatorWithDSpacing:
43
43
 
44
44
  @device_factory()
45
45
  def energy_source() -> DualEnergySource:
46
- return DualEnergySource(dcm().energy_in_ev, pgm().energy.user_readback)
46
+ return DualEnergySource(dcm().energy_in_eV, pgm().energy.user_readback)
47
47
 
48
48
 
49
49
  # Connect will work again after this work completed
dodal/beamlines/i09_1.py CHANGED
@@ -34,7 +34,7 @@ def dcm() -> DoubleCrystalMonochromatorWithDSpacing:
34
34
 
35
35
  @device_factory()
36
36
  def energy_source() -> EnergySource:
37
- return EnergySource(dcm().energy_in_ev)
37
+ return EnergySource(dcm().energy_in_eV)
38
38
 
39
39
 
40
40
  # Connect will work again after this work completed
dodal/beamlines/i09_2.py CHANGED
@@ -2,6 +2,11 @@ from dodal.common.beamlines.beamline_utils import (
2
2
  device_factory,
3
3
  )
4
4
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
5
+ from dodal.devices.apple2_undulator import (
6
+ Apple2,
7
+ UndulatorGap,
8
+ UndulatorPhaseAxes,
9
+ )
5
10
  from dodal.devices.i09.enums import Grating
6
11
  from dodal.devices.pgm import PGM
7
12
  from dodal.devices.synchrotron import Synchrotron
@@ -22,3 +27,28 @@ def synchrotron() -> Synchrotron:
22
27
  @device_factory()
23
28
  def pgm() -> PGM:
24
29
  return PGM(prefix=f"{PREFIX.beamline_prefix}-MO-PGM-01:", grating=Grating)
30
+
31
+
32
+ @device_factory()
33
+ def jid_gap() -> UndulatorGap:
34
+ return UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:")
35
+
36
+
37
+ @device_factory()
38
+ def jid_phase() -> UndulatorPhaseAxes:
39
+ return UndulatorPhaseAxes(
40
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
41
+ top_outer="PUO",
42
+ top_inner="PUI",
43
+ btm_inner="PLI",
44
+ btm_outer="PLO",
45
+ )
46
+
47
+
48
+ @device_factory()
49
+ def jid() -> Apple2:
50
+ """I09 soft x-ray insertion device."""
51
+ return Apple2(
52
+ id_gap=jid_gap(),
53
+ id_phase=jid_phase(),
54
+ )
@@ -80,22 +80,35 @@ I10_CONF_CLIENT = ConfigServer(url="https://daq-config.diamond.ac.uk")
80
80
  LOOK_UPTABLE_DIR = "/dls_sw/i10/software/gda/workspace_git/gda-diamond.git/configurations/i10-shared/lookupTables/"
81
81
 
82
82
 
83
+ @device_factory()
84
+ def idd_gap() -> UndulatorGap:
85
+ return UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:")
86
+
87
+
88
+ @device_factory()
89
+ def idd_phase() -> UndulatorPhaseAxes:
90
+ return UndulatorPhaseAxes(
91
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
92
+ top_outer="RPQ1",
93
+ top_inner="RPQ2",
94
+ btm_inner="RPQ3",
95
+ btm_outer="RPQ4",
96
+ )
97
+
98
+
99
+ @device_factory()
100
+ def idd_jaw_phase() -> UndulatorJawPhase:
101
+ return UndulatorJawPhase(
102
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
103
+ move_pv="RPQ1",
104
+ )
105
+
106
+
83
107
  @device_factory()
84
108
  def idd() -> I10Apple2:
85
109
  """i10 downstream insertion device:"""
86
110
  return I10Apple2(
87
- id_gap=UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:"),
88
- id_phase=UndulatorPhaseAxes(
89
- prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
90
- top_outer="RPQ1",
91
- top_inner="RPQ2",
92
- btm_inner="RPQ3",
93
- btm_outer="RPQ4",
94
- ),
95
- id_jaw_phase=UndulatorJawPhase(
96
- prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
97
- move_pv="RPQ1",
98
- ),
111
+ id_gap=idd_gap(), id_phase=idd_phase(), id_jaw_phase=idd_jaw_phase()
99
112
  )
100
113
 
101
114
 
@@ -131,22 +144,35 @@ def energy_dd() -> BeamEnergy:
131
144
  return BeamEnergy(id_energy=idd_energy(), mono=pgm().energy)
132
145
 
133
146
 
147
+ @device_factory()
148
+ def idu_gap() -> UndulatorGap:
149
+ return UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-21:")
150
+
151
+
152
+ @device_factory()
153
+ def idu_phase() -> UndulatorPhaseAxes:
154
+ return UndulatorPhaseAxes(
155
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-21:",
156
+ top_outer="RPQ1",
157
+ top_inner="RPQ2",
158
+ btm_inner="RPQ3",
159
+ btm_outer="RPQ4",
160
+ )
161
+
162
+
163
+ @device_factory()
164
+ def idu_jaw_phase() -> UndulatorJawPhase:
165
+ return UndulatorJawPhase(
166
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-21:",
167
+ move_pv="RPQ1",
168
+ )
169
+
170
+
134
171
  @device_factory()
135
172
  def idu() -> I10Apple2:
136
173
  """i10 upstream insertion device"""
137
174
  return I10Apple2(
138
- id_gap=UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-21:"),
139
- id_phase=UndulatorPhaseAxes(
140
- prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-21:",
141
- top_outer="RPQ1",
142
- top_inner="RPQ2",
143
- btm_inner="RPQ3",
144
- btm_outer="RPQ4",
145
- ),
146
- id_jaw_phase=UndulatorJawPhase(
147
- prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-21:",
148
- move_pv="RPQ1",
149
- ),
175
+ id_gap=idu_gap(), id_phase=idu_phase(), id_jaw_phase=idu_jaw_phase()
150
176
  )
151
177
 
152
178
 
dodal/beamlines/i17.py CHANGED
@@ -47,18 +47,28 @@ def pgm() -> PGM:
47
47
  )
48
48
 
49
49
 
50
+ @device_factory()
51
+ def id_gap() -> UndulatorGap:
52
+ return UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:")
53
+
54
+
55
+ @device_factory()
56
+ def id_phase() -> UndulatorPhaseAxes:
57
+ return UndulatorPhaseAxes(
58
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
59
+ top_outer="RPQ1",
60
+ top_inner="RPQ2",
61
+ btm_inner="RPQ3",
62
+ btm_outer="RPQ4",
63
+ )
64
+
65
+
50
66
  @device_factory(skip=True)
51
67
  def id() -> Apple2:
52
68
  """I17 insertion device:"""
53
69
  return Apple2(
54
- id_gap=UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:"),
55
- id_phase=UndulatorPhaseAxes(
56
- prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
57
- top_outer="RPQ1",
58
- top_inner="RPQ2",
59
- btm_inner="RPQ3",
60
- btm_outer="RPQ4",
61
- ),
70
+ id_gap=id_gap(),
71
+ id_phase=id_phase(),
62
72
  )
63
73
 
64
74
 
dodal/beamlines/i21.py CHANGED
@@ -2,6 +2,11 @@ from dodal.common.beamlines.beamline_utils import (
2
2
  device_factory,
3
3
  )
4
4
  from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
5
+ from dodal.devices.apple2_undulator import (
6
+ Apple2,
7
+ UndulatorGap,
8
+ UndulatorPhaseAxes,
9
+ )
5
10
  from dodal.devices.i21 import Grating
6
11
  from dodal.devices.pgm import PGM
7
12
  from dodal.devices.synchrotron import Synchrotron
@@ -9,7 +14,7 @@ from dodal.log import set_beamline as set_log_beamline
9
14
  from dodal.utils import BeamlinePrefix, get_beamline_name
10
15
 
11
16
  BL = get_beamline_name("i21")
12
- PREFIX = BeamlinePrefix(BL, suffix="I")
17
+ PREFIX = BeamlinePrefix(BL)
13
18
  set_log_beamline(BL)
14
19
  set_utils_beamline(BL)
15
20
 
@@ -25,3 +30,28 @@ def pgm() -> PGM:
25
30
  prefix=f"{PREFIX.beamline_prefix}-OP-PGM-01:",
26
31
  grating=Grating,
27
32
  )
33
+
34
+
35
+ @device_factory()
36
+ def id_gap() -> UndulatorGap:
37
+ return UndulatorGap(prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:")
38
+
39
+
40
+ @device_factory()
41
+ def id_phase() -> UndulatorPhaseAxes:
42
+ return UndulatorPhaseAxes(
43
+ prefix=f"{PREFIX.insertion_prefix}-MO-SERVC-01:",
44
+ top_outer="PUO",
45
+ top_inner="PUI",
46
+ btm_inner="PLI",
47
+ btm_outer="PLO",
48
+ )
49
+
50
+
51
+ @device_factory()
52
+ def id() -> Apple2:
53
+ """I21 insertion device."""
54
+ return Apple2(
55
+ id_gap=id_gap(),
56
+ id_phase=id_phase(),
57
+ )
dodal/cli.py CHANGED
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
 
5
5
  import click
6
6
  from bluesky.run_engine import RunEngine
7
- from ophyd_async.core import NotConnected, StaticPathProvider, UUIDFilenameProvider
7
+ from ophyd_async.core import NotConnectedError, StaticPathProvider, UUIDFilenameProvider
8
8
  from ophyd_async.plan_stubs import ensure_connected
9
9
 
10
10
  from dodal.beamlines import all_beamline_names, module_name_for_beamline
@@ -79,7 +79,7 @@ def connect(beamline: str, all: bool, sim_backend: bool) -> None:
79
79
  # If exceptions have occurred, this will print details of the relevant PVs
80
80
  exceptions = {**instance_exceptions, **connect_exceptions}
81
81
  if len(exceptions) > 0:
82
- raise NotConnected(exceptions)
82
+ raise NotConnectedError(exceptions)
83
83
 
84
84
 
85
85
  def _report_successful_devices(
@@ -113,7 +113,7 @@ def _connect_devices(
113
113
  # Connect ophyd-async devices
114
114
  try:
115
115
  run_engine(ensure_connected(*ophyd_async_devices.values(), mock=sim_backend))
116
- except NotConnected as ex:
116
+ except NotConnectedError as ex:
117
117
  exceptions = {**exceptions, **ex.sub_errors}
118
118
 
119
119
  # Only return the subset of devices that haven't raised an exception
@@ -326,8 +326,8 @@ class Apple2(StandardReadable, Movable):
326
326
  Name of the device.
327
327
  """
328
328
  with self.add_children_as_readables():
329
- self.gap = id_gap
330
- self.phase = id_phase
329
+ self.gap = Reference(id_gap)
330
+ self.phase = Reference(id_phase)
331
331
  super().__init__(name=name)
332
332
 
333
333
  @AsyncStatus.wrap
@@ -338,25 +338,27 @@ class Apple2(StandardReadable, Movable):
338
338
  """
339
339
 
340
340
  # Only need to check gap as the phase motors share both fault and gate with gap.
341
- await self.gap.raise_if_cannot_move()
341
+ await self.gap().raise_if_cannot_move()
342
342
  await asyncio.gather(
343
- self.phase.top_outer.user_setpoint.set(value=id_motor_values.top_outer),
344
- self.phase.top_inner.user_setpoint.set(value=id_motor_values.top_inner),
345
- self.phase.btm_inner.user_setpoint.set(value=id_motor_values.btm_inner),
346
- self.phase.btm_outer.user_setpoint.set(value=id_motor_values.btm_outer),
347
- self.gap.user_setpoint.set(value=id_motor_values.gap),
343
+ self.phase().top_outer.user_setpoint.set(value=id_motor_values.top_outer),
344
+ self.phase().top_inner.user_setpoint.set(value=id_motor_values.top_inner),
345
+ self.phase().btm_inner.user_setpoint.set(value=id_motor_values.btm_inner),
346
+ self.phase().btm_outer.user_setpoint.set(value=id_motor_values.btm_outer),
347
+ self.gap().user_setpoint.set(value=id_motor_values.gap),
348
348
  )
349
349
  timeout = np.max(
350
- await asyncio.gather(self.gap.get_timeout(), self.phase.get_timeout())
350
+ await asyncio.gather(self.gap().get_timeout(), self.phase().get_timeout())
351
351
  )
352
352
  LOGGER.info(
353
353
  f"Moving f{self.name} apple2 motors to {id_motor_values}, timeout = {timeout}"
354
354
  )
355
355
  await asyncio.gather(
356
- self.gap.set_move.set(value=1, wait=False, timeout=timeout),
357
- self.phase.set_move.set(value=1, wait=False, timeout=timeout),
356
+ self.gap().set_move.set(value=1, wait=False, timeout=timeout),
357
+ self.phase().set_move.set(value=1, wait=False, timeout=timeout),
358
+ )
359
+ await wait_for_value(
360
+ self.gap().gate, UndulatorGateStatus.CLOSE, timeout=timeout
358
361
  )
359
- await wait_for_value(self.gap.gate, UndulatorGateStatus.CLOSE, timeout=timeout)
360
362
 
361
363
 
362
364
  class EnergyMotorConvertor(Protocol):
@@ -448,11 +450,11 @@ class Apple2Controller(abc.ABC, StandardReadable, Generic[Apple2Type]):
448
450
  raw_to_derived=self._read_pol,
449
451
  set_derived=self._set_pol,
450
452
  pol=self.polarisation_setpoint,
451
- top_outer=self.apple2().phase.top_outer.user_readback,
452
- top_inner=self.apple2().phase.top_inner.user_readback,
453
- btm_inner=self.apple2().phase.btm_inner.user_readback,
454
- btm_outer=self.apple2().phase.btm_outer.user_readback,
455
- gap=self.apple2().gap.user_readback,
453
+ top_outer=self.apple2().phase().top_outer.user_readback,
454
+ top_inner=self.apple2().phase().top_inner.user_readback,
455
+ btm_inner=self.apple2().phase().btm_inner.user_readback,
456
+ btm_outer=self.apple2().phase().btm_outer.user_readback,
457
+ gap=self.apple2().gap().user_readback,
456
458
  )
457
459
  super().__init__(name)
458
460
 
@@ -62,7 +62,7 @@ class ChannelCutMonochromator(
62
62
  )
63
63
 
64
64
  # energy derived signal as property
65
- self.energy_in_ev = derived_signal_r(
65
+ self.energy_in_eV = derived_signal_r(
66
66
  self._convert_pos_to_ev, pos_signal=self.crystal
67
67
  )
68
68
  super().__init__(name=name)
@@ -53,9 +53,9 @@ class DoubleCrystalMonochromatorBase(StandardReadable, Generic[Xtal_1, Xtal_2]):
53
53
  ) -> None:
54
54
  with self.add_children_as_readables():
55
55
  # Virtual motor PV's which set the physical motors so that the DCM produces requested energy
56
- self.energy_in_kev = Motor(prefix + "ENERGY")
57
- self.energy_in_ev = derived_signal_r(
58
- self._convert_keV_to_eV, energy_signal=self.energy_in_kev.user_readback
56
+ self.energy_in_keV = Motor(prefix + "ENERGY")
57
+ self.energy_in_eV = derived_signal_r(
58
+ self._convert_keV_to_eV, energy_signal=self.energy_in_keV.user_readback
59
59
  )
60
60
 
61
61
  self._make_crystals(prefix, xtal_1, xtal_2)
@@ -58,7 +58,7 @@ class UndulatorDCM(StandardReadable, Movable[float]):
58
58
  async def set(self, value: float):
59
59
  await self.undulator_ref().raise_if_not_enabled()
60
60
  await asyncio.gather(
61
- self.dcm_ref().energy_in_kev.set(value, timeout=ENERGY_TIMEOUT_S),
61
+ self.dcm_ref().energy_in_keV.set(value, timeout=ENERGY_TIMEOUT_S),
62
62
  self.undulator_ref().set(value),
63
63
  )
64
64
 
File without changes
@@ -0,0 +1,33 @@
1
+ from ophyd_async.epics.core import epics_signal_r
2
+ from ophyd_async.epics.motor import Motor
3
+
4
+ from dodal.devices.common_dcm import (
5
+ DoubleCrystalMonochromator,
6
+ PitchAndRollCrystal,
7
+ StationaryCrystal,
8
+ )
9
+
10
+
11
+ class DCM(DoubleCrystalMonochromator[PitchAndRollCrystal, StationaryCrystal]):
12
+ """
13
+ Device for i07's DCM, including temperature monitors and vertical motor which were
14
+ included in GDA.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ motor_prefix: str,
20
+ xtal_prefix: str,
21
+ name: str = "",
22
+ ) -> None:
23
+ super().__init__(motor_prefix, PitchAndRollCrystal, StationaryCrystal, name)
24
+ with self.add_children_as_readables():
25
+ self.vertical_in_mm = Motor(motor_prefix + "PERP")
26
+
27
+ # temperatures
28
+ self.xtal1_temp = epics_signal_r(float, xtal_prefix + "PT100-2")
29
+ self.xtal2_temp = epics_signal_r(float, xtal_prefix + "PT100-3")
30
+ self.xtal1_holder_temp = epics_signal_r(float, xtal_prefix + "PT100-1")
31
+ self.xtal2_holder_temp = epics_signal_r(float, xtal_prefix + "PT100-4")
32
+ self.gap_motor = epics_signal_r(float, xtal_prefix + "TC-1")
33
+ self.white_beam_stop_temp = epics_signal_r(float, xtal_prefix + "WBS:TEMP")
@@ -0,0 +1,3 @@
1
+ from .hard_undulator_functions import calculate_gap_i09_hu, get_hu_lut_as_dict
2
+
3
+ __all__ = ["calculate_gap_i09_hu", "get_hu_lut_as_dict"]
@@ -0,0 +1,111 @@
1
+ import numpy as np
2
+
3
+ from dodal.devices.util.lookup_tables import energy_distance_table
4
+ from dodal.log import LOGGER
5
+
6
+ LUT_COMMENTS = ["#"]
7
+ HU_SKIP_ROWS = 3
8
+
9
+ # Physics constants
10
+ ELECTRON_REST_ENERGY_MEV = 0.510999
11
+
12
+ # Columns in the lookup table
13
+ RING_ENERGY_COLUMN = 1
14
+ MAGNET_FIELD_COLUMN = 2
15
+ MIN_ENERGY_COLUMN = 3
16
+ MAX_ENERGY_COLUMN = 4
17
+ GAP_OFFSET_COLUMN = 7
18
+
19
+
20
+ async def get_hu_lut_as_dict(lut_path: str) -> dict:
21
+ lut_dict: dict = {}
22
+ _lookup_table: np.ndarray = await energy_distance_table(
23
+ lut_path,
24
+ comments=LUT_COMMENTS,
25
+ skiprows=HU_SKIP_ROWS,
26
+ )
27
+ for i in range(_lookup_table.shape[0]):
28
+ lut_dict[_lookup_table[i][0]] = _lookup_table[i]
29
+ LOGGER.debug(f"Loaded lookup table:\n {lut_dict}")
30
+ return lut_dict
31
+
32
+
33
+ def calculate_gap_i09_hu(
34
+ photon_energy_kev: float,
35
+ look_up_table: dict[int, "np.ndarray"],
36
+ order: int = 1,
37
+ gap_offset: float = 0.0,
38
+ undulator_period_mm: int = 27,
39
+ ) -> float:
40
+ """
41
+ Calculate the undulator gap required to produce a given energy at a given harmonic order.
42
+ This algorithm was provided by the I09 beamline scientists, and is based on the physics of undulator radiation.
43
+ https://cxro.lbl.gov//PDF/X-Ray-Data-Booklet.pdf
44
+
45
+ Args:
46
+ photon_energy_kev (float): Requested photon energy in keV.
47
+ look_up_table (dict[int, np.ndarray]): Lookup table containing undulator and beamline parameters for each harmonic order.
48
+ order (int, optional): Harmonic order for which to calculate the gap. Defaults to 1.
49
+ gap_offset (float, optional): Additional gap offset to apply (in mm). Defaults to 0.0.
50
+ undulator_period_mm (int, optional): Undulator period in mm. Defaults to 27.
51
+
52
+ Returns:
53
+ float: Calculated undulator gap in millimeters.
54
+ """
55
+ magnet_blocks_per_period = 4
56
+ magnet_block_height_mm = 16
57
+
58
+ if order not in look_up_table.keys():
59
+ raise ValueError(f"Order parameter {order} not found in lookup table")
60
+
61
+ gamma = 1000 * look_up_table[order][RING_ENERGY_COLUMN] / ELECTRON_REST_ENERGY_MEV
62
+
63
+ # Constructive interference of radiation emitted at different poles
64
+ # lamda = (lambda_u/2*gamma^2)*(1+K^2/2 + gamma^2*theta^2)/n for n=1,2,3...
65
+ # theta is the observation angle, assumed to be 0 here.
66
+ # Rearranging for K (the undulator parameter, related to magnetic field and gap)
67
+ # gives K^2 = 2*((2*n*gamma^2*lamda/lambda_u)-1)
68
+
69
+ undulator_parameter_sqr = (
70
+ 4.959368e-6
71
+ * (order * gamma * gamma / (undulator_period_mm * photon_energy_kev))
72
+ - 2
73
+ )
74
+ if undulator_parameter_sqr < 0:
75
+ raise ValueError(
76
+ f"Diffraction parameter squared must be positive! Calculated value {undulator_parameter_sqr}."
77
+ )
78
+ undulator_parameter = np.sqrt(undulator_parameter_sqr)
79
+
80
+ # Undulator_parameter K is also defined as K = 0.934*B0[T]*lambda_u[cm],
81
+ # where B0[T] is a peak magnetic field that must depend on gap,
82
+ # but in our LUT it is does not depend on gap, so it's a factor,
83
+ # leading to K = 0.934*B0[T]*lambda_u[cm]*exp(-pi*gap/lambda_u) or
84
+ # K = undulator_parameter_max*exp(-pi*gap/lambda_u)
85
+ # Calculating undulator_parameter_max gives:
86
+ undulator_parameter_max = (
87
+ (
88
+ 2
89
+ * 0.0934
90
+ * undulator_period_mm
91
+ * look_up_table[order][MAGNET_FIELD_COLUMN]
92
+ * magnet_blocks_per_period
93
+ / np.pi
94
+ )
95
+ * np.sin(np.pi / magnet_blocks_per_period)
96
+ * (1 - np.exp(-2 * np.pi * magnet_block_height_mm / undulator_period_mm))
97
+ )
98
+
99
+ # Finnaly, rearranging the equation:
100
+ # undulator_parameter = undulator_parameter_max*exp(-pi*gap/lambda_u) for gap gives
101
+ gap = (
102
+ (undulator_period_mm / np.pi)
103
+ * np.log(undulator_parameter_max / undulator_parameter)
104
+ + look_up_table[order][GAP_OFFSET_COLUMN]
105
+ + gap_offset
106
+ )
107
+ LOGGER.debug(
108
+ f"Calculated gap is {gap}mm for energy {photon_energy_kev}keV at order {order}"
109
+ )
110
+
111
+ return gap
@@ -340,7 +340,7 @@ class I10Apple2(Apple2):
340
340
  The name of the device, by default "".
341
341
  """
342
342
  with self.add_children_as_readables():
343
- self.jaw_phase = id_jaw_phase
343
+ self.jaw_phase = Reference(id_jaw_phase)
344
344
  super().__init__(id_gap=id_gap, id_phase=id_phase, name=name)
345
345
 
346
346
 
@@ -425,7 +425,7 @@ class I10Apple2Controller(Apple2Controller[I10Apple2]):
425
425
  f"jaw_phase position for angle ({pol_angle}) is outside permitted range"
426
426
  f" [-{self.jaw_phase_limit}, {self.jaw_phase_limit}]"
427
427
  )
428
- await self.apple2().jaw_phase.set(jaw_phase)
428
+ await self.apple2().jaw_phase().set(jaw_phase)
429
429
  await self._linear_arbitrary_angle.set(pol_angle)
430
430
 
431
431
  async def _set_motors_from_energy(self, value: float) -> None:
@@ -447,8 +447,8 @@ class I10Apple2Controller(Apple2Controller[I10Apple2]):
447
447
  LOGGER.info(f"Setting polarisation to {pol}, with values: {id_set_val}")
448
448
  await self.apple2().set(id_motor_values=id_set_val)
449
449
  if pol != Pol.LA:
450
- await self.apple2().jaw_phase.set(0)
451
- await self.apple2().jaw_phase.set_move.set(1)
450
+ await self.apple2().jaw_phase().set(0)
451
+ await self.apple2().jaw_phase().set_move.set(1)
452
452
 
453
453
  def _raise_if_not_la(self, pol: Pol) -> None:
454
454
  if pol != Pol.LA:
dodal/devices/i15/dcm.py CHANGED
@@ -33,7 +33,7 @@ class DCM(DoubleCrystalMonochromatorBase[ThetaRollYZCrystal, ThetaYCrystal]):
33
33
 
34
34
  def __init__(self, prefix: str, name: str = "") -> None:
35
35
  with self.add_children_as_readables():
36
- self.calibrated_energy_in_kev = Motor(prefix + "CAL")
36
+ self.calibrated_energy_in_keV = Motor(prefix + "CAL")
37
37
  self.x1 = Motor(prefix + "X1")
38
38
 
39
39
  super().__init__(prefix, ThetaRollYZCrystal, ThetaYCrystal, name)
dodal/devices/i22/dcm.py CHANGED
@@ -107,7 +107,7 @@ class DCM(DoubleCrystalMonochromatorWithDSpacing[RollCrystal, PitchAndRollCrysta
107
107
 
108
108
  async def read(self) -> dict[str, Reading]:
109
109
  default_reading = await super().read()
110
- energy: float = default_reading[f"{self.name}-energy_in_kev"]["value"]
110
+ energy: float = default_reading[f"{self.name}-energy_in_keV"]["value"]
111
111
  if energy > 0.0:
112
112
  wavelength = _CONVERSION_CONSTANT / energy
113
113
  else:
@@ -13,13 +13,19 @@ from numpy import interp, loadtxt
13
13
  from dodal.log import LOGGER
14
14
 
15
15
 
16
- async def energy_distance_table(lookup_table_path: str) -> np.ndarray:
16
+ async def energy_distance_table(
17
+ lookup_table_path: str,
18
+ comments: Sequence[str] = ["#", "Units"],
19
+ skiprows: int = 0,
20
+ ) -> np.ndarray:
17
21
  """
18
22
  Returns a numpy formatted lookup table for required positions of an ID gap to
19
23
  provide emission at a given beam energy.
20
24
 
21
25
  Args:
22
26
  lookup_table_path: Path to lookup table
27
+ comments: Lines starting with any of these strings will be ignored
28
+ skiprows: Number of rows to skip at the start of the file
23
29
 
24
30
  Returns:
25
31
  ndarray: Lookup table
@@ -29,7 +35,7 @@ async def energy_distance_table(lookup_table_path: str) -> np.ndarray:
29
35
  # decodes the text
30
36
  async with aiofiles.open(lookup_table_path) as stream:
31
37
  raw_table = await stream.read()
32
- return loadtxt(StringIO(raw_table), comments=["#", "Units"])
38
+ return loadtxt(StringIO(raw_table), comments=comments, skiprows=skiprows)
33
39
 
34
40
 
35
41
  def parse_lookup_table(filename: str) -> list[Sequence]:
@@ -15,5 +15,5 @@ class CheckUndulatorDevices(Protocol):
15
15
  def verify_undulator_gap(devices: CheckUndulatorDevices):
16
16
  """Verify Undulator gap is correct - it may not be after a beam dump"""
17
17
 
18
- energy_in_kev = yield from bps.rd(devices.dcm.energy_in_kev.user_readback)
19
- yield from bps.abs_set(devices.undulator, energy_in_kev, wait=True)
18
+ energy_in_keV = yield from bps.rd(devices.dcm.energy_in_keV.user_readback) # noqa: N806
19
+ yield from bps.abs_set(devices.undulator, energy_in_keV, wait=True)
File without changes
@@ -0,0 +1,46 @@
1
+ """
2
+ Allow external repos to reuse these fixtures so defined in single place.
3
+ """
4
+
5
+ import asyncio
6
+ import time
7
+ from collections.abc import Mapping
8
+
9
+ import pytest
10
+ from bluesky.run_engine import RunEngine
11
+ from bluesky.simulators import RunEngineSimulator
12
+
13
+
14
+ @pytest.fixture(scope="session", autouse=True)
15
+ async def _ensure_running_bluesky_event_loop():
16
+ run_engine = RunEngine()
17
+ # make sure the event loop is thoroughly up and running before we try to create
18
+ # any ophyd_async devices which might need it
19
+ timeout = time.monotonic() + 1
20
+ while not run_engine.loop.is_running():
21
+ await asyncio.sleep(0)
22
+ if time.monotonic() > timeout:
23
+ raise TimeoutError("This really shouldn't happen but just in case...")
24
+
25
+
26
+ @pytest.fixture()
27
+ async def run_engine():
28
+ yield RunEngine()
29
+
30
+
31
+ @pytest.fixture
32
+ def sim_run_engine() -> RunEngineSimulator:
33
+ return RunEngineSimulator()
34
+
35
+
36
+ @pytest.fixture
37
+ def run_engine_documents(run_engine: RunEngine) -> Mapping[str, list[dict]]:
38
+ docs: dict[str, list[dict]] = {}
39
+
40
+ def append_and_print(name, doc):
41
+ if name not in docs:
42
+ docs[name] = []
43
+ docs[name] += [doc]
44
+
45
+ run_engine.subscribe(append_and_print)
46
+ return docs
@@ -0,0 +1,57 @@
1
+ import asyncio
2
+ import threading
3
+ import time
4
+ from random import random
5
+ from threading import Thread
6
+
7
+ import pytest
8
+
9
+
10
+ @pytest.fixture
11
+ async def event_loop_fuzzing():
12
+ """
13
+ This fixture can be used to try and detect / reproduce intermittent test failures
14
+ caused by race conditions and timing issues, which are often difficult to replicate
15
+ due to caching etc. causing timing to be different on a development machine compared
16
+ to when the test runs in CI.
17
+
18
+ It works by attaching a fuzzer to the current event loop which randomly schedules
19
+ a fixed delay into the event loop thread every few milliseconds. The idea is that
20
+ over a number of iterations, there should be sufficient timing variation introduced
21
+ that the failure can be reproduced.
22
+
23
+ Examples:
24
+ Example usage:
25
+ >>> import pytest
26
+ >>> # repeat the test a number of times
27
+ >>> @pytest.mark.parametrize("i", range(0, 100))
28
+ ... async def my_unreliable_test(i, event_loop_fuzzing):
29
+ ... # Do some stuff in here
30
+ ... ...
31
+ """
32
+ fuzz_probability = 0.05
33
+ fuzz_delay_s = 0.05
34
+ fuzz_period_s = 0.001
35
+ stop_running = threading.Event()
36
+ event_loop = asyncio.get_running_loop()
37
+
38
+ def delay(finished_event: threading.Event):
39
+ time.sleep(fuzz_delay_s) # noqa: TID251
40
+ finished_event.set()
41
+
42
+ def fuzz():
43
+ while not stop_running.is_set():
44
+ if random() < fuzz_probability:
45
+ delay_is_finished = threading.Event()
46
+ event_loop.call_soon_threadsafe(delay, delay_is_finished)
47
+ delay_is_finished.wait()
48
+
49
+ time.sleep(fuzz_period_s) # noqa: TID251
50
+
51
+ fuzzer_thread = Thread(group=None, target=fuzz, name="Event loop fuzzer")
52
+ fuzzer_thread.start()
53
+ try:
54
+ yield None
55
+ finally:
56
+ stop_running.set()
57
+ fuzzer_thread.join()