Non-exhaustive test of E6C calculations

Verify hklpy (from its interface to the hkl library) computations of orientation, U, UB, and rotation directions.

With the aid of Yong Chu’s mental math.

TL;DR appears to function as documented and as expected


Note: This example is available as a Jupyter notebook from the hklpy source code website: https://github.com/bluesky/hklpy/tree/main/examples

Import the python libraries needed

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import gi
gi.require_version('Hkl', '5.0')
from hkl.calc import CalcE6C
from hkl.util import Lattice

Initialize the calculation engine

calc = CalcE6C(engine='hkl')
calc.engine.mode = 'constant_chi_vertical'
calc.wavelength = 1.  # Angstrom

Setup the crystal lattice

lattice = Lattice(a=1, b=1, c=1, alpha=90, beta=90, gamma=90)
sample = calc.new_sample('sample0', lattice=lattice)

print('lattice', sample.lattice)
print('physical axes', calc.physical_axes)
print('pseudo axes', calc.pseudo_axes)
print('omega parameter is', calc['omega'])
lattice LatticeTuple(a=1.0, b=1.0, c=1.0, alpha=90.0, beta=90.0, gamma=90.0)
physical axes OrderedDict([('mu', 0.0), ('omega', 0.0), ('chi', 0.0), ('phi', 0.0), ('gamma', 0.0), ('delta', 0.0)])
pseudo axes OrderedDict([('h', 0.0), ('k', 0.0), ('l', 0.0)])
omega parameter is CalcParameter(name='omega', limits=(-180.0, 180.0), value=0.0, fit=True, inverted=False, units='Degree')

Compute the UB matrix from two reflections

# define the wavelength
calc.wavelength = 1.0

# checking orientation of delta
r1p = calc.Position(mu=0.0, omega=30.0, chi=0.0, phi=0.0, gamma=0., delta=60.)
r1 = sample.add_reflection(0, 0, 1, position=r1p)
r2p = calc.Position(mu=0.0, omega=120.0, chi=0.0, phi=0.0, gamma=0, delta=60.)
r2 = sample.add_reflection(1, 0, 0, position=r2p)
sample.compute_UB(r1, r2)
1

Show the computed U matrix

sample.U
array([[ 1.00000000e+00, -3.74939946e-33,  6.12323400e-17],
       [ 0.00000000e+00,  1.00000000e+00,  6.12323400e-17],
       [-6.12323400e-17, -6.12323400e-17,  1.00000000e+00]])

Show the computed UB matrix

sample.UB
array([[ 6.28318531e+00, -3.84734139e-16,  0.00000000e+00],
       [ 0.00000000e+00,  6.28318531e+00,  0.00000000e+00],
       [-3.84734139e-16, -3.84734139e-16,  6.28318531e+00]])

Calculate various (hkl) given motor positions

(010)

calc.physical_positions = calc.Position(mu=0.0, omega=30.0, chi=90.0, phi=0.0, gamma=0, delta=60.)
print('pseudo should be (0,1,0)=', calc.pseudo_axes)
pseudo should be (0,1,0)= OrderedDict([('h', 1.7187070131469975e-16), ('k', 0.9999999999999998), ('l', 1.7919353632379053e-16)])
# checking orientation of delta
calc.physical_positions = calc.Position(mu=30.0, omega=0.0, chi=0.0, phi=0.0, gamma=60., delta=0.)
print('pseudo should be (0,1,0)=', calc.pseudo_axes)
pseudo should be (0,1,0)= OrderedDict([('h', 5.729023377156659e-17), ('k', 0.9999999999999999), ('l', 6.123233995736765e-17)])

(0 -1 0)

calc.physical_positions = calc.Position(mu=0, omega=30., chi=-90.0, phi=0.0, gamma=0., delta=60.)
print('pseudo should be (0,-1,0)=', calc.pseudo_axes)
pseudo should be (0,-1,0)= OrderedDict([('h', 0.0), ('k', -0.9999999999999998), ('l', 5.672885640905521e-17)])

(-1 0 0)

calc.physical_positions = calc.Position(mu=0.0, omega=-60.0, chi=0.0, phi=0.0, gamma=0, delta=60.)
print('pseudo should be (-1,0,0)=', calc.pseudo_axes)
pseudo should be (-1,0,0)= OrderedDict([('h', -0.9999999999999999), ('k', 0.0), ('l', 2.291609350862664e-16)])

Diffracting upside-down now

Note that omega and phi only need to sum to +-120 (\(\omega+\varphi = \pm |120|\)), which reflects what the inverse calculations from the library give.

(100)

calc.physical_positions = calc.Position(mu=0.0, omega=-50.0, chi=0.0, phi=-70.0, gamma=0, delta=-60.)
print('pseudo should be (1,0,0)=', calc.pseudo_axes)

calc.physical_positions = calc.Position(mu=0.0, omega=-100.0, chi=0.0, phi=-20.0, gamma=0, delta=-60.)
print('pseudo should be (1,0,0)=', calc.pseudo_axes)

calc.physical_positions = calc.Position(mu=0.0, omega=100.0, chi=0.0, phi=-220.0, gamma=0, delta=-60.)
print('pseudo should be (1,0,0)=', calc.pseudo_axes)
pseudo should be (1,0,0)= OrderedDict([('h', 1.0), ('k', 0.0), ('l', 5.729023377156662e-17)])
pseudo should be (1,0,0)= OrderedDict([('h', 1.0), ('k', 0.0), ('l', 5.729023377156662e-17)])
pseudo should be (1,0,0)= OrderedDict([('h', 1.0), ('k', 0.0), ('l', 5.729023377156662e-17)])

(011)

calc.physical_positions = calc.Position(mu=0.0, omega=45.0, chi=45.0, phi=0.0, gamma=0, delta=90.)
print('pseudo should be (0,1,1)=', calc.pseudo_axes)
pseudo should be (0,1,1)= OrderedDict([('h', 3.4374140262939965e-16), ('k', 1.0), ('l', 1.0)])

Verify that \(\omega+\varphi = \pm |120|\) is kept.

# calculate all allowed combinations of motor positions, given hkl
solutions = calc.forward((1,0,0))
for sol in solutions:
    print("expecting ~120:", sol.omega + sol.phi)
expecting ~120: 119.9999999269113
expecting ~120: -119.9999999269113