UB : Save and Restore Crystal Orientation#

see: bluesky/hklpy#50

Objectives

  1. Save the information defining the crystal orientation into the descriptor document

  2. List runs that have orientation that can be restored

  3. Restore crystal orientation from a given Bluesky run


Data collection#

Setup for data collection#

Use a local, temporary, file-based databroker. It will reset after each restart of the notebook. Prepare to define the diffractometers needed here plus some items from the ophyd simulators.

[1]:
from bluesky import RunEngine
from bluesky.callbacks.best_effort import BestEffortCallback
import bluesky.plans as bp
import bluesky.plan_stubs as bps
import bluesky.preprocessors as bpp
import databroker
import hkl
from hkl import *
import numpy as np
import pyRestTable
from ophyd import Component, Device, EpicsSignal, Signal
from ophyd.signal import AttributeSignal, ArrayAttributeSignal
from ophyd.sim import *
import pandas as pd

bec = BestEffortCallback()
bec.disable_plots()
cat = databroker.temp().v2

RE = RunEngine({})
RE.subscribe(bec)
RE.subscribe(cat.v1.insert)
RE.md["notebook"] = "tst_UB_in_descriptor_document"
RE.md["objective"] = "Demonstrate UB matrix save & restore in descriptor document of bluesky run"

Build simulated 4-circle diffractometer#

Build two 4-circles so that we can test routines that differentiate between similar diffractometers. Use the second one to restore orientation saved from the first.

[2]:
class Fourc(SimulatedE4CV):
    pass

fourc = Fourc("", name="fourc")
fourc.energy.put(A_KEV / 1.54)
a0 = SI_LATTICE_PARAMETER
fourc.calc.new_sample("silicon", lattice=(a0, a0, a0, 90, 90, 90))
fourc.calc.sample.compute_UB(
    fourc.calc.sample.add_reflection(4, 0, 0, (-145.451, 0, 0, 69.0966)),
    fourc.calc.sample.add_reflection(0, 4, 0, (-145.451, 0, 90, 69.0966))
)
fourc.pa()

orange = Fourc("", name="orange")
orange.pa()
===================== ===========================================================================
term                  value
===================== ===========================================================================
diffractometer        fourc
geometry              E4CV
class                 Fourc
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           hkl
mode                  bissector
positions             ===== =======
                      name  value
                      ===== =======
                      omega 0.00000
                      chi   0.00000
                      phi   0.00000
                      tth   0.00000
                      ===== =======
constraints           ===== ========= ========== ===== ====
                      axis  low_limit high_limit value fit
                      ===== ========= ========== ===== ====
                      omega -180.0    180.0      0.0   True
                      chi   -180.0    180.0      0.0   True
                      phi   -180.0    180.0      0.0   True
                      tth   -180.0    180.0      0.0   True
                      ===== ========= ========== ===== ====
sample: silicon       ================= =========================================================
                      term              value
                      ================= =========================================================
                      unit cell edges   a=5.431020511, b=5.431020511, c=5.431020511
                      unit cell angles  alpha=90.0, beta=90.0, gamma=90.0
                      ref 1 (hkl)       h=4.0, k=0.0, l=0.0
                      ref 1 positioners omega=-145.45100, chi=0.00000, phi=0.00000, tth=69.09660
                      ref 2 (hkl)       h=0.0, k=4.0, l=0.0
                      ref 2 positioners omega=-145.45100, chi=0.00000, phi=90.00000, tth=69.09660
                      [U]               [[-1.22173048e-05 -1.00000000e+00  0.00000000e+00]
                                         [ 0.00000000e+00  0.00000000e+00  1.00000000e+00]
                                         [-1.00000000e+00  1.22173048e-05  0.00000000e+00]]
                      [UB]              [[-1.41342846e-05 -1.15690694e+00  7.08409844e-17]
                                         [ 0.00000000e+00  0.00000000e+00  1.15690694e+00]
                                         [-1.15690694e+00  1.41342846e-05  7.08392534e-17]]
                      ================= =========================================================
===================== ===========================================================================

===================== ====================================================================
term                  value
===================== ====================================================================
diffractometer        orange
geometry              E4CV
class                 Fourc
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           hkl
mode                  bissector
positions             ===== =======
                      name  value
                      ===== =======
                      omega 0.00000
                      chi   0.00000
                      phi   0.00000
                      tth   0.00000
                      ===== =======
constraints           ===== ========= ========== ===== ====
                      axis  low_limit high_limit value fit
                      ===== ========= ========== ===== ====
                      omega -180.0    180.0      0.0   True
                      chi   -180.0    180.0      0.0   True
                      phi   -180.0    180.0      0.0   True
                      tth   -180.0    180.0      0.0   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]]
                      ================ ===================================================
===================== ====================================================================

[2]:
<pyRestTable.rest_table.Table at 0x7f6f08106820>

Build simulators for other diffractometer geometries to test code that differentiates between various possibile sources for restore of orientation information.

[3]:
class Kappa(SimulatedK4CV):
    pass

kappa = Kappa("", name="kappa")
kappa.energy.put(A_KEV / 1.54)
a0 = SI_LATTICE_PARAMETER
kappa.calc.new_sample("silicon", lattice=(a0, a0, a0, 90, 90, 90))
kappa.calc.sample.compute_UB(
    kappa.calc.sample.add_reflection(4, 0, 0, (55.4507, 0, 90, -69.0966)),
    kappa.calc.sample.add_reflection(0, 4, 0, (-1.5950, 134.7568, 123.3554, -69.0966))
)
kappa.pa()
===================== =================================================================================
term                  value
===================== =================================================================================
diffractometer        kappa
geometry              K4CV
class                 Kappa
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
                      ====== =======
constraints           ====== ========= ========== ===== ====
                      axis   low_limit high_limit value fit
                      ====== ========= ========== ===== ====
                      komega -180.0    180.0      0.0   True
                      kappa  -180.0    180.0      0.0   True
                      kphi   -180.0    180.0      0.0   True
                      tth    -180.0    180.0      0.0   True
                      ====== ========= ========== ===== ====
sample: silicon       ================= ===============================================================
                      term              value
                      ================= ===============================================================
                      unit cell edges   a=5.431020511, b=5.431020511, c=5.431020511
                      unit cell angles  alpha=90.0, beta=90.0, gamma=90.0
                      ref 1 (hkl)       h=4.0, k=0.0, l=0.0
                      ref 1 positioners komega=55.45070, kappa=0.00000, kphi=90.00000, tth=-69.09660
                      ref 2 (hkl)       h=0.0, k=4.0, l=0.0
                      ref 2 positioners komega=-1.59500, kappa=134.75680, kphi=123.35540, tth=-69.09660
                      [U]               [[-1.74532925e-05 -6.22695871e-06  1.00000000e+00]
                                         [ 0.00000000e+00 -1.00000000e+00 -6.22695872e-06]
                                         [ 1.00000000e+00 -1.08680932e-10  1.74532925e-05]]
                      [UB]              [[-2.01918352e-05 -7.20401174e-06  1.15690694e+00]
                                         [ 0.00000000e+00 -1.15690694e+00 -7.20401174e-06]
                                         [ 1.15690694e+00 -1.25733795e-10  2.01918352e-05]]
                      ================= ===============================================================
===================== =================================================================================

[3]:
<pyRestTable.rest_table.Table at 0x7f6f46563610>
[4]:
class Sixc(SimulatedE6C):
    pass

sixc = Sixc("", name="sixc")
sixc.energy.put(A_KEV / 1.54)
a0 = SI_LATTICE_PARAMETER
sixc.calc.new_sample("silicon", lattice=(a0, a0, a0, 90, 90, 90))
sixc.calc.sample.compute_UB(
    sixc.calc.sample.add_reflection(4, 0, 0, (0, -145.451, 0, 0, 0, 69.0966)),
    sixc.calc.sample.add_reflection(0, 4, 0, (0, -145.451, 90, 0, 0, 69.0966))
)
sixc.pa()
===================== ========================================================================================================
term                  value
===================== ========================================================================================================
diffractometer        sixc
geometry              E6C
class                 Sixc
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           hkl
mode                  bissector_vertical
positions             ===== =======
                      name  value
                      ===== =======
                      mu    0.00000
                      omega 0.00000
                      chi   0.00000
                      phi   0.00000
                      gamma 0.00000
                      delta 0.00000
                      ===== =======
constraints           ===== ========= ========== ===== ====
                      axis  low_limit high_limit value fit
                      ===== ========= ========== ===== ====
                      mu    -180.0    180.0      0.0   True
                      omega -180.0    180.0      0.0   True
                      chi   -180.0    180.0      0.0   True
                      phi   -180.0    180.0      0.0   True
                      gamma -180.0    180.0      0.0   True
                      delta -180.0    180.0      0.0   True
                      ===== ========= ========== ===== ====
sample: silicon       ================= ======================================================================================
                      term              value
                      ================= ======================================================================================
                      unit cell edges   a=5.431020511, b=5.431020511, c=5.431020511
                      unit cell angles  alpha=90.0, beta=90.0, gamma=90.0
                      ref 1 (hkl)       h=4.0, k=0.0, l=0.0
                      ref 1 positioners mu=0.00000, omega=-145.45100, chi=0.00000, phi=0.00000, gamma=0.00000, delta=69.09660
                      ref 2 (hkl)       h=0.0, k=4.0, l=0.0
                      ref 2 positioners mu=0.00000, omega=-145.45100, chi=90.00000, phi=0.00000, gamma=0.00000, delta=69.09660
                      [U]               [[-1.22173048e-05 -1.22173048e-05 -1.00000000e+00]
                                         [ 0.00000000e+00 -1.00000000e+00  1.22173048e-05]
                                         [-1.00000000e+00  1.49262536e-10  1.22173048e-05]]
                      [UB]              [[-1.41342846e-05 -1.41342846e-05 -1.15690694e+00]
                                         [ 0.00000000e+00 -1.15690694e+00  1.41342846e-05]
                                         [-1.15690694e+00  1.72682934e-10  1.41342846e-05]]
                      ================= ======================================================================================
===================== ========================================================================================================

[4]:
<pyRestTable.rest_table.Table at 0x7f6f080a97c0>

Collect data with all the diffractometers#

Show data collection with and without the orientation information.

Tip: To save orientation information, add the diffractometer as an additional detector. That’s all! Works with any scan that supports multiple detectors.

[5]:
def scan_all():
    ### count runs ###
    # this run will not save orientation information
    yield from bp.count([noisy_det])
    # this run _will_ save orientation information for fourc
    yield from bp.count([noisy_det, fourc])
    # this run _will_ save orientation information for several diffractometers
    yield from bp.count([noisy_det, fourc, orange, kappa, sixc])

    ### scan runs ###
    yield from bp.scan([noisy_det], fourc.h, 0.9, 1.1, 2)
    yield from bp.scan([noisy_det, fourc], fourc.h, 0.9, 1.1, 2)
    yield from bp.scan([noisy_det], kappa.h, 0.9, 1.1, 2)
    yield from bp.scan([noisy_det, kappa], kappa.h, 0.9, 1.1, 2)
    yield from bp.scan([noisy_det], sixc.h, 0.9, 1.1, 2)
    yield from bp.scan([noisy_det, sixc], sixc.h, 0.9, 1.1, 2)

    ### mesh runs at the (100) ###
    # first, move to the (100)
    yield from bps.mv(fourc.h, 1, fourc.k, 0, fourc.l, 0)
    yield from bp.rel_grid_scan([noisy_det], fourc.h, -0.1, 0.1, 3, fourc.k, -0.1, 0.1, 3)
    yield from bp.rel_grid_scan([noisy_det, fourc], fourc.h, -0.1, 0.1, 3, fourc.k, -0.1, 0.1, 3)

Run the scans, gather all the uids into a variable to be ignored. That way, they do not print.

[6]:
_uids = RE(scan_all())


Transient Scan ID: 1     Time: 2022-06-13 00:06:20
Persistent Unique Scan ID: 'd321e9b5-757b-42fe-a57b-2a9eb971aec6'
New stream: 'primary'
+-----------+------------+------------+
|   seq_num |       time |  noisy_det |
+-----------+------------+------------+
|         1 | 00:06:20.1 |      0.980 |
+-----------+------------+------------+
generator count ['d321e9b5'] (scan num: 1)





Transient Scan ID: 2     Time: 2022-06-13 00:06:20
Persistent Unique Scan ID: '88782b29-df5b-4fd7-8b89-6597f814f733'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
|   seq_num |       time |    fourc_h |    fourc_k |    fourc_l |  noisy_det |
+-----------+------------+------------+------------+------------+------------+
|         1 | 00:06:20.2 |      0.000 |      0.000 |      0.000 |      0.953 |
+-----------+------------+------------+------------+------------+------------+
generator count ['88782b29'] (scan num: 2)





Transient Scan ID: 3     Time: 2022-06-13 00:06:20
Persistent Unique Scan ID: '40a645b8-9397-4aad-aeea-fc8cb12d8ced'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
|   seq_num |       time |     sixc_h |     sixc_k |     sixc_l |  noisy_det |    kappa_h |    kappa_k |    kappa_l |   orange_h |   orange_k |   orange_l |    fourc_h |    fourc_k |    fourc_l |
+-----------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
|         1 | 00:06:20.4 |     -0.000 |      0.000 |      0.000 |      1.085 |      0.000 |     -0.000 |      0.000 |      0.000 |      0.000 |      0.000 |      0.000 |      0.000 |      0.000 |
+-----------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+
generator count ['40a645b8'] (scan num: 3)





Transient Scan ID: 4     Time: 2022-06-13 00:06:20
Persistent Unique Scan ID: '064d007a-363d-43a5-ab22-3d78b2ff5b0a'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |    fourc_h |  noisy_det |
+-----------+------------+------------+------------+
|         1 | 00:06:20.5 |      0.900 |      1.021 |
|         2 | 00:06:20.5 |      1.100 |      1.074 |
+-----------+------------+------------+------------+
generator scan ['064d007a'] (scan num: 4)





Transient Scan ID: 5     Time: 2022-06-13 00:06:20
Persistent Unique Scan ID: 'c5e1e9b6-c8af-48fe-8a22-e68d39a3fc3a'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
|   seq_num |       time |    fourc_h |    fourc_k |    fourc_l |  noisy_det |
+-----------+------------+------------+------------+------------+------------+
|         1 | 00:06:20.7 |      0.900 |      0.000 |      0.000 |      1.019 |
|         2 | 00:06:20.7 |      1.100 |      0.000 |      0.000 |      0.936 |
+-----------+------------+------------+------------+------------+------------+
generator scan ['c5e1e9b6'] (scan num: 5)





Transient Scan ID: 6     Time: 2022-06-13 00:06:20
Persistent Unique Scan ID: '57c5a944-ac79-45f7-979a-ac5e4372c1ed'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |    kappa_h |  noisy_det |
+-----------+------------+------------+------------+
|         1 | 00:06:20.9 |      0.900 |      0.974 |
|         2 | 00:06:20.9 |      1.100 |      0.944 |
+-----------+------------+------------+------------+
generator scan ['57c5a944'] (scan num: 6)





Transient Scan ID: 7     Time: 2022-06-13 00:06:21
Persistent Unique Scan ID: 'f1ae9421-dbbe-4a60-9fa4-6692e077514a'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
|   seq_num |       time |    kappa_h |    kappa_k |    kappa_l |  noisy_det |
+-----------+------------+------------+------------+------------+------------+
|         1 | 00:06:21.1 |      0.900 |      0.000 |      0.000 |      1.082 |
|         2 | 00:06:21.2 |      1.100 |      0.000 |      0.000 |      1.009 |
+-----------+------------+------------+------------+------------+------------+
generator scan ['f1ae9421'] (scan num: 7)





Transient Scan ID: 8     Time: 2022-06-13 00:06:21
Persistent Unique Scan ID: '1770d5b4-f6a5-4c7c-a210-8e6996474892'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |     sixc_h |  noisy_det |
+-----------+------------+------------+------------+
|         1 | 00:06:21.3 |      0.900 |      1.076 |
|         2 | 00:06:21.3 |      1.100 |      0.979 |
+-----------+------------+------------+------------+
generator scan ['1770d5b4'] (scan num: 8)





Transient Scan ID: 9     Time: 2022-06-13 00:06:21
Persistent Unique Scan ID: '8515819d-27af-4927-acb7-cd07d0836039'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
|   seq_num |       time |     sixc_h |     sixc_k |     sixc_l |  noisy_det |
+-----------+------------+------------+------------+------------+------------+
|         1 | 00:06:21.4 |      0.900 |      0.000 |      0.000 |      1.053 |
|         2 | 00:06:21.4 |      1.100 |      0.000 |      0.000 |      1.068 |
+-----------+------------+------------+------------+------------+------------+
generator scan ['8515819d'] (scan num: 9)





Transient Scan ID: 10     Time: 2022-06-13 00:06:21
Persistent Unique Scan ID: 'c43f5687-5793-4f60-aefe-77ab2dd835f1'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
|   seq_num |       time |    fourc_h |    fourc_k |    fourc_l |  noisy_det |
+-----------+------------+------------+------------+------------+------------+
|         1 | 00:06:21.6 |      0.900 |     -0.100 |     -0.000 |      0.975 |
|         2 | 00:06:21.6 |      0.900 |     -0.000 |     -0.000 |      1.003 |
|         3 | 00:06:21.6 |      0.900 |      0.100 |     -0.000 |      0.971 |
|         4 | 00:06:21.6 |      1.000 |     -0.100 |     -0.000 |      0.979 |
|         5 | 00:06:21.6 |      1.000 |     -0.000 |     -0.000 |      0.945 |
|         6 | 00:06:21.6 |      1.000 |      0.100 |     -0.000 |      1.095 |
|         7 | 00:06:21.6 |      1.100 |     -0.100 |     -0.000 |      0.967 |
|         8 | 00:06:21.6 |      1.100 |     -0.000 |     -0.000 |      1.032 |
|         9 | 00:06:21.6 |      1.100 |      0.100 |     -0.000 |      0.988 |
+-----------+------------+------------+------------+------------+------------+
generator rel_grid_scan ['c43f5687'] (scan num: 10)





Transient Scan ID: 11     Time: 2022-06-13 00:06:21
Persistent Unique Scan ID: 'ea4b3a85-6dbc-4287-98a4-93ad254e4a47'
New stream: 'primary'
+-----------+------------+------------+------------+------------+------------+
|   seq_num |       time |    fourc_h |    fourc_k |    fourc_l |  noisy_det |
+-----------+------------+------------+------------+------------+------------+
|         1 | 00:06:21.8 |      0.900 |     -0.100 |     -0.000 |      0.920 |
|         2 | 00:06:21.8 |      0.900 |     -0.000 |     -0.000 |      0.924 |
|         3 | 00:06:21.8 |      0.900 |      0.100 |     -0.000 |      1.032 |
|         4 | 00:06:21.8 |      1.000 |     -0.100 |     -0.000 |      1.080 |
|         5 | 00:06:21.8 |      1.000 |     -0.000 |     -0.000 |      1.056 |
|         6 | 00:06:21.9 |      1.000 |      0.100 |     -0.000 |      1.014 |
|         7 | 00:06:21.9 |      1.100 |     -0.100 |     -0.000 |      0.945 |
|         8 | 00:06:21.9 |      1.100 |     -0.000 |     -0.000 |      1.100 |
|         9 | 00:06:21.9 |      1.100 |      0.100 |     -0.000 |      0.963 |
+-----------+------------+------------+------------+------------+------------+
generator rel_grid_scan ['ea4b3a85'] (scan num: 11)




Show the orientation information that was collected#

Show the full contents of the descriptor document (primary stream) for the fourc “detector” from the run with scan_id=5. This is where the orientation information is saved. You may need to expand the Data variables row to see all the orientation information.

[7]:
cat[5].primary.config["fourc"].read()
[7]:
<xarray.Dataset>
Dimensions:                    (time: 2, dim_0: 6, dim_1: 6, dim_2: 3,
                                dim_3: 3, dim_4: 3, dim_5: 3, dim_6: 2,
                                dim_7: 3, dim_8: 4, dim_9: 4, dim_10: 4,
                                dim_11: 21)
Coordinates:
  * time                       (time) float64 1.655e+09 1.655e+09
Dimensions without coordinates: dim_0, dim_1, dim_2, dim_3, dim_4, dim_5,
                                dim_6, dim_7, dim_8, dim_9, dim_10, dim_11
Data variables: (12/21)
    fourc_energy               (time) float64 8.051 8.051
    fourc_energy_units         (time) <U3 'keV' 'keV'
    fourc_energy_offset        (time) int64 0 0
    fourc_geometry_name        (time) <U4 'E4CV' 'E4CV'
    fourc_class_name           (time) <U5 'Fourc' 'Fourc'
    fourc_sample_name          (time) <U7 'silicon' 'silicon'
    ...                         ...
    fourc__hklpy_version       (time) <U23 '1.0.1+10.ge87af6a.dirty' '1.0.1+1...
    fourc__pseudos             (time, dim_7) <U1 'h' 'k' 'l' 'h' 'k' 'l'
    fourc__reals               (time, dim_8) <U5 'omega' 'chi' ... 'phi' 'tth'
    fourc__constraints         (time, dim_9, dim_10) float64 -180.0 ... 1.0
    fourc__mode                (time) <U9 'bissector' 'bissector'
    fourc_orientation_attrs    (time, dim_11) <U19 'orientation_attrs' ... '_...

Show orientation that was saved#

In scan_id=3, orientation information from 4 different diffractometers was saved with the run. Show what is available from each of those diffractometers. The columns in the next table are the diffractometers, the rows are the orientation information saved for each.

[8]:
roi = run_orientation_info(cat[3])
pd.DataFrame(roi)
[8]:
fourc kappa orange sixc
energy 8.050922 8.050922 8.0 8.050922
energy_units keV keV keV keV
energy_offset 0 0 0 0
geometry_name E4CV K4CV E4CV E6C
class_name Fourc Kappa Fourc Sixc
sample_name silicon silicon main silicon
lattice [5.431020511, 5.431020511, 5.431020511, 90.0, ... [5.431020511, 5.431020511, 5.431020511, 90.0, ... [1.54, 1.54, 1.54, 90.0, 90.0, 90.0] [5.431020511, 5.431020511, 5.431020511, 90.0, ...
lattice_reciprocal [1.156906937555034, 1.156906937555034, 1.15690... [1.156906937555034, 1.156906937555034, 1.15690... [4.079990459207523, 4.079990459207523, 4.07999... [1.156906937555034, 1.156906937555034, 1.15690...
U [[-1.2217304763832563e-05, -0.9999999999253688... [[-1.7453292519418068e-05, -6.226958714415449e... [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, ... [[-1.2217304763832563e-05, -1.221730476200898e...
UB [[-1.413428463950206e-05, -1.1569069374686927,... [[-2.0191835198892143e-05, -7.204011736576008e... [[4.079990459207523, -2.4982736282101165e-16, ... [[-1.413428463950206e-05, -1.4134284637392341e...
reflections_details [{'reflection': {'h': 4.0, 'k': 0.0, 'l': 0.0}... [{'reflection': {'h': 4.0, 'k': 0.0, 'l': 0.0}... [] [{'reflection': {'h': 4.0, 'k': 0.0, 'l': 0.0}...
ux -90.0 19.635305 0.0 -45.0
uy 0.0 89.998938 0.0 -89.99901
uz 90.0007 160.364695 0.0 135.0
diffractometer_name fourc kappa orange sixc
_hklpy_version 1.0.1+10.ge87af6a.dirty 1.0.1+10.ge87af6a.dirty 1.0.1+10.ge87af6a.dirty 1.0.1+10.ge87af6a.dirty
_pseudos [h, k, l] [h, k, l] [h, k, l] [h, k, l]
_reals [omega, chi, phi, tth] [komega, kappa, kphi, tth] [omega, chi, phi, tth] [mu, omega, chi, phi, gamma, delta]
_constraints [[-180.0, 180.0, 0.0, 1.0], [-180.0, 180.0, 0.... [[-180.0, 180.0, 0.0, 1.0], [-180.0, 180.0, 0.... [[-180.0, 180.0, 0.0, 1.0], [-180.0, 180.0, 0.... [[-180.0, 180.0, 0.0, 1.0], [-180.0, 180.0, 0....
_mode bissector bissector bissector bissector_vertical
orientation_attrs [orientation_attrs, geometry_name, class_name,... [orientation_attrs, geometry_name, class_name,... [orientation_attrs, geometry_name, class_name,... [orientation_attrs, geometry_name, class_name,...

Show runs with orientation information#

Since a given run (scan_id) may have more than one set of orientation information, corresponding to more than one diffractometer, report for each when found. Here, extra columns are reported for energy & units, and the crystal lattice parameters. The names are taken from the above table. (They must be one of the names in the orientation_attrs list.)

Use this type of listing to determine which scan_id and diffractometer_name has the orientation you wish to recover. If the scan_id is not unique, identify the run with the uid (as a string, such as cat["007abcd"]).

[9]:
list_orientation_runs(cat, "energy", "energy_units", "lattice")
[9]:
scan_id sample_name diffractometer_name geometry_name energy energy_units lattice uid
0 2 silicon fourc E4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... 88782b2
1 3 silicon fourc E4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... 40a645b
2 3 silicon kappa K4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... 40a645b
3 3 main orange E4CV 8.000000 keV [1.54, 1.54, 1.54, 90.0, 90.0, 90.0] 40a645b
4 3 silicon sixc E6C 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... 40a645b
5 5 silicon fourc E4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... c5e1e9b
6 7 silicon kappa K4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... f1ae942
7 9 silicon sixc E6C 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... 8515819
8 10 silicon fourc E4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... c43f568
9 11 silicon fourc E4CV 8.050922 keV [5.431020511, 5.431020511, 5.431020511, 90.0, ... ea4b3a8

Restore orientation information#

This demo will restore the orientation information from a fourc run (choosing scan_id=2) to the orange diffractometer. They have the same geometry_name so the information is compatible.

First, get the orientation information from the chosen run. Show the information for the fourc diffractometer.

[10]:
info = run_orientation_info(cat[2])

import pprint
pprint.pprint(info["fourc"])
{'U': [[-1.2217304763832563e-05, -0.9999999999253688, 0.0],
       [0.0, 0.0, 1.0],
       [-0.9999999999253688, 1.2217304763832563e-05, 0.0]],
 'UB': [[-1.413428463950206e-05, -1.1569069374686927, 7.084098436944218e-17],
        [0.0, 0.0, 1.156906937555034],
        [-1.1569069374686927, 1.41342846395729e-05, 7.083925341879798e-17]],
 '_constraints': [[-180.0, 180.0, 0.0, 1.0],
                  [-180.0, 180.0, 0.0, 1.0],
                  [-180.0, 180.0, 0.0, 1.0],
                  [-180.0, 180.0, 0.0, 1.0]],
 '_hklpy_version': '1.0.1+10.ge87af6a.dirty',
 '_mode': 'bissector',
 '_pseudos': ['h', 'k', 'l'],
 '_reals': ['omega', 'chi', 'phi', 'tth'],
 'class_name': 'Fourc',
 'diffractometer_name': 'fourc',
 'energy': 8.050921974025975,
 'energy_offset': 0,
 'energy_units': 'keV',
 'geometry_name': 'E4CV',
 'lattice': [5.431020511, 5.431020511, 5.431020511, 90.0, 90.0, 90.0],
 'lattice_reciprocal': [1.156906937555034,
                        1.156906937555034,
                        1.156906937555034,
                        90.00000000000001,
                        90.00000000000001,
                        90.00000000000001],
 'orientation_attrs': ['orientation_attrs',
                       'geometry_name',
                       'class_name',
                       'UB',
                       'U',
                       'ux',
                       'uy',
                       'uz',
                       'energy',
                       'energy_units',
                       'energy_offset',
                       'sample_name',
                       'lattice',
                       'lattice_reciprocal',
                       'reflections_details',
                       '_pseudos',
                       '_reals',
                       '_constraints',
                       '_mode',
                       'diffractometer_name',
                       '_hklpy_version'],
 'reflections_details': [{'flag': 1,
                          'orientation_reflection': True,
                          'position': {'chi': 0.0,
                                       'omega': -145.451,
                                       'phi': 0.0,
                                       'tth': 69.0966},
                          'reflection': {'h': 4.0, 'k': 0.0, 'l': 0.0},
                          'wavelength': 1.5399999999999998},
                         {'flag': 1,
                          'orientation_reflection': True,
                          'position': {'chi': 0.0,
                                       'omega': -145.451,
                                       'phi': 90.0,
                                       'tth': 69.0966},
                          'reflection': {'h': 0.0, 'k': 4.0, 'l': 0.0},
                          'wavelength': 1.5399999999999998}],
 'sample_name': 'silicon',
 'ux': -90.0,
 'uy': 0.0,
 'uz': 90.00070000000002}

Earlier, the sample and orientation were setup on the fourc with these steps:

fourc.energy.put(A_KEV / 1.54)
a0 = SI_LATTICE_PARAMETER
fourc.calc.new_sample("silicon", lattice=(a0, a0, a0, 90, 90, 90))
fourc.calc.sample.compute_UB(
    fourc.calc.sample.add_reflection(4, 0, 0, (-145.451, 0, 0, 69.0966)),
    fourc.calc.sample.add_reflection(0, 4, 0, (-145.451, 0, 90, 69.0966))
)

We have all the information here to repeat those steps for the orange diffractometer (same E4CV geometry).

term

recovered orientation

energy

info["fourc"]["energy"]

sample name

info["fourc"]["sample_name"]

sample lattice

info["fourc"]["lattice"]

first reflection

info["fourc"]["reflections_details"][0]: ["reflection"] and ["position"]

second reflection

info["fourc"]["reflections_details"][1]: ["reflection"] and ["position"]

The energy and sample info are ready to use. Both the constraints and reflections info will take a bit of reformatting.

Due to issues with how bluesky writes data, the constraints info was written with all floating point numbers (including the boolean for the fit parameter).

  1. The order of constraints is exactly the order of the _reals so those associations and conversions must be handled.

  2. We can’t just use the dictionaries for reflection and position in info since order is important and those are recovered in alphabetical order. That’s why the ordered lists for pseudo and real positioners have been saved with the orientation information. Those lists provide the canonical order for any diffractometer geometry.

Before restoring, need to check the target diffractometer to see if it already has these settings.

[11]:
print(f"{info['fourc']['energy'] = }")
print(f"{info['fourc']['energy_units'] = }")
print(f"{info['fourc']['energy_offset'] = }")
print(f"{orange.energy.get() = }")
print(f"{orange.energy_units.get() = }")
print(f"{orange.energy_offset.get() = }")
print()
print(f"{info['fourc']['sample_name'] = }")
print(f"{list(orange.calc._samples.keys()) = }")
print()
print(f"{orange.calc._samples = }")
info['fourc']['energy'] = 8.050921974025975
info['fourc']['energy_units'] = 'keV'
info['fourc']['energy_offset'] = 0
orange.energy.get() = 8.0
orange.energy_units.get() = 'keV'
orange.energy_offset.get() = 0

info['fourc']['sample_name'] = 'silicon'
list(orange.calc._samples.keys()) = ['main']

orange.calc._samples = {'main': HklSample(name='main', lattice=LatticeTuple(a=1.54, b=1.54, c=1.54, alpha=90.0, beta=90.0, gamma=90.0), ux=Parameter(name='None (internally: ux)', limits=(min=-180.0, max=180.0), value=0.0, fit=True, inverted=False, units='Degree'), uy=Parameter(name='None (internally: uy)', limits=(min=-180.0, max=180.0), value=0.0, fit=True, inverted=False, units='Degree'), uz=Parameter(name='None (internally: uz)', limits=(min=-180.0, max=180.0), value=0.0, fit=True, inverted=False, units='Degree'), U=array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]]), UB=array([[ 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]]), reflections=[])}

We need to change the energy (units and offset do not need to be changed), create a new sample, define the two reflections, then compute UB. Then compare that computed UB with the recovered value.

Restore the recovered fourc orientation into orange#

[12]:
from hkl import restore_orientation
orange = Fourc("", name="orange")  # TODO: remove this line before publishing
restore_orientation(info["fourc"], orange)

Compare the UB matrices. (Convert the recovered UB matrix to a numpy array to match the type in the diffractometer. Expect True in all cells of the 3x3 matrix.)

[13]:
print(f'{np.array(info["fourc"]["UB"]) = }')
print(f"{orange.UB.get() = }")
# finally, compare the two matrices cell by cell
np.array(info["fourc"]["UB"]) == orange.UB.get()
np.array(info["fourc"]["UB"]) = array([[-1.41342846e-05, -1.15690694e+00,  7.08409844e-17],
       [ 0.00000000e+00,  0.00000000e+00,  1.15690694e+00],
       [-1.15690694e+00,  1.41342846e-05,  7.08392534e-17]])
orange.UB.get() = array([[-1.41342846e-05, -1.15690694e+00,  7.08409844e-17],
       [ 0.00000000e+00,  0.00000000e+00,  1.15690694e+00],
       [-1.15690694e+00,  1.41342846e-05,  7.08392534e-17]])
[13]:
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]])
[14]:
orange.pa()
===================== ===========================================================================
term                  value
===================== ===========================================================================
diffractometer        orange
geometry              E4CV
class                 Fourc
energy (keV)          8.05092
wavelength (angstrom) 1.54000
calc engine           hkl
mode                  bissector
positions             ===== =======
                      name  value
                      ===== =======
                      omega 0.00000
                      chi   0.00000
                      phi   0.00000
                      tth   0.00000
                      ===== =======
constraints           ===== ========= ========== ===== ====
                      axis  low_limit high_limit value fit
                      ===== ========= ========== ===== ====
                      omega -180.0    180.0      0.0   True
                      chi   -180.0    180.0      0.0   True
                      phi   -180.0    180.0      0.0   True
                      tth   -180.0    180.0      0.0   True
                      ===== ========= ========== ===== ====
sample: silicon       ================= =========================================================
                      term              value
                      ================= =========================================================
                      unit cell edges   a=5.431020511, b=5.431020511, c=5.431020511
                      unit cell angles  alpha=90.0, beta=90.0, gamma=90.0
                      ref 1 (hkl)       h=4.0, k=0.0, l=0.0
                      ref 1 positioners omega=-145.45100, chi=0.00000, phi=0.00000, tth=69.09660
                      ref 2 (hkl)       h=0.0, k=4.0, l=0.0
                      ref 2 positioners omega=-145.45100, chi=0.00000, phi=90.00000, tth=69.09660
                      [U]               [[-1.22173048e-05 -1.00000000e+00  0.00000000e+00]
                                         [ 0.00000000e+00  0.00000000e+00  1.00000000e+00]
                                         [-1.00000000e+00  1.22173048e-05  0.00000000e+00]]
                      [UB]              [[-1.41342846e-05 -1.15690694e+00  7.08409844e-17]
                                         [ 0.00000000e+00  0.00000000e+00  1.15690694e+00]
                                         [-1.15690694e+00  1.41342846e-05  7.08392534e-17]]
                      ================= =========================================================
===================== ===========================================================================

[14]:
<pyRestTable.rest_table.Table at 0x7f6ef35eddf0>

Try to restore the recovered fourc orientation in kappa#

Try to share a recovered orientation from fourc, scan_id=2 with the kappa diffractometer.

[15]:
try:
    info = run_orientation_info(cat[2])
    restore_orientation(info["fourc"], kappa)
except ValueError as exc:
    print(f"{exc = }")
exc = ValueError('Geometries do not match: Orientation=E4CV, kappa=K4CV, will not restore.')

Restore is not be possible since the geometries are not identical. (To avoid stopping the notebook with an exception here, we used a try..except clause.)

Try to create a sample when it already exists#

Then, try to restore the sample and lattice when they already exist.

[16]:
from hkl import restore_sample

print(f"{kappa.sample_name.get() = }")
try:
    info = run_orientation_info(cat[2])
    restore_sample(info["fourc"], kappa)
    kappa.wh()
except Exception as exc:
    print(f"{exc = }")
kappa.sample_name.get() = 'silicon'
exc = ValueError("Sample 'silicon' already exists in kappa.")

Restore the energy#

[17]:
from hkl import restore_energy

# make a new kappa, then restore the sample
kappa = Kappa("", name="kappa")
print(f"{kappa.sample_name.get() = }")
try:
    info = run_orientation_info(cat[2])
    restore_energy(info["fourc"], kappa)
    restore_sample(info["fourc"], kappa)
    kappa.wh()
except Exception as exc:
    print(f"{exc = }")
kappa.sample_name.get() = 'main'
===================== ========= =========
term                  value     axis_type
===================== ========= =========
diffractometer        kappa
sample name           silicon
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
===================== ========= =========