UB : Save and Restore Crystal Orientation#
see: bluesky/hklpy#50
Objectives
Save the information defining the crystal orientation into the descriptor document
List runs that have orientation that can be restored
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 |
|
sample name |
|
sample lattice |
|
first reflection |
|
second reflection |
|
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).
The order of constraints is exactly the order of the
_reals
so those associations and conversions must be handled.We can’t just use the dictionaries for
reflection
andposition
ininfo
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
===================== ========= =========