{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# **hkl_soleil** UB matrix : Set directly\n", "\n", "$UB$ is the 3x3 orientation matrix used to transform coordinates between\n", "reciprocal space directions (of the crystal lattice planes) and the rotational\n", "axes of the diffractometer.\n", "\n", "It is possible to set $UB$ directly, as shown below.\n", "\n", "Alternatively, $UB$ can be [calculated](./hkl_soleil-ub_calc.ipynb) from two\n", "non-parallel reflections, using the method of Busing and Levy (*Acta Cryst*\n", "**22** (1967) 457)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Quick example\n", "\n", "In **hklpy2**, the $UB$ matrix is a property of the *solver*. For a\n", "diffractometer object named `diffractometer`, set $UB$ such as:\n", "\n", "```py\n", "diffractometer.core.solver.UB = [\n", " [0.5, 6.24, -0.5],\n", " [-0.5, -0.5, 6.24],\n", " [-6.24, -0.5, -0.5],\n", "]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create a diffractometer object\n", "\n", "First, create a diffractometer object that uses the `\"hkl_soleil\"` solver with\n", "the `\"hkl\"` computation engine. This solver provides support for many\n", "diffractometer geometries. This example will use the simulated 4-circle\n", "geometry from the solver's `\"E4CV\"`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from hklpy2 import creator\n", "\n", "diffractometer = creator(name=\"diffractometer\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Defaults\n", "\n", "The diffractometer object starts with a default sample. The structure is cubic ($a=b=c$, 90 degree corners)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Sample(name='sample', lattice=Lattice(a=1, system='cubic'))" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.sample" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the sample the solver will be using.\n", "\n", "$U$ is the orientation of the sample's crystal lattice as mounted on the diffractometer\n", "sample holder. The default is to assume $U=I$, where $I$ is the 3x3 identity\n", "matrix. \n", "\n", "$U$ is provided by the *solver*, in this case `\"hkl_soleil\"`. From the\n", "diffractometer object, the solver's default sample $U$ is accessed through a\n", "couple software layers:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[1.0, -2.28495e-07, -4.7217e-08],\n", " [2.28495e-07, 1.0, 9.0229e-08],\n", " [4.7216e-08, -9.0229e-08, 1.0]]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.core.solver.U" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The default $UB$ (without knowing how the crystal planes are oriented with\n", "respect to the diffractometer) of this cubic crystal is $(2\\pi/a) I$ where $I$\n", "is the 3x3 identity matrix and $a$ is the lattice parameter. Like $U$, this is\n", "provided by the *solver*, in this case `\"hkl_soleil\"`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[6.283183866345, 9.31516e-07, -3.0274e-08],\n", " [1.435676e-06, 6.28318625087, -1.044844e-06],\n", " [2.9667e-07, -5.66923e-07, 6.283184307075]]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.core.solver.UB" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$UB$ is used to transform *(hkl)* to angles (method: `forward()`) and angles to\n", "*(hkl)* (method: `inverse()`). These transformations are fundamental to\n", "diffractometer operations, so they are provided to the diffractometer object\n", "directly.\n", "\n", "Here, we compute the angles for the (111) orientation:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Hklpy2DiffractometerRealPos(omega=59.999997499677, chi=35.264399309167, phi=45.000003331627, tth=119.999994999354)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.forward(1, 1, 1)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'bissector'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.core.solver.mode" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, convert *(hkl)* from these angles. Because we have truncated the numerical\n", "precision, we should not expect the precise values of (1.0, 1.0, 1.0)." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Hklpy2DiffractometerPseudoPos(h=-1.003252647315, k=0.993463442978, l=-1.003252287813)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.inverse(-60, -35, 45, -120)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Set UB to new matrix\n", "\n", "The solver's $UB$ matrix can be re-defined by providing a new Python matrix.\n", "Here is a matrix for a cubic sample, oriented previously:\n", "\n", "```\n", "[[0.545455316412, -6.239788968842, -0.495930309978],\n", " [-0.547615630691, -0.543471652084, 6.235639164201],\n", " [-6.235463558747, -0.498103654451, -0.591011669061]]\n", "```" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[0.545457225972, -6.239789175362, -0.495932073845],\n", " [-0.547614402008, -0.543469679002, 6.235639497651],\n", " [-6.235462520451, -0.498102997207, -0.591014567627]]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.core.solver.UB = [\n", " [0.545455316412, -6.239788968842, -0.495930309978],\n", " [-0.547615630691, -0.543471652084, 6.235639164201],\n", " [-6.235463558747, -0.498103654451, -0.591011669061]\n", "]\n", "diffractometer.core.solver.UB" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[0.086812235355, -0.993093022199, -0.078929620878],\n", " [-0.087155560669, -0.086495919275, 0.992432548939],\n", " [-0.99240492947, -0.079276132651, -0.094062483248]]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.core.solver.U" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Try it out\n", "\n", "First, compute $(hkl)$ from a set of *reals*." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Hklpy2DiffractometerPseudoPos(h=0.82195245423, k=0.989925535285, l=1.159499834922)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.inverse(-60, -35, 45, -120)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There can be more than one solution to the `forward()` transformation; many combinations of *reals* can be represented by the same set of *pseudos*. A *solver*'s geometry provides one or *modes* which provide additional constraints or relationships to limit the `forward()` computation.\n", "\n", "The E4CV geometry's `\"bissector\"` mode adds this relationship: `tth = 2 * omega`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'bissector'" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.core.solver.mode" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Still, there can be more than one solution returned by the *solver*. In **hklpy2**, the default is to pick the first solution in the list returned from the *solver*, as shown next:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer._forward_solution" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Show the first computed solution in `\"bissector\"` mode for the $(1,1,1)$ reflection:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Hklpy2DiffractometerRealPos(omega=-60.000016806342, chi=-28.211236858993, phi=40.202345307388, tth=-120.000033612684)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diffractometer.forward(1, 1, 1)" ] } ], "metadata": { "kernelspec": { "display_name": "hklpy2", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 2 }