Use E4CV’s \(Q\) calculation engine#

Many of the diffractometer geometries support different calculation engines. By default, hklpy provides h, k, & l pseudo positioners (the hkl engine) since this is the most common case. For example, the E4CV geometry supports several calculation engines:

engine

pseudo(s)

real(s)

hkl

h, k, l

omega, chi, phi, tth

psi

psi

omega, chi, phi, tth

q

q

tth

incidence

incidence, azimuth

omega, chi, phi

emergence

emergence, azimuth

omega, chi, phi, tth

NOTE: The choice of calculation engine is locked in the hkl.diffract.Diffractometer() class. Once the diffractometer object is created, the calculation engine cannot be changed.

Objective

Many of the examples in the hklpy repository use the hkl engine, it is the most common use case. Below, we’ll demonstrate the q calculation engine of the E4CV (4-circle Eulerian in vertical scattering) geometry.

Standard Imports#

First, we start by importing the constant, A_KEV (product of Planck’s constant and speed of light in a vacuum). The value of this constant is obtained from the 2019 NIST publication of 2018 CODATA Fundamental Physical Constants. (Keep in mind that gi is not the Gtk library, just the tools that enable other languages such as Python to use the libraries compiled with Gnome’s Glib Object: GObject framework.)

[1]:
from hkl import A_KEV

q engine#

The `q engine <https://people.debian.org/~picca/hkl/hkl.html#org7ef08ba>`__ is easy to demonstrate since it only involves the actions of the tth circle (\(q=4\pi\sin(\theta)/\lambda\) where \(\theta\) is half of tth) and no crystal orientation reflections are necessary.

Still, it is necessary to define all required real positioners of the geometry. The required positioners are listed as Axes directly under the section title in the libhkl documentation for each geometry. Also, specify them in the order they appear in the documentation. The real positioners stay the same for all engines of a diffractometer geometry. The pseudo positioners are defined by the calculation engine and may be different for each engine.

TIP: If you do not define all the required Axes of the geometry, Python will likely terminate (and with no useful message, at that) when you first try to create the diffractometer object.

term

value

geometry

E4CV

engine

q

mode

default

Create a custom class for the E4CV geometry with the q calculation engine. There is only one pseudo positioner, q, for the calculation engine and the four real positioners for the geometry. Since this demonstration uses SoftPositioners, we must provide an init_pos kwarg with the initial position for each real axis. There is no particular significance to the initial positions used in this example.

[2]:
from hkl import E4CV
from ophyd import Component
from ophyd import PseudoSingle
from ophyd import SoftPositioner

class FourcQ(E4CV):
    # one pseudo axis for the q calculation engine
    q = Component(PseudoSingle)

    # four real axes (MUST specify in canonical order)
    omega = Component(SoftPositioner, init_pos=20)
    chi = Component(SoftPositioner, init_pos=90)
    phi = Component(SoftPositioner, init_pos=0)
    tth = Component(SoftPositioner, init_pos=40)  # "q" engine calls this "tth"

Create the diffractometer object.#

You specify the q calculation engine (engine="q") when you create the diffractometer object. Otherwise, the support will default to the hkl engine. Once the object is created, the calculation engine cannot be changed.

[3]:
fourcq = FourcQ("", name="fourcq", engine="q")

Test the q engine by calculating the angles associated with \(Q=1.00\) 1/angstrom. There is only one pseudo positioner so only one value is provided to the forward() calculation. Notice that only the tth position is computed.

[4]:
print(f"q to angle: {fourcq.forward(1) = }")
q to angle: fourcq.forward(1) = PosCalcE4CV(omega=20.0, chi=90.0, phi=0.0, tth=14.0785064531777)

Calculate the \(q\) associated with tth=1.0 degrees. While four real motors are defined, only tth is used for the calculation so only one value is provided to the inverse() calculation.

[5]:
print(f"angle to q: {fourcq.inverse(1) = }")
angle to q: fourcq.inverse(1) = FourcQPseudoPos(q=2.790877843251037)

Show the basic settings of the fourcq diffractometer.

[6]:
fourcq.wh()
===================== ================= =========
term                  value             axis_type
===================== ================= =========
diffractometer        fourcq
sample name           main
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           q
mode                  q
q                     2.790877843251037 pseudo
omega                 20                real
chi                   90                real
phi                   0                 real
tth                   40                real
===================== ================= =========

[6]:
<pyRestTable.rest_table.Table at 0x7f37c5da50d0>

Move fourcq to \(Q=1.0\) 1/Angstrom and show the settings again.

[7]:
fourcq.move(1)
fourcq.wh()
===================== ================= =========
term                  value             axis_type
===================== ================= =========
diffractometer        fourcq
sample name           main
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           q
mode                  q
q                     1.000000000000004 pseudo
omega                 20.0              real
chi                   90.0              real
phi                   0.0               real
tth                   14.0785064531777  real
===================== ================= =========

[7]:
<pyRestTable.rest_table.Table at 0x7f37c5de90a0>

Show all the fourcq diffractometer settings.

[8]:
fourcq.pa()
===================== ====================================================================
term                  value
===================== ====================================================================
diffractometer        fourcq
geometry              E4CV
class                 FourcQ
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           q
mode                  q
positions             ===== ========
                      name  value
                      ===== ========
                      omega 20.00000
                      chi   90.00000
                      phi   0.00000
                      tth   14.07851
                      ===== ========
constraints           ===== ========= ========== ================ ====
                      axis  low_limit high_limit value            fit
                      ===== ========= ========== ================ ====
                      omega -180.0    180.0      20.0             True
                      chi   -180.0    180.0      90.0             True
                      phi   -180.0    180.0      0.0              True
                      tth   -180.0    180.0      14.0785064531777 True
                      ===== ========= ========== ================ ====
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]]
                      ================ ===================================================
===================== ====================================================================

[8]:
<pyRestTable.rest_table.Table at 0x7f37c5da5220>

Move to a different wavelength (1.00 Angstrom) and move back to the same \(Q\) of 1.000 1/Angstrom.

[9]:
fourcq.energy.set(A_KEV / 1.0)
fourcq.move(1)
fourcq.wh()
===================== ================== =========
term                  value              axis_type
===================== ================== =========
diffractometer        fourcq
sample name           main
energy (keV)          12.39842
wavelength (angstrom) 1.00000
calc engine           q
mode                  q
q                     1.0000000000000022 pseudo
omega                 20.0               real
chi                   90.0               real
phi                   0.0                real
tth                   9.128558416134153  real
===================== ================== =========

[9]:
<pyRestTable.rest_table.Table at 0x7f37c5da5160>