From 1e28dcf97af9cb1cdcb7b1e4b0778b65709c7a01 Mon Sep 17 00:00:00 2001 From: Riley Bode Date: Mon, 10 Jul 2023 14:13:37 -0400 Subject: [PATCH] Automated stateful support for CRLs and crystals --- sirepo_bluesky/sirepo_ophyd.py | 39 +++++++- .../tests/test_bl_elements_as_ophyd_objs.py | 90 +++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/sirepo_bluesky/sirepo_ophyd.py b/sirepo_bluesky/sirepo_ophyd.py index a522ada8..957c6f05 100644 --- a/sirepo_bluesky/sirepo_ophyd.py +++ b/sirepo_bluesky/sirepo_ophyd.py @@ -335,9 +335,13 @@ def set(self, value): for cpt in [ "dSpacing", "grazingAngle", - "nvx" "nvy", + "nvx", + "nvy", "nvz", - "outframevx" "outframevy" "outoptvx" "outoptvy", + "outframevx", + "outframevy", + "outoptvx", + "outoptvy", "outoptvz", "psi0i", "psi0r", @@ -345,7 +349,8 @@ def set(self, value): "psiHBr", "psiHi", "psiHr", - "tvx" "tvy", + "tvx", + "tvy", ]: getattr(self.parent, cpt).put(ret[cpt]) return NullStatus() @@ -428,6 +433,34 @@ def create_classes(sirepo_data, connection, create_objects=True, extra_model_fie and k == "grazingAngle" ): cpt_class = SirepoSignalGrazingAngle + elif "type" in el and el["type"] == "crl" and k not in ["absoluteFocusPosition", "focalDistance"]: + cpt_class = SirepoSignalCRL + elif ( + "type" in el + and el["type"] == "crystal" + and k + not in [ + "dSpacing", + "grazingAngle", + "nvx", + "nvy", + "nvz", + "outframevx", + "outframevy", + "outoptvx", + "outoptvy", + "outoptvz", + "psi0i", + "psi0r", + "psiHBi", + "psiHBr", + "psiHi", + "psiHr", + "tvx", + "tvy", + ] + ): + cpt_class = SirepoSignalCrystal else: # TODO: Cover the cases for mirror and crystal grazing angles cpt_class = SirepoSignal diff --git a/sirepo_bluesky/tests/test_bl_elements_as_ophyd_objs.py b/sirepo_bluesky/tests/test_bl_elements_as_ophyd_objs.py index 5bf2948d..9f79ab49 100644 --- a/sirepo_bluesky/tests/test_bl_elements_as_ophyd_objs.py +++ b/sirepo_bluesky/tests/test_bl_elements_as_ophyd_objs.py @@ -53,6 +53,96 @@ def test_beamline_elements_set_put(srw_tes_simulation, method): assert abs(new_value - (old_value + 100)) < 1e-8 +@pytest.mark.parametrize("method", ["set", "put"]) +def test_crl_calculation(srw_chx_simulation, method): + classes, objects = create_classes(srw_chx_simulation.data, connection=srw_chx_simulation) + globals().update(**objects) + + params_before = copy.deepcopy(crl1.tipRadius._sirepo_dict) # noqa F821 + params_before.pop("tipRadius") + + getattr(crl1.tipRadius, method)(2000) # noqa F821 + + params_after = copy.deepcopy(crl1.tipRadius._sirepo_dict) # noqa F821 + params_after.pop("tipRadius") + + params_diff = list(dictdiffer.diff(params_before, params_after)) + assert len(params_diff) > 0 # should not be empty + + expected_values = { + "absoluteFocusPosition": -6.195573642892285, + "focalDistance": 237.666984823537, + } + + actual_values = { + "absoluteFocusPosition": crl1.absoluteFocusPosition.get(), # noqa F821 + "focalDistance": crl1.focalDistance.get(), # noqa F821 + } + + assert not list(dictdiffer.diff(expected_values, actual_values)) + + +@pytest.mark.parametrize("method", ["set", "put"]) +def test_crystal_calculation(srw_tes_simulation, method): + classes, objects = create_classes(srw_tes_simulation.data, connection=srw_tes_simulation) + globals().update(**objects) + + params_before = copy.deepcopy(mono_crystal1.energy._sirepo_dict) # noqa F821 + params_before.pop("energy") + + getattr(mono_crystal1.energy, method)(2000) # noqa F821 + + params_after = copy.deepcopy(mono_crystal1.energy._sirepo_dict) # noqa F821 + params_after.pop("energy") + + params_diff = list(dictdiffer.diff(params_before, params_after)) + assert len(params_diff) > 0 # should not be empty + + expected_values = { + "dSpacing": 3.1355713563754857, + "grazingAngle": 1419.9107955732711, + "nvx": 0, + "nvy": 0.15031366142760424, + "nvz": -0.9886383581412506, + "outframevx": 1.0, + "outframevy": 0.0, + "outoptvx": 0.0, + "outoptvy": 0.29721170287997256, + "outoptvz": -0.9548116063764552, + "psi0i": 6.530421915581681e-05, + "psi0r": -0.00020558072555357544, + "psiHBi": 4.559368494529194e-05, + "psiHBr": -0.00010207663788071082, + "psiHi": 4.559368494529194e-05, + "psiHr": -0.00010207663788071082, + "tvx": 0, + "tvy": 0.9886383581412506, + } + + actual_values = { + "dSpacing": mono_crystal1.dSpacing.get(), # noqa F821 + "grazingAngle": mono_crystal1.grazingAngle.get(), # noqa F821 + "nvx": mono_crystal1.nvx.get(), # noqa F821 + "nvy": mono_crystal1.nvy.get(), # noqa F821 + "nvz": mono_crystal1.nvz.get(), # noqa F821 + "outframevx": mono_crystal1.outframevx.get(), # noqa F821 + "outframevy": mono_crystal1.outframevy.get(), # noqa F821 + "outoptvx": mono_crystal1.outoptvx.get(), # noqa F821 + "outoptvy": mono_crystal1.outoptvy.get(), # noqa F821 + "outoptvz": mono_crystal1.outoptvz.get(), # noqa F821 + "psi0i": mono_crystal1.psi0i.get(), # noqa F821 + "psi0r": mono_crystal1.psi0r.get(), # noqa F821 + "psiHBi": mono_crystal1.psiHBi.get(), # noqa F821 + "psiHBr": mono_crystal1.psiHBr.get(), # noqa F821 + "psiHi": mono_crystal1.psiHi.get(), # noqa F821 + "psiHr": mono_crystal1.psiHr.get(), # noqa F821 + "tvx": mono_crystal1.tvx.get(), # noqa F821 + "tvy": mono_crystal1.tvy.get(), # noqa F821 + } + + assert not list(dictdiffer.diff(expected_values, actual_values)) + + @pytest.mark.parametrize("method", ["set", "put"]) def test_grazing_angle_calculation(srw_tes_simulation, method): classes, objects = create_classes(srw_tes_simulation.data, connection=srw_tes_simulation)