Module: diffract#

A local subclass of hkl.diffract.Diffractometer for the desired diffractometer geometry must be created to define the reciprocal-space axes and customize the EPICS PVs used for the motor axes. Other capabilities are also customized in a local subclass.

Examples are provided in the Geometries Examples section.

Common Diffractometer Geometries#

These are the diffractometer geometries provided by the libhkl library [1]:

E4CH

Eulerian 4-circle, horizontal scattering plane

E4CV

Eulerian 4-circle, vertical scattering plane

E6C

Eulerian 6-circle, vertical scattering plane

K4CV

Kappa 4-circle, vertical scattering plane

K6C

Kappa 6-circle, vertical scattering plane

Zaxis

Z-axis geometry

Special-use Diffractometer Geometries#

These special-use geometries are also provided by the libhkl library [1]:

ApsPolar

APS POLAR 6-circle diffractometer.

Petra3_p09_eh2

6-circle Used at Petra3 P09

Petra3_p23_4c

4-circle Used at Petra3 P23

Petra3_p23_6c

7-circle Used at Petra3 P23

SoleilMars

Used at Soleil

SoleilSiriusKappa

Used at Soleil

SoleilSiriusTurret

Used at Soleil

SoleilSixsMed1p2

Used at Soleil

SoleilSixsMed2p2

Used at Soleil

SoleilSixsMed2p3

Used at Soleil

SoleilSixsMed2p3v2

Used at Soleil

In all cases, see the libhkl documentation for further information on these geometries.

Energy#

The monochromatic X-ray energy used by the diffractometer is defined as an ophyd Signal. The .energy signal may be used as-provided, as a constant or the Component may be replaced by an ophyd.EpicsSignal in a custom subclass of Diffractometer, to connect with an EPICS PV from the instrument’s control system. There is no corresponding .wavelength signal at the diffractometer level. The .energy_offset signal is used to allow for a difference between the reported energy of .energy and the energy used for diffractometer operations .calc.energy.

These are the terms:

term

meaning

.energy

nominal energy (such as value reported by control system)

.energy_offset

adjustment from nominal to calibrated energy for diffraction

.energy_units

engineering units to be used for .energy and .energy_offset

.calc.energy

energy for diffraction (used to compute wavelength)

.calc.wavelength

used for diffraction calculations

These are the relationships:

.calc.energy = in_keV(.energy + .energy_offset)
.calc.wavelength = A*keV / .calc.energy

For use with other types of radiation (such as neutrons or electrons), the conversion between energy and wavelength must be changed by editing the energy and wavelength methods in the hkl.calc.CalcRecip class.

When the Diffractometer .energy signal is written (via .energy.put(value) operation), the .energy_offset is added. This result is converted to keV as required by the lower-level hkl.calc module, and then written to .calc.energy which in turn writes the .calc.wavelength value. Likewise, when .energy.get() reads the energy, it takes its value from .calc.energy, converts into .energy_units, and then applies .energy_offset.

Tip

How to set energy, offset, and units

To change energy, offset, & units, do it in this order:

  • First, set units and offset

  • Finally, set energy

Example for a e4cv diffractometer:

e4cv.energy_units.put("eV")
e4cv.energy_offset.put(1.5)
e4cv.energy.put(8000)
# now, e4cv.calc.energy = 8.0015 keV

Engineering Units#

The geometries module is the main user interface, providing the various diffractometer geometries provided by libhkl as ophyd PseudoPositioner Devices [2]. Each of these geometries is built from the base class Diffractometer, where a feature has been added to allow the energy units to be set by the user. Internally, Diffractometer will apply any necessary units conversion when interacting with the values from the calc module.

In the calc module (lower level than Diffractometer), the units for energy and wavelength are keV and angstrom, respectively. These engineering units are not changeable by the user since the libhkl code requires a consistent set of units (it does not anticipate the need to do a units conversion).

The Python pint package [3] is used to apply any unit conversion.

This table summarizes the units for the energy and wavelength terms for all diffractometer geometries.

diffractometer attribute

engineering units

.energy

set by value of .energy_units Signal

.calc.energy

keV

.wavelength

does not exist in Diffractometer

.calc.wavelength

angstrom

Control System Energy#

One way to connect the control system energy is to replace the energy attribute in the custom subclass of hkl.diffract.Diffractometer(), connecting it as either ophyd.EpicsSignal, ophyd.EpicsSignalRO, or ophyd.EpicsSignalWithRBV. In the next example, the monochromator energy, engineering units, and offset are provided by EPICS. Also, another EPICS PV controls whether the energy of the diffractometer object should be updated by changes in the EPICS energy. Note that the _energy_changed() method is already subscribed to updates in the energy signal. It is not necessary to do this in the custom subclass. Don’t forget to add the definitions for the real and pseudo positioners.

Connecting control system energy to a 4-circle diffractometer.#
 1from hkl import E4CV
 2import logging
 3from ophyd import Component
 4from ophyd import EpicsMotor
 5from ophyd import EpicsSignal
 6from ophyd import PseudoSingle
 7
 8logger = logging.getLogger(__name__)
 9
10class LocalDiffractometer(E4CV):
11    h = Component(PseudoSingle, '', kind="hinted")
12    k = Component(PseudoSingle, '', kind="hinted")
13    l = Component(PseudoSingle, '', kind="hinted")
14
15    omega = Component(EpicsMotor, "EPICS:m1", kind="hinted")
16    chi = Component(EpicsMotor, "EPICS:m2", kind="hinted")
17    phi = Component(EpicsMotor, "EPICS:m3", kind="hinted")
18    tth = Component(EpicsMotor, "EPICS:m4", kind="hinted")
19
20    energy = Component(EpicsSignal, "EPICS:energy")
21    energy_units = Component(EpicsSignal, "EPICS:energy.EGU")
22    energy_offset = Component(EpicsSignal, "EPICS:energy:offset")
23    energy_update_calc_flag = Component(EpicsSignal, "EPICS:energy:lock")
24
25fourc = LocalDiffractometer("", name="fourc")
26fourc.wait_for_connection()
27fourc._energy_changed()  # force the callback to update

Source code documentation#

diffract#

Common Support for diffractometers

Diffractometer(prefix[, calc_kw, ...])

Diffractometer pseudopositioner

class hkl.diffract.Diffractometer(prefix, calc_kw=None, decision_fcn=None, calc_inst=None, engine='hkl', *, configuration_attrs=None, read_attrs=None, **kwargs)[source]#

Diffractometer pseudopositioner

PUBLIC API

calc

The calculation instance

engine

The calculation engine associated with the diffractometer

forward(pseudo)

Calculate the real positions given the pseudo positions (hkl -> angles).

inverse(real)

Calculate the pseudo positions given the real positions (angles -> hkl).

forward_solutions_table(reflections[, full, ...])

Return table of computed solutions for each supplied (hkl) reflection.

apply_constraints(constraints)

Constrain the solutions of the diffractometer's forward() computation.

get_axis_constraints(axis)

Show the constraints for one axis.

reset_constraints()

Set constraints back to initial settings.

show_constraints([fmt, printing])

Print the current constraints in a table.

undo_last_constraints()

Remove the current constraints additions, restore previous.

pa([all_samples, printing])

Report (all) the diffractometer settings.

wh([printing])

Report (brief) where is the diffractometer.

geometry_table()

Print a table describing this diffractometer geometry.

PRIVATE API

_calc_energy_update_permitted

return boolean True if permitted

_constraints_dict

Return the constraints.

_constraints_for_databroker

Return the constraints for databroker.

_energy_changed([value])

Callback indicating that the energy signal was updated

_energy_offset_changed([value])

Callback indicating that the energy offset signal was updated.

_energy_units_changed([value])

Callback indicating that the energy units signal was updated.

_push_current_constraints()

push current constraints onto the stack

_set_constraints(constraints)

set diffractometer's constraints

_update_calc_energy([value])

writes self.calc.energy from value or self.energy.

A Diffractometer has a corresponding calculation engine from hklpy that does forward and inverse calculations.

If instantiating a specific diffractometer class such as E4C, E6C, neither the calc_inst or the calc_kw parameters are required.

However, there is the option to either pass in a calculation instance (with calc_inst) or keywords for the default calculation class (using calc_kw) to instantiate a new one.

Parameters:
  • prefix (str) – PV prefix for all components

  • calc_kw (dict, optional) – Initializer arguments for the calc class

  • decision_fcn (callable, optional) – The decision function to use when multiple solutions exist for a given forward calculation. Defaults to arbitrarily picking the first solution.

  • engine (str, optional) – Calculation engine name. Default: hkl

  • read_attrs (list, optional) – Read attributes default to all pseudo and real positioners

  • configuration_attrs (list, optional) – Defaults to the UB matrix and energy

  • parent (Device, optional) – Parent device

  • name (str, optional) – Device name

calc_class#

Reciprocal calculation class used with this diffractometer. If None (as used in hkl.diffract.Diffractometer), calc_inst must be specified upon initialization.

Type:

sub-class of CalcRecip

max_forward_iterations#

Maximum allowed number of iterations in the forward() method before failing to find a forward() solution.

Type:

ophyd.Signal (default value: 100)

property _calc_energy_update_permitted#

return boolean True if permitted

property _constraints_dict#

Return the constraints.

property _constraints_for_databroker#

Return the constraints for databroker.

Cannot write a dictionary from bluesky because all values must be of the same data type, chosen from the first item in the list. Just write the values (the boolean will be written as a float.) The constraints will be written in the order of the real positioners.

_energy_changed(value=None, **kwargs)[source]#

Callback indicating that the energy signal was updated

Note

The energy signal is subscribed to this method in the Diffractometer.__init__() method.

_energy_offset_changed(value=None, **kwargs)[source]#

Callback indicating that the energy offset signal was updated.

Note

The energy_offset signal is subscribed to this method in the Diffractometer.__init__() method.

_energy_units_changed(value=None, **kwargs)[source]#

Callback indicating that the energy units signal was updated.

Note

The energy_units signal is subscribed to this method in the Diffractometer.__init__() method.

_push_current_constraints()[source]#

push current constraints onto the stack

property _reflections#

Return the list of reflections as a [[float]]

_set_constraints(constraints)[source]#

set diffractometer’s constraints

_update_calc_energy(value=None, **kwargs)[source]#

writes self.calc.energy from value or self.energy.

apply_constraints(constraints)[source]#

Constrain the solutions of the diffractometer’s forward() computation.

This action will first save the current constraints onto a stack, enabling both undo and reset features.

property calc#

The calculation instance

check_value(pos)[source]#

Raise exception if pos is not within limits.

In a scan, a subset of the pseudo axes may be directed, which are given in a dict from a set message from the bluesky RunEngine.

It is not permitted to scan both pseudo and real positioners.

property engine#

The calculation engine associated with the diffractometer

forward(pseudo)[source]#

Calculate the real positions given the pseudo positions (hkl -> angles).

Return the default solution using the _decision_fcn().

forward_solutions_table(reflections, full=False, digits=5)[source]#

Return table of computed solutions for each supplied (hkl) reflection.

The solutions are calculated using the current UB matrix & constraints.

Parameters:
  • reflections (list of (h, k, l) reflections) – Each reflection is a tuple of 3 numbers, (h, k, l) of the reflection.

  • full (bool) – If True, show all solutions. If False, only show the default solution.

  • digits (int) – Number of digits to roundoff each position value. Default is 5.

geometry_table()[source]#

Print a table describing this diffractometer geometry.

Calls hkl.calc.CalcRecip.geometry_table().

get_axis_constraints(axis)[source]#

Show the constraints for one axis.

inverse(real)[source]#

Calculate the pseudo positions given the real positions (angles -> hkl).

pa(all_samples=False, printing=True)[source]#

Report (all) the diffractometer settings.

EXAMPLE:

In [1]: from hkl import SimulatedK4CV

In [2]: k4cv = SimulatedK4CV('', name='k4cv')

In [3]: k4cv.pa()  # FIXME lines are too long to include in source code
===================== ====================================================================
term                  value
===================== ====================================================================
diffractometer        k4cv
geometry              K4CV
class                 SimulatedK4CV
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           hkl
mode                  bissector
positions             ====== =======
                    name   value
                    ====== =======
                    komega 0.00000
                    kappa  0.00000
                    kphi   0.00000
                    tth    0.00000
                    ====== =======
sample: main          ================ ===================================================
                    term             value
                    ================ ===================================================
                    unit cell edges  a=1.54, b=1.54, c=1.54
                    unit cell angles alpha=90.0, beta=90.0, gamma=90.0
                    [U]              [[1. 0. 0.]
                                        [0. 1. 0.]
                                        [0. 0. 1.]]
                    [UB]             [[ 4.07999046e+00 -2.49827363e-16 -2.49827363e-16]
                                        [ 0.00000000e+00  4.07999046e+00 -2.49827363e-16]
                                        [ 0.00000000e+00  0.00000000e+00  4.07999046e+00]]
                    ================ ===================================================
===================== ====================================================================

Out[3]: <pyRestTable.rest_table.Table at 0x7f5c16503e20>
reset_constraints()[source]#

Set constraints back to initial settings.

show_constraints(fmt='simple', printing=True)[source]#

Print the current constraints in a table.

undo_last_constraints()[source]#

Remove the current constraints additions, restore previous.

wh(printing=True)[source]#

Report (brief) where is the diffractometer.

EXAMPLE:

In [1]: from hkl import SimulatedK4CV

In [2]: k4cv = SimulatedK4CV('', name='k4cv')

In [3]: k4cv.wh()
===================== ========= =========
term                  value     axis_type
===================== ========= =========
diffractometer        k4cv
sample name           main
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           hkl
mode                  bissector
h                     0.0       pseudo
k                     0.0       pseudo
l                     0.0       pseudo
komega                0         real
kappa                 0         real
kphi                  0         real
tth                   0         real
===================== ========= =========

Out[3]: <pyRestTable.rest_table.Table at 0x7f55c4775cd0>

compare with similar function in SPEC:

1117.KAPPA> wh
H K L =  0  0  1.7345
Alpha = 20  Beta = 20  Azimuth = 90
Omega = 32.952  Lambda = 1.54
Two Theta       Theta         Chi         Phi     K_Theta       Kappa       K_Phi
40.000000   20.000000   90.000000   57.048500   77.044988  134.755995  114.093455