{ "cells": [ { "cell_type": "markdown", "id": "687747a6-47b1-4d11-9d4c-64c071d735b3", "metadata": {}, "source": [ "## Demonstrate **hklpy2**'s API\n", "\n", "A working notebook as the package is being developed.\n", "***Anything*** could change." ] }, { "cell_type": "markdown", "id": "8583c12b-a219-4ab1-a40e-67b9ac0d985c", "metadata": {}, "source": [ "Load the **hklpy2** package and show basic information about it." ] }, { "cell_type": "code", "execution_count": 1, "id": "61d5927e-d2d3-4fae-9b47-eb5fdb6117c1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2024-07-29 09:58:21.160093\n", "hklpy2.__version__='0.0.25.dev40+g72b5421.d20240729'\n", "hklpy2.solvers()={'hkl_soleil': 'hklpy2.backends.hkl_soleil:HklSolver', 'no_op': 'hklpy2.backends.no_op:NoOpSolver', 'th_tth': 'hklpy2.backends.th_tth_q:ThTthSolver'}\n" ] } ], "source": [ "import datetime\n", "import hklpy2\n", "import math\n", "from pprint import pprint\n", "\n", "print(f\"{datetime.datetime.now()}\")\n", "print(f\"{hklpy2.__version__=}\")\n", "print(f\"{hklpy2.solvers()=}\")" ] }, { "cell_type": "markdown", "id": "9a32e292-376f-4506-ae33-69e74a6f3468", "metadata": {}, "source": [ "Create the simulated [E4CV](https://prjemian.github.io/hklpy2/api/geom.html#hklpy2.geom.SimulatedE4CV)\n", "(4-circle) diffractometer from the `\"hkl_soleil\"` [solver](https://prjemian.github.io/hklpy2/api/solvers.html).\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "84d9c8f5-46c9-4fd0-8d5a-9fb048b99474", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sim4c.solver.get()='hkl_soleil'\n", "sim4c.geometry.get()='E4CV'\n", "sim4c.sample=Sample(name='sample', lattice=Lattice(a=1, system='cubic'))\n", "{'chi': 'chi',\n", " 'h': 'h',\n", " 'k': 'k',\n", " 'l': 'l',\n", " 'omega': 'omega',\n", " 'phi': 'phi',\n", " 'tth': 'tth'}\n", "sim4c.position=SimulatedE4CVPseudoPos(h=0, k=0, l=0)\n" ] } ], "source": [ "from hklpy2 import SimulatedE4CV\n", "\n", "sim4c = SimulatedE4CV(name=\"sim4c\")\n", "print(f\"{sim4c.solver.get()=}\")\n", "print(f\"{sim4c.geometry.get()=}\")\n", "print(f\"{sim4c.sample=}\")\n", "pprint(sim4c.operator.axes_xref)\n", "print(f\"{sim4c.position=}\")" ] }, { "cell_type": "markdown", "id": "4bc75398-701e-4069-9acc-b58d2578d2a0", "metadata": {}, "source": [ "Create a $\\theta-2\\theta$ 2-circle \n", "[diffractometer](https://prjemian.github.io/hklpy2/api/geom.html#hklpy2.geom.SimulatedTheta2Theta)\n", "using `\"th_tth\"`, a different backend solver. This demonstrates the ability to choose between\n", "different backend solvers.\n", "\n", "The `\"th_tth\"` [solver](https://prjemian.github.io/hklpy2/api/backends/th_tth_q.html)\n", "was written in Python to demonstrate this new capability as a design goal for **hklpy2**." ] }, { "cell_type": "code", "execution_count": 3, "id": "c7d3119b-c0f3-41fb-8456-cf740df94657", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "powder.solver.get()='th_tth'\n", "powder.geometry.get()='TH TTH Q'\n", "powder.sample=Sample(name='sample', lattice=Lattice(a=1, system='cubic'))\n", "powder.operator.axes_xref={'q': 'q', 'theta': 'th', 'ttheta': 'tth'}\n", "powder.position=SimulatedTheta2ThetaPseudoPos(q=0)\n" ] } ], "source": [ "from hklpy2 import SimulatedTheta2Theta\n", "\n", "powder = SimulatedTheta2Theta(name=\"powder\")\n", "print(f\"{powder.solver.get()=}\")\n", "print(f\"{powder.geometry.get()=}\")\n", "print(f\"{powder.sample=}\")\n", "print(f\"{powder.operator.axes_xref=}\")\n", "print(f\"{powder.position=}\")" ] }, { "cell_type": "markdown", "id": "02f5c0aa-c9fb-4a7d-9b9d-3803ff12ac78", "metadata": {}, "source": [ "## SimulatedE4CV" ] }, { "cell_type": "code", "execution_count": 4, "id": "1edc7da0-8717-4d84-af20-b5440800235b", "metadata": {}, "outputs": [], "source": [ "fourc = hklpy2.SimulatedE4CV(name=\"fourc\")" ] }, { "cell_type": "markdown", "id": "20533445-7c31-4b32-b46e-fb7d19bcf4cc", "metadata": {}, "source": [ "Add a sample, as in **hklpy**." ] }, { "cell_type": "code", "execution_count": 5, "id": "32eba399-8101-4f1a-8f2a-f81d9a3945b1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fourc.samples={'sample': Sample(name='sample', lattice=Lattice(a=1, system='cubic'))}\n", "fourc.sample=Sample(name='sample', lattice=Lattice(a=1, system='cubic'))\n", "fourc.sample=Sample(name='vibranium', lattice=Lattice(a=6.283, system='cubic'))\n", "fourc.samples={'sample': Sample(name='sample', lattice=Lattice(a=1, system='cubic')), 'vibranium': Sample(name='vibranium', lattice=Lattice(a=6.283, system='cubic'))}\n", "fourc.sample=Sample(name='sample', lattice=Lattice(a=1, system='cubic'))\n" ] } ], "source": [ "print(f\"{fourc.samples=}\")\n", "print(f\"{fourc.sample=}\")\n", "fourc.operator.remove_sample(\"vibranium\",) # just in case it was defined previously\n", "fourc.add_sample(\"vibranium\", 2*math.pi, digits=3, replace=True) # or force a replacement\n", "print(f\"{fourc.sample=}\")\n", "print(f\"{fourc.samples=}\")\n", "fourc.sample = \"sample\" # switch back to the default sample\n", "print(f\"{fourc.sample=}\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "b0b0e47a-49f3-4416-95f8-6b2435ae20e3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'position': SimulatedE4CVPseudoPos(h=0, k=0, l=0)}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fourc.report" ] }, { "cell_type": "code", "execution_count": 7, "id": "b1f545e2-ec28-4468-a6ac-43a807a586ad", "metadata": {}, "outputs": [], "source": [ "fourc.sample = \"vibranium\"\n", "fourc.sample.reflections.order = []" ] }, { "cell_type": "markdown", "id": "a421b2a2-76fa-4202-8bbe-cbcfe6c3d357", "metadata": {}, "source": [ "Add a couple reflections (with the eventual goal of calculating the $UB$ matrix)." ] }, { "cell_type": "code", "execution_count": 8, "id": "d93281fe-ec11-4e6d-aad4-fc62987a6de5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Reflection(name='r1', geometry='E4CV', pseudos={'h': 1, 'k': 0, 'l': 0}, reals={'omega': 10, 'chi': 0, 'phi': 0, 'tth': 20}, wavelength=1.0)\n", "Reflection(name='r2', geometry='E4CV', pseudos={'h': 0, 'k': 1, 'l': 0}, reals={'omega': 10, 'chi': -90, 'phi': 0, 'tth': 20}, wavelength=1.0)\n", "fourc.operator.solver.U=[[-0.0, -0.0, 1.0], [0.0, -1.0, -0.0], [1.0, -0.0, 0.0]]\n", "fourc.operator.solver.UB=[[-0.0, -0.0, 1.0], [0.0, -1.0, 0.0], [1.0, -0.0, -0.0]]\n", "fourc.inverse(10, 0, 0, 20)=SimulatedE4CVPseudoPos(h=2.182127357071, k=0, l=0)\n" ] } ], "source": [ "fourc.add_reflection((1, 0, 0), (10, 0, 0, 20), name=\"r1\")\n", "fourc.add_reflection((0, 1, 0), (10, -90, 0, 20), name=\"r2\")\n", "for r in fourc.sample.reflections.order:\n", " print(f\"{fourc.sample.reflections[r]}\")\n", "fourc.operator.calcUB(*fourc.sample.reflections.order)\n", "print(f\"{fourc.operator.solver.U=!r}\")\n", "print(f\"{fourc.operator.solver.UB=!r}\")\n", "print(f\"{fourc.inverse(10, 0, 0, 20)=!r}\")" ] }, { "cell_type": "markdown", "id": "0c30b949-04c7-4895-afd5-6cd1be9f0084", "metadata": {}, "source": [ "Swap the first two reflections." ] }, { "cell_type": "code", "execution_count": 9, "id": "eb3391b0-1560-486d-b1d9-005e766063dd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fourc.sample.reflections.order=['r2', 'r1']\n", "Reflection(name='r2', geometry='E4CV', pseudos={'h': 0, 'k': 1, 'l': 0}, reals={'omega': 10, 'chi': -90, 'phi': 0, 'tth': 20}, wavelength=1.0)\n", "Reflection(name='r1', geometry='E4CV', pseudos={'h': 1, 'k': 0, 'l': 0}, reals={'omega': 10, 'chi': 0, 'phi': 0, 'tth': 20}, wavelength=1.0)\n", "fourc.operator.solver.U=[[-0.0, -0.0, 1.0], [0.0, -1.0, -0.0], [1.0, -0.0, 0.0]]\n", "fourc.operator.solver.UB=[[-0.0, -0.0, 1.0], [0.0, -1.0, 0.0], [1.0, -0.0, -0.0]]\n", "fourc.forward(1, 0, 0)=SimulatedE4CVRealPos(omega=4.564279207751, chi=0, phi=-2.0227e-08, tth=9.128558415503)\n", "fourc.inverse(10, 0, 0, 20)=SimulatedE4CVPseudoPos(h=2.182127357071, k=0, l=0)\n" ] } ], "source": [ "fourc.sample.reflections.swap()\n", "print(f\"{fourc.sample.reflections.order=}\")\n", "for r in fourc.sample.reflections.order:\n", " print(f\"{fourc.sample.reflections[r]}\")\n", "print(f\"{fourc.operator.solver.U=!r}\")\n", "print(f\"{fourc.operator.solver.UB=!r}\")\n", "print(f\"{fourc.forward(1, 0, 0)=!r}\")\n", "print(f\"{fourc.inverse(10, 0, 0, 20)=!r}\")" ] }, { "cell_type": "markdown", "id": "89ae8342-d7e0-465e-885e-3e56082f8575", "metadata": {}, "source": [ "## 4-circle with extra axes\n", "\n", "Construct a 4-circle diffractometer with many additional axes, both in real (rotation angle) space and in reciprocal (pseudo) space." ] }, { "cell_type": "code", "execution_count": 10, "id": "b6f15454-7d0d-418a-bef4-89be51a26cf1", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fourc=Fourc(prefix='', name='fourc', settle_time=0.0, timeout=None, egu='', limits=(0, 0), source='computed', read_attrs=['h', 'h.readback', 'h.setpoint', 'k', 'k.readback', 'k.setpoint', 'l', 'l.readback', 'l.setpoint', 'theta', 'chi', 'phi', 'ttheta', 'h2', 'h2.readback', 'h2.setpoint', 'k2', 'k2.readback', 'k2.setpoint', 'l2', 'l2.readback', 'l2.setpoint', 'psi', 'energy'], configuration_attrs=['geometry', 'solver', 'wavelength', 'h', 'k', 'l', 'h2', 'k2', 'l2'], concurrent=True)\n" ] } ], "source": [ "from ophyd import Component as Cpt\n", "from ophyd import Kind\n", "from ophyd import PseudoSingle\n", "from ophyd import SoftPositioner\n", "\n", "NORMAL_HINTED = Kind.hinted | Kind.normal\n", "\n", "class Fourc(hklpy2.DiffractometerBase):\n", " \"\"\"Test case.\"\"\"\n", "\n", " # pseudo-space axes, in order expected by hkl_soleil E4CV, engine=\"hkl\"\n", " h = Cpt(PseudoSingle, \"\", kind=NORMAL_HINTED) # noqa: E741\n", " k = Cpt(PseudoSingle, \"\", kind=NORMAL_HINTED) # noqa: E741\n", " l = Cpt(PseudoSingle, \"\", kind=NORMAL_HINTED) # noqa: E741\n", "\n", " # real-space axes, in order expected by hkl_soleil E4CV\n", " # using different names\n", " theta = Cpt(SoftPositioner, limits=(-180, 180), init_pos=0, kind=NORMAL_HINTED)\n", " chi = Cpt(SoftPositioner, limits=(-180, 180), init_pos=0, kind=NORMAL_HINTED)\n", " phi = Cpt(SoftPositioner, limits=(-180, 180), init_pos=0, kind=NORMAL_HINTED)\n", " ttheta = Cpt(SoftPositioner, limits=(-170, 170), init_pos=0, kind=NORMAL_HINTED)\n", "\n", " # pseudo-space extra axes used in a couple modes\n", " h2 = Cpt(PseudoSingle, \"\", kind=NORMAL_HINTED) # noqa: E741\n", " k2 = Cpt(PseudoSingle, \"\", kind=NORMAL_HINTED) # noqa: E741\n", " l2 = Cpt(PseudoSingle, \"\", kind=NORMAL_HINTED) # noqa: E741\n", "\n", " # real-space extra axis used in a couple modes\n", " psi = Cpt(SoftPositioner, limits=(-170, 170), init_pos=0, kind=NORMAL_HINTED)\n", "\n", " # another Component, not used (yet)\n", " energy = Cpt(SoftPositioner, limits=(5, 35), init_pos=12.4, kind=NORMAL_HINTED)\n", "\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, solver=\"hkl_soleil\", geometry=\"E4CV\", solver_kwargs=dict(engine=\"hkl\"), **kwargs)\n", " self.operator.auto_assign_axes()\n", "\n", "fourc = Fourc(name=\"fourc\")\n", "print(f\"{fourc=}\")" ] }, { "cell_type": "markdown", "id": "53bbbf38-c24e-4898-adf9-75f33db39894", "metadata": {}, "source": [ "Next steps demonstrate some additional design goals:\n", "\n", "- Easy to add additional axes, such as $\\psi$, $h_2$, $k_2$, & $l_2$.\n", " - Even axes, such as *energy*, that are not used directly but may be interesting to include.\n", "- Support for axes used as extra parameters in various diffractometer modes.\n", "- User can specify which axes are to be used by the solver.\n", "- Automatic selection of pseudo and real axes (based on order of appearance).\n", "- User can choose any names for their axes.\n", "- Solver class provides some introspection:\n", " - name and version\n", " - geometries supported\n", " - axes and parameters used by a geometry and mode" ] }, { "cell_type": "code", "execution_count": 11, "id": "42e286ed-f07a-4815-9708-d8ff40978a4e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fourc.operator.solver.geometries()=['APS POLAR', 'E4CH', 'E4CV', 'E6C', 'ESRF ID01 PSIC', 'K4CV', 'K6C', 'PETRA3 P09 EH2', 'PETRA3 P23 4C', 'PETRA3 P23 6C', 'SOLEIL MARS', 'SOLEIL NANOSCOPIUM ROBOT', 'SOLEIL SIRIUS KAPPA', 'SOLEIL SIRIUS TURRET', 'SOLEIL SIXS MED1+2', 'SOLEIL SIXS MED2+2', 'SOLEIL SIXS MED2+3', 'SOLEIL SIXS MED2+3 v2', 'TwoC', 'ZAXIS']\n" ] } ], "source": [ "print(f\"{fourc.operator.solver.geometries()=}\")" ] }, { "cell_type": "code", "execution_count": 12, "id": "c3d78dda-a773-4ed1-a388-212b54f4ea85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fourc.solver.get()='hkl_soleil'\n", "fourc.geometry.get()='E4CV'\n", "fourc.wavelength.get()=1.0\n" ] } ], "source": [ "print(f\"{fourc.solver.get()=}\")\n", "print(f\"{fourc.geometry.get()=}\")\n", "print(f\"{fourc.wavelength.get()=}\")" ] }, { "cell_type": "code", "execution_count": 13, "id": "8ceb97f6-6235-41b1-8b5c-474469f1a428", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "fourc.solver_name='hkl_soleil'\n", "fourc.operator.solver=HklSolver(name='hkl_soleil', version='5.0.0.3511', geometry='E4CV', engine_name='hkl', mode='bissector')\n", "fourc.operator.axes_xref={'h': 'h', 'k': 'k', 'l': 'l', 'theta': 'omega', 'chi': 'chi', 'phi': 'phi', 'ttheta': 'tth'}\n", "fourc.pseudo_axis_names=['h', 'k', 'l', 'h2', 'k2', 'l2']\n", "fourc.real_axis_names=['theta', 'chi', 'phi', 'ttheta', 'psi', 'energy']\n", "fourc.operator.solver.pseudo_axis_names=['h', 'k', 'l']\n", "fourc.operator.solver.real_axis_names=['omega', 'chi', 'phi', 'tth']\n", "fourc.operator.solver.extra_axis_names=[]\n" ] } ], "source": [ "print(f\"{fourc.solver_name=}\")\n", "print(f\"{fourc.operator.solver=}\")\n", "print(f\"{fourc.operator.axes_xref=!r}\") # our names to solver's names\n", "print(f\"{fourc.pseudo_axis_names=}\") # our full ordered lists of names\n", "print(f\"{fourc.real_axis_names=}\")\n", "print(f\"{fourc.operator.solver.pseudo_axis_names=}\") # solver's ordered lists of names\n", "print(f\"{fourc.operator.solver.real_axis_names=}\")\n", "print(f\"{fourc.operator.solver.extra_axis_names=}\")" ] }, { "cell_type": "markdown", "id": "f2f254d2", "metadata": {}, "source": [ "Where is the diffractometer now?" ] }, { "cell_type": "code", "execution_count": 15, "id": "b255aa63", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "wavelength=1.0\n", "h=0 k=0 l=0 h2=0 k2=0 l2=0\n", "theta=0 chi=0 phi=0 ttheta=0 psi=0 energy=12.4\n" ] } ], "source": [ "fourc.wh()" ] }, { "cell_type": "markdown", "id": "dfb28fbc-6b9d-48ea-88ab-4d91e2e65ad0", "metadata": {}, "source": [ "Show ophyd's description of the diffractometer object." ] }, { "cell_type": "code", "execution_count": 14, "id": "c7be3657-7b17-42ba-9746-0bdfbc7e0bb4", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "data keys (* hints)\n", "-------------------\n", "*fourc_chi\n", "*fourc_energy\n", "*fourc_h\n", "*fourc_h2\n", " fourc_h2_setpoint\n", " fourc_h_setpoint\n", "*fourc_k\n", "*fourc_k2\n", " fourc_k2_setpoint\n", " fourc_k_setpoint\n", "*fourc_l\n", "*fourc_l2\n", " fourc_l2_setpoint\n", " fourc_l_setpoint\n", "*fourc_phi\n", "*fourc_psi\n", "*fourc_theta\n", "*fourc_ttheta\n", "\n", "read attrs\n", "----------\n", "h PseudoSingle ('fourc_h')\n", "h.readback AttributeSignal ('fourc_h')\n", "h.setpoint AttributeSignal ('fourc_h_setpoint')\n", "k PseudoSingle ('fourc_k')\n", "k.readback AttributeSignal ('fourc_k')\n", "k.setpoint AttributeSignal ('fourc_k_setpoint')\n", "l PseudoSingle ('fourc_l')\n", "l.readback AttributeSignal ('fourc_l')\n", "l.setpoint AttributeSignal ('fourc_l_setpoint')\n", "theta SoftPositioner ('fourc_theta')\n", "chi SoftPositioner ('fourc_chi')\n", "phi SoftPositioner ('fourc_phi')\n", "ttheta SoftPositioner ('fourc_ttheta')\n", "h2 PseudoSingle ('fourc_h2')\n", "h2.readback AttributeSignal ('fourc_h2')\n", "h2.setpoint AttributeSignal ('fourc_h2_setpoint')\n", "k2 PseudoSingle ('fourc_k2')\n", "k2.readback AttributeSignal ('fourc_k2')\n", "k2.setpoint AttributeSignal ('fourc_k2_setpoint')\n", "l2 PseudoSingle ('fourc_l2')\n", "l2.readback AttributeSignal ('fourc_l2')\n", "l2.setpoint AttributeSignal ('fourc_l2_setpoint')\n", "psi SoftPositioner ('fourc_psi')\n", "energy SoftPositioner ('fourc_energy')\n", "\n", "config keys\n", "-----------\n", "fourc_geometry\n", "fourc_solver\n", "fourc_wavelength\n", "\n", "configuration attrs\n", "-------------------\n", "geometry AttributeSignal ('fourc_geometry')\n", "solver AttributeSignal ('fourc_solver')\n", "wavelength AttributeSignal ('fourc_wavelength')\n", "h PseudoSingle ('fourc_h')\n", "k PseudoSingle ('fourc_k')\n", "l PseudoSingle ('fourc_l')\n", "h2 PseudoSingle ('fourc_h2')\n", "k2 PseudoSingle ('fourc_k2')\n", "l2 PseudoSingle ('fourc_l2')\n", "\n", "unused attrs\n", "------------\n", "\n" ] } ], "source": [ "fourc.summary()" ] } ], "metadata": { "kernelspec": { "display_name": "bluesky_2024_2", "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.11.0" } }, "nbformat": 4, "nbformat_minor": 5 }