bluesky / hklpy

Diffractometer computation library with ophyd pseudopositioner support
https://blueskyproject.io/hklpy
BSD 3-Clause "New" or "Revised" License
4 stars 12 forks source link

wish list: get wavelength from beam line controls #26

Closed prjemian closed 4 years ago

prjemian commented 4 years ago

Other things we need to consider are:

  • ability to convert the RBV of beamline

Also, convert energy units appropriately (soft x-ray beamlines at NSLS-II use eV, not keV). In other words, rely on EPICS UNITS to decide how to convert.

If we continue to require a manual setting of the hklpy calculation energy, then these things are transparent. I am just anticipating something more sophisticated as I am not sure how much you want to change.

Originally posted by @ambarb in https://github.com/bluesky/hklpy/issues/17#issuecomment-591471108

prjemian commented 4 years ago

In https://github.com/bluesky/hklpy/issues/29#issuecomment-700155456, it was shown how to get photon energy from beam line controls. The same steps could be used to get wavelength instead.

Internally, the calc module stores only the wavelength (angstroms). It converts energy to wavelength when energy is set. Here's the code: https://github.com/bluesky/hklpy/blob/14463c40d956f73768a3a4494ea1af3fba98dd85/hkl/calc.py#L136-L152

example

This user operation sets the energy: fourc.calc.energy = 8.04 (the units are keV). The setter part sets the internal wavelength fourc.calc._wavelength by calculating A_KEV/energy. When the users asks for the current energy, it is recalculated from the internal wavelength (A_KEV/wavelength).

prjemian commented 4 years ago

It is recommended to get either the energy or wavelength from the beam line controls, but not both. This is always a local customization to the specific instrument so it is not appropriate to add into the code here. (This example could be part of the documentation, however.)

There is already code in place to use an energy ophyd.Signal (and a default Diffractometer._energy_changed() method) to set energy and wavelength in the calc attribute. Override the energy Signal with an ophyd.EpicsSignal and connect to the desired energy PV. An example is provided (from https://github.com/bluesky/hklpy/issues/29#issuecomment-700097970 & https://github.com/bluesky/hklpy/issues/29#issuecomment-700155456). The example also shows unit conversion (using thepint package) and a Signal for energy offset (as requested in #25). (If the offset is an EPICS PV, then use EpicsSignal and the PV instead).

import gi
gi.require_version('Hkl', '5.0')
import hkl.diffract
from ophyd import Component, EpicsSignal, Signal
import pint

class LocalDiffractometer(hkl.diffract.Diffractometer):
    # ...
    energy = Component(EpicsSignal, "EPICS:diffractometer:energy")
    energy_EGU = Component(EpicsSignal, "EPICS:diffractometer:energy.EGU")
    energy_update_calc = Component(EpicsSignal, "EPICS:diffractometer:energy_update_hkl")
    energy_offset = Component(Signal, value=0)
    # ...

    def _energy_changed(self, value=None, **kwargs):
        '''
        Callback indicating that the energy signal was updated
        '''
        if energy_update_calc.get() in (1, "Yes", "locked", "OK"):
            local_energy = value + self.energy_offset.get()
            units = self.energy_EGU.get()   # get units from EPICS
            # units = "eV"    # alternative: from control system always gives "eV"
            logger.debug(
                '{0.name} energy changed: {1} {2}'.format(
                    self, value, units))
            keV = pint.Quantity(local_energy, units).to("keV")
            self._calc.energy = keV
            self._update_position()