From 17fad79df8af3f6ae67497bbc000bec9bf57c127 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Thu, 9 Nov 2023 16:40:40 -0600 Subject: [PATCH 1/7] MNT #288 add preview method --- hkl/configuration.py | 91 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/hkl/configuration.py b/hkl/configuration.py index 5e4d3e2f..42044671 100644 --- a/hkl/configuration.py +++ b/hkl/configuration.py @@ -40,6 +40,7 @@ from dataclasses import field import numpy +import pyRestTable import yaml from apischema import deserialize from apischema import serialize @@ -53,6 +54,8 @@ DEFAULT_WAVELENGTH = 1.54 # angstrom EXPORT_FORMATS = "dict json yaml".split() +SIGNIFICANT_DIGITS = 7 + # standard value checks, raise exception(s) when appropriate def _check_key(key, biblio, intro): @@ -478,6 +481,7 @@ class DiffractometerConfiguration: .. autosummary:: ~export + ~preview ~restore ~model ~reset_diffractometer @@ -528,6 +532,93 @@ def export(self, fmt="json"): f.write(data) return data + def preview(self, data, show_constraints=False, show_reflections=False): + """ + List the samples in the configuration. + + PARAMETERS + + data *dict* or *str* *pathlib.Path* object: + Structure (dict, json, or yaml) with diffractometer configuration + or pathlib object referring to a file with one of these formats. + show_constraints *bool*: + If ``True`` (default: ``False``), will also show any constraints + in a separate table. + show_reflections *bool*: + If ``True`` (default: ``False``), will also show reflections, if + any, in a separate table for each sample. + """ + if isinstance(data, pathlib.Path): + if not data.exists(): + raise FileNotFoundError(f"{data}") + with open(data) as f: + data = f.read() + + if isinstance(data, str): + if data.strip().startswith("{"): + data = json.loads(data) + else: + data = yaml.load(data, Loader=yaml.Loader) + + return self._preview(data, show_constraints, show_reflections) + + def _preview(self, data, show_constraints=False, show_reflections=False): + if not isinstance(data, dict): + raise TypeError(f"Cannot interpret configuration data: {type(data)}") + + text = ( + f"name: {data.get('name', '-n/a-')}" + f"\ndate: {data.get('datetime', '-n/a-')}" + f"\ngeometry: {data['geometry']}" + ) + + title = "Table of Samples" + table = pyRestTable.Table() + table.labels = "# sample a b c alpha beta gamma #refl".split() + for i, sname in enumerate(data["samples"], start=1): + sample = data["samples"][sname] + row = [i, sname] + for v in sample["lattice"].values(): + row.append(f"{round(v, SIGNIFICANT_DIGITS)}") + row.append(len(sample["reflections"])) + table.addRow(row) + text += f"\n\n{title}\n{table}" + + if show_reflections: + for sname, sample in data["samples"].items(): + if len(sample["reflections"]) == 0: + continue # nothing to report + title = f"Table of Reflections for Sample: {sname}" + table = pyRestTable.Table() + refl = sample["reflections"][0] + table.addLabel("#") + table.labels += list(refl["reflection"]) + table.labels += list(refl["position"]) + table.addLabel("wavelength") + table.addLabel("orient?") + for i, refl in enumerate(sample["reflections"], start=1): + row = [i] + row += [f"{round(v, SIGNIFICANT_DIGITS)}" for v in refl["reflection"].values()] + row += [f"{round(v, SIGNIFICANT_DIGITS)}" for v in refl["position"].values()] + row.append(str(refl["wavelength"])) + row.append(str(refl["orientation_reflection"])) + table.addRow(row) + text += f"\n\n{title}\n{table}" + + if show_constraints and len(data["constraints"]) > 0: + title = "Table of Axis Constraints" + table = pyRestTable.Table() + table.labels = "axis low_limit high_limit value fit?".split() + for aname, constraint in data["constraints"].items(): + row = [aname] + for k in "low_limit high_limit value".split(): + row.append(f"{round(constraint[k], SIGNIFICANT_DIGITS)}") + row.append(f"{constraint['fit']}") + table.addRow(row) + text += f"\n\n{title}\n{table}" + + return text + def restore(self, data, clear=True, restore_constraints=True): """ Restore configuration from a recognized format (dict, json, yaml). From 61883acb782abea2e43fd77b4bcdfd9c3f9917c4 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Thu, 16 Nov 2023 12:52:39 -0600 Subject: [PATCH 2/7] DOC #288 demonstrate export, preview, and restore --- .../notebooks/how_configuration.ipynb | 1062 +++++++++++++++++ 1 file changed, 1062 insertions(+) create mode 100644 docs/source/examples/notebooks/how_configuration.ipynb diff --git a/docs/source/examples/notebooks/how_configuration.ipynb b/docs/source/examples/notebooks/how_configuration.ipynb new file mode 100644 index 00000000..dded152a --- /dev/null +++ b/docs/source/examples/notebooks/how_configuration.ipynb @@ -0,0 +1,1062 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sample orientation: export & restore diffractometer configuration\n", + "\n", + "A common user request is to preserve the orientation (**UB**) matrix and\n", + "related terms used to compute it. Terms such as the list of orientation\n", + "reflections, samples, and diffractometer geometry from their current session.\n", + "The configuration should be saved somewhere so it is available, even after\n", + "bluesky stops. Multiple sessions could (optionally) access the same\n", + "configuration or use a different one.\n", + "\n", + "It is a local choice how and where to save this configuration. The first step\n", + "is to define a common structure to be exported and restored by *hklpy*. The\n", + "structure would be stored in any of a variety of possibilities. A session could\n", + "restore the configuration automatically from the chosen storage. Later, the\n", + "session would export the configuration both automatically, and to a different\n", + "location at the choice of the user.\n", + "\n", + "- text file: stored in:\n", + " - the local file directory\n", + " - some other directory\n", + " - network storage\n", + "- NeXus HDF5 file: stored in the `pwd` or elsewhere\n", + "- EPICS [Channel\n", + " Access](https://epics-controls.org/resources-and-support/documents/ca/) string\n", + " [*waveform*](https://epics.anl.gov/base/R7-0/3-docs/waveformRecord.html) record\n", + "- EPICS [pvAccess](https://epics-controls.org/resources-and-support/documents/pvaccess/)\n", + "structure\n", + "- in-memory cache, such as:\n", + " - [redis](https://redis.io)\n", + "- database, such as:\n", + " - [MySQL](https://mysql.com)\n", + " - [SQLite](https://sqlite.org)\n", + "- Python object (such as used in this documentation)\n", + "\n", + "**Objective**\n", + "\n", + "Show how to export diffractometer configuration to a file and later restore it.\n", + "Demonstrate with a 4-circle diffractometer (E4CV geometry)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup a (simulated) 4-circle diffractometer\n", + "\n", + "Load the support for a 4-circle diffractometer using the *E4CV* geometry. Since\n", + "this is a simulation, we do not need an EPICS system with motors. The\n", + "[SimulatedE4CV](https://blueskyproject.io/hklpy/ready_to_use.html#simulatede4cv)\n", + "structure (a Python class) provides all that is needed." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from hkl import SimulatedE4CV\n", + "\n", + "sim4c = SimulatedE4CV(\"\", name=\"sim4c\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print all the details about this `sim4c` diffractometer we just created. Since this is a default configuration, no orientation reflections have been reported. The **UB** matrix is the default $(2\\pi/1.54)$**U** where **U** is the identity matrix and 1.54 is the unit cell length. The geometry defines the names of the rotational axes `omega`, `chi`, `phi`, and `tth`. This is the canonical order. The *constraints* are applied when computing values for the rotational axes from reciprocal space coordinates." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===================== ====================================================================\n", + "term value \n", + "===================== ====================================================================\n", + "diffractometer sim4c \n", + "geometry E4CV \n", + "class SimulatedE4CV \n", + "energy (keV) 8.05092 \n", + "wavelength (angstrom) 1.54000 \n", + "calc engine hkl \n", + "mode bissector \n", + "positions ===== ======= \n", + " name value \n", + " ===== ======= \n", + " omega 0.00000 \n", + " chi 0.00000 \n", + " phi 0.00000 \n", + " tth 0.00000 \n", + " ===== ======= \n", + "constraints ===== ========= ========== ===== ==== \n", + " axis low_limit high_limit value fit \n", + " ===== ========= ========== ===== ==== \n", + " omega -180.0 180.0 0.0 True \n", + " chi -180.0 180.0 0.0 True \n", + " phi -180.0 180.0 0.0 True \n", + " tth -180.0 180.0 0.0 True \n", + " ===== ========= ========== ===== ==== \n", + "sample: main ================ ===================================================\n", + " term value \n", + " ================ ===================================================\n", + " unit cell edges a=1.54, b=1.54, c=1.54 \n", + " unit cell angles alpha=90.0, beta=90.0, gamma=90.0 \n", + " [U] [[1. 0. 0.] \n", + " [0. 1. 0.] \n", + " [0. 0. 1.]] \n", + " [UB] [[ 4.07999046e+00 -2.49827363e-16 -2.49827363e-16] \n", + " [ 0.00000000e+00 4.07999046e+00 -2.49827363e-16] \n", + " [ 0.00000000e+00 0.00000000e+00 4.07999046e+00]]\n", + " ================ ===================================================\n", + "===================== ====================================================================\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sim4c.pa()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What is described in a diffractometer configuration?\n", + "\n", + "The `hkl.DiffractometerConfiguration` structure is used for export and restore.\n", + "To show what an export contains (for this default diffractometer), we make a\n", + "Python object specifically for the `sim4c` diffractometer. In this example, the\n", + "object is called `agent` (you could pick some other name, such as `config`).\n", + "\n", + "Create `agent` specifically for the `sim4c` diffractometer to provide the\n", + "methods in the next table.\n", + "\n", + "Python | description\n", + "--- | ---\n", + "`agent.export()` | describe a diffractometer’s configuration\n", + "`agent.preview()` | preview a saved configuration\n", + "`agent.restore()` | set a diffractometer’s configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from hkl import DiffractometerConfiguration\n", + "\n", + "agent = DiffractometerConfiguration(sim4c)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Export\n", + "\n", + "`export()` returns the current configuration of the diffractometer in any of several formats:\n", + "\n", + "- JSON string (default)\n", + "- YAML string\n", + "- Python dictionary\n", + "- file (as defined by a Python *pathlib* object)\n", + "\n", + "Note: Internally, the code uses the Python dictionary for the actual export and\n", + "restore operations. The other formats derive from the Python dictionary. All\n", + "information in the Python dictionary is first validated before it is restored to\n", + "the diffractometer." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Export: JSON string (default) format\n", + "\n", + "Describe `sim4c` using `agent.export()`. The information content is similar to\n", + "`sim4c.pa()`, but in a format convenient for both saving (to a file or other\n", + "structure) and for restoring.\n", + "\n", + "The default format is a [JSON](https://json.org) string, formatted with line\n", + "endings and indentation. (Alternatives include Python dictionary,\n", + "[YAML](https://yaml.org), and file.)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"geometry\": \"E4CV\",\n", + " \"engine\": \"hkl\",\n", + " \"library\": \"gi.repository.Hkl\",\n", + " \"mode\": \"bissector\",\n", + " \"canonical_axes\": [\n", + " \"omega\",\n", + " \"chi\",\n", + " \"phi\",\n", + " \"tth\"\n", + " ],\n", + " \"real_axes\": [\n", + " \"omega\",\n", + " \"chi\",\n", + " \"phi\",\n", + " \"tth\"\n", + " ],\n", + " \"reciprocal_axes\": [\n", + " \"h\",\n", + " \"k\",\n", + " \"l\"\n", + " ],\n", + " \"constraints\": {\n", + " \"omega\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " },\n", + " \"chi\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " },\n", + " \"phi\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " },\n", + " \"tth\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " }\n", + " },\n", + " \"samples\": {\n", + " \"main\": {\n", + " \"name\": \"main\",\n", + " \"lattice\": {\n", + " \"a\": 1.54,\n", + " \"b\": 1.54,\n", + " \"c\": 1.54,\n", + " \"alpha\": 90.0,\n", + " \"beta\": 90.0,\n", + " \"gamma\": 90.0\n", + " },\n", + " \"reflections\": [],\n", + " \"UB\": [\n", + " [\n", + " 4.079990459207523,\n", + " -2.4982736282101165e-16,\n", + " -2.4982736282101165e-16\n", + " ],\n", + " [\n", + " 0.0,\n", + " 4.079990459207523,\n", + " -2.4982736282101165e-16\n", + " ],\n", + " [\n", + " 0.0,\n", + " 0.0,\n", + " 4.079990459207523\n", + " ]\n", + " ],\n", + " \"U\": [\n", + " [\n", + " 1.0,\n", + " 0.0,\n", + " 0.0\n", + " ],\n", + " [\n", + " 0.0,\n", + " 1.0,\n", + " 0.0\n", + " ],\n", + " [\n", + " 0.0,\n", + " 0.0,\n", + " 1.0\n", + " ]\n", + " ]\n", + " }\n", + " },\n", + " \"name\": \"sim4c\",\n", + " \"datetime\": \"2023-11-16 12:48:44.500249\",\n", + " \"wavelength_angstrom\": 1.54,\n", + " \"energy_keV\": 8.050921974025975,\n", + " \"hklpy_version\": \"1.0.5.dev207+g955ab4d\",\n", + " \"library_version\": \"v5.0.0.3001\",\n", + " \"python_class\": \"SimulatedE4CV\",\n", + " \"other\": {}\n", + "}\n" + ] + } + ], + "source": [ + "print(agent.export())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Export: YAML string format\n", + "\n", + "View the same content in YAML format:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "geometry: E4CV\n", + "engine: hkl\n", + "library: gi.repository.Hkl\n", + "mode: bissector\n", + "canonical_axes:\n", + "- omega\n", + "- chi\n", + "- phi\n", + "- tth\n", + "real_axes:\n", + "- omega\n", + "- chi\n", + "- phi\n", + "- tth\n", + "reciprocal_axes:\n", + "- h\n", + "- k\n", + "- l\n", + "constraints:\n", + " omega:\n", + " low_limit: -180.0\n", + " high_limit: 180.0\n", + " value: 0.0\n", + " fit: true\n", + " chi:\n", + " low_limit: -180.0\n", + " high_limit: 180.0\n", + " value: 0.0\n", + " fit: true\n", + " phi:\n", + " low_limit: -180.0\n", + " high_limit: 180.0\n", + " value: 0.0\n", + " fit: true\n", + " tth:\n", + " low_limit: -180.0\n", + " high_limit: 180.0\n", + " value: 0.0\n", + " fit: true\n", + "samples:\n", + " main:\n", + " name: main\n", + " lattice:\n", + " a: 1.54\n", + " b: 1.54\n", + " c: 1.54\n", + " alpha: 90.0\n", + " beta: 90.0\n", + " gamma: 90.0\n", + " reflections: []\n", + " UB:\n", + " - - 4.079990459207523\n", + " - -2.4982736282101165e-16\n", + " - -2.4982736282101165e-16\n", + " - - 0.0\n", + " - 4.079990459207523\n", + " - -2.4982736282101165e-16\n", + " - - 0.0\n", + " - 0.0\n", + " - 4.079990459207523\n", + " U:\n", + " - - 1.0\n", + " - 0.0\n", + " - 0.0\n", + " - - 0.0\n", + " - 1.0\n", + " - 0.0\n", + " - - 0.0\n", + " - 0.0\n", + " - 1.0\n", + "name: sim4c\n", + "datetime: '2023-11-16 12:48:44.571162'\n", + "wavelength_angstrom: 1.54\n", + "energy_keV: 8.050921974025975\n", + "hklpy_version: 1.0.5.dev207+g955ab4d\n", + "library_version: v5.0.0.3001\n", + "python_class: SimulatedE4CV\n", + "other: {}\n", + "\n" + ] + } + ], + "source": [ + "print(agent.export(\"yaml\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Export: Python dictionary format\n", + "\n", + "Or as Python dictionary:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'geometry': 'E4CV',\n", + " 'engine': 'hkl',\n", + " 'library': 'gi.repository.Hkl',\n", + " 'mode': 'bissector',\n", + " 'canonical_axes': ['omega', 'chi', 'phi', 'tth'],\n", + " 'real_axes': ['omega', 'chi', 'phi', 'tth'],\n", + " 'reciprocal_axes': ['h', 'k', 'l'],\n", + " 'constraints': {'omega': {'low_limit': -180.0,\n", + " 'high_limit': 180.0,\n", + " 'value': 0.0,\n", + " 'fit': True},\n", + " 'chi': {'low_limit': -180.0, 'high_limit': 180.0, 'value': 0.0, 'fit': True},\n", + " 'phi': {'low_limit': -180.0, 'high_limit': 180.0, 'value': 0.0, 'fit': True},\n", + " 'tth': {'low_limit': -180.0,\n", + " 'high_limit': 180.0,\n", + " 'value': 0.0,\n", + " 'fit': True}},\n", + " 'samples': {'main': {'name': 'main',\n", + " 'lattice': {'a': 1.54,\n", + " 'b': 1.54,\n", + " 'c': 1.54,\n", + " 'alpha': 90.0,\n", + " 'beta': 90.0,\n", + " 'gamma': 90.0},\n", + " 'reflections': [],\n", + " 'UB': [[4.079990459207523,\n", + " -2.4982736282101165e-16,\n", + " -2.4982736282101165e-16],\n", + " [0.0, 4.079990459207523, -2.4982736282101165e-16],\n", + " [0.0, 0.0, 4.079990459207523]],\n", + " 'U': [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]}},\n", + " 'name': 'sim4c',\n", + " 'datetime': '2023-11-16 12:48:44.578586',\n", + " 'wavelength_angstrom': 1.54,\n", + " 'energy_keV': 8.050921974025975,\n", + " 'hklpy_version': '1.0.5.dev207+g955ab4d',\n", + " 'library_version': 'v5.0.0.3001',\n", + " 'python_class': 'SimulatedE4CV',\n", + " 'other': {}}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent.export(\"dict\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Export: Data file (text)\n", + "\n", + "Or as a data file. To save as a data file, we need the name (and directory\n", + "path) of that file. Could be a local file such as `sim4c-config.json` or a file\n", + "in some other directory such as `~/.config/sim4c/autosave.json`. These are just\n", + "ideas.\n", + "\n", + "In any case, we describe the file name and path using Python's\n", + "[pathlib](https://docs.python.org/3/library/pathlib.html) support package.\n", + "\n", + "For this example, we'll use files created just for this notebook in a temporary\n", + "directory using Python's\n", + "[tempfile](https://docs.python.org/3/library/tempfile.html) package. The entire\n", + "directory will be deleted when the notebook exits. (The `iterdir()` method\n", + "prints the directory's contents. It's empty at the start.)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "config_path=PosixPath('/tmp/tmpvrwz9f7z')\n", + "list(config_path.iterdir())=[]\n" + ] + } + ], + "source": [ + "import pathlib\n", + "import tempfile\n", + "\n", + "temp_dir = tempfile.TemporaryDirectory()\n", + "config_path = pathlib.Path(temp_dir.name)\n", + "print(f\"{config_path=}\")\n", + "print(f\"{list(config_path.iterdir())=}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Export the configuration to file `sim4c.json` in the temporary directory. Show the file exists." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "list(config_path.iterdir())=[PosixPath('/tmp/tmpvrwz9f7z/sim4c.json')]\n" + ] + } + ], + "source": [ + "config_file = config_path / \"sim4c.json\"\n", + "agent.export(config_file)\n", + "print(f\"{list(config_path.iterdir())=}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Show the contents of the new configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"geometry\": \"E4CV\",\n", + " \"engine\": \"hkl\",\n", + " \"library\": \"gi.repository.Hkl\",\n", + " \"mode\": \"bissector\",\n", + " \"canonical_axes\": [\n", + " \"omega\",\n", + " \"chi\",\n", + " \"phi\",\n", + " \"tth\"\n", + " ],\n", + " \"real_axes\": [\n", + " \"omega\",\n", + " \"chi\",\n", + " \"phi\",\n", + " \"tth\"\n", + " ],\n", + " \"reciprocal_axes\": [\n", + " \"h\",\n", + " \"k\",\n", + " \"l\"\n", + " ],\n", + " \"constraints\": {\n", + " \"omega\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " },\n", + " \"chi\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " },\n", + " \"phi\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " },\n", + " \"tth\": {\n", + " \"low_limit\": -180.0,\n", + " \"high_limit\": 180.0,\n", + " \"value\": 0.0,\n", + " \"fit\": true\n", + " }\n", + " },\n", + " \"samples\": {\n", + " \"main\": {\n", + " \"name\": \"main\",\n", + " \"lattice\": {\n", + " \"a\": 1.54,\n", + " \"b\": 1.54,\n", + " \"c\": 1.54,\n", + " \"alpha\": 90.0,\n", + " \"beta\": 90.0,\n", + " \"gamma\": 90.0\n", + " },\n", + " \"reflections\": [],\n", + " \"UB\": [\n", + " [\n", + " 4.079990459207523,\n", + " -2.4982736282101165e-16,\n", + " -2.4982736282101165e-16\n", + " ],\n", + " [\n", + " 0.0,\n", + " 4.079990459207523,\n", + " -2.4982736282101165e-16\n", + " ],\n", + " [\n", + " 0.0,\n", + " 0.0,\n", + " 4.079990459207523\n", + " ]\n", + " ],\n", + " \"U\": [\n", + " [\n", + " 1.0,\n", + " 0.0,\n", + " 0.0\n", + " ],\n", + " [\n", + " 0.0,\n", + " 1.0,\n", + " 0.0\n", + " ],\n", + " [\n", + " 0.0,\n", + " 0.0,\n", + " 1.0\n", + " ]\n", + " ]\n", + " }\n", + " },\n", + " \"name\": \"sim4c\",\n", + " \"datetime\": \"2023-11-16 12:48:44.591289\",\n", + " \"wavelength_angstrom\": 1.54,\n", + " \"energy_keV\": 8.050921974025975,\n", + " \"hklpy_version\": \"1.0.5.dev207+g955ab4d\",\n", + " \"library_version\": \"v5.0.0.3001\",\n", + " \"python_class\": \"SimulatedE4CV\",\n", + " \"other\": {}\n", + "}\n" + ] + } + ], + "source": [ + "with open(config_file) as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preview & Restore\n", + "\n", + "To demonstrate `agent.preview()` and `agent.restore()`, let's compare the file\n", + "we just wrote with a file found on the internet. The *hklpy* repository on\n", + "GitHub has a *compatible* (same geometry, canonical motor names, and back-end\n", + "computation library) file used for testing the *hklpy* source code.\n", + "\n", + "We'll use the [requests](https://docs.python-requests.org/) package for the\n", + "download. With *requests*, we can keep the structure in memory. (There is no\n", + "need to save it to a local file.)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "test_e4c_config='{\\n \"geometry\": \"E4CV\",\\n \"engine\": \"hkl\",\\n \"library\": \"gi.repository.Hkl\",\\n \"mode\": \"bissector\",\\n \"canonical_axes\": [\\n \"omega\",\\n \"chi\",\\n \"phi\",\\n \"tth\"\\n ],\\n \"real_axes\": [\\n \"omega\",\\n \"chi\",\\n \"phi\",\\n \"tth\"\\n ],\\n \"reciprocal_axes\": [\\n \"h\",\\n \"k\",\\n \"l\"\\n ],\\n \"constraints\": {\\n \"omega\": {\\n \"low_limit\": -100.0,\\n \"high_limit\": 100.0,\\n \"value\": 0.0,\\n \"fit\": true\\n },\\n \"chi\": {\\n \"low_limit\": -100.0,\\n \"high_limit\": 100.0,\\n \"value\": 0.0,\\n \"fit\": true\\n },\\n \"phi\": {\\n \"low_limit\": -100.0,\\n \"high_limit\": 100.0,\\n \"value\": 0.0,\\n \"fit\": true\\n },\\n \"tth\": {\\n \"low_limit\": -100.0,\\n \"high_limit\": 100.0,\\n \"value\": 0.0,\\n \"fit\": true\\n }\\n },\\n \"samples\": {\\n \"main\": {\\n \"name\": \"main\",\\n \"lattice\": {\\n \"a\": 1.54,\\n \"b\": 1.54,\\n \"c\": 1.54,\\n \"alpha\": 90.0,\\n \"beta\": 90.0,\\n \"gamma\": 90.0\\n },\\n \"reflections\": [],\\n \"UB\": [\\n [\\n 4.079990459207523,\\n -2.4982736282101165e-16,\\n -2.4982736282101165e-16\\n ],\\n [\\n 0.0,\\n 4.079990459207523,\\n -2.4982736282101165e-16\\n ],\\n [\\n 0.0,\\n 0.0,\\n 4.079990459207523\\n ]\\n ],\\n \"U\": [\\n [\\n 1.0,\\n 0.0,\\n 0.0\\n ],\\n [\\n 0.0,\\n 1.0,\\n 0.0\\n ],\\n [\\n 0.0,\\n 0.0,\\n 1.0\\n ]\\n ]\\n },\\n \"vibranium\": {\\n \"name\": \"vibranium\",\\n \"lattice\": {\\n \"a\": 6.283185307179586,\\n \"b\": 6.283185307179586,\\n \"c\": 6.283185307179586,\\n \"alpha\": 90.0,\\n \"beta\": 90.0,\\n \"gamma\": 90.0\\n },\\n \"reflections\": [\\n {\\n \"reflection\": {\\n \"h\": 4.0,\\n \"k\": 0.0,\\n \"l\": 0.0\\n },\\n \"position\": {\\n \"omega\": -145.451,\\n \"chi\": 0.0,\\n \"phi\": 0.0,\\n \"tth\": 69.0966\\n },\\n \"wavelength\": 1.54,\\n \"orientation_reflection\": false,\\n \"flag\": 1\\n },\\n {\\n \"reflection\": {\\n \"h\": 0.0,\\n \"k\": 4.0,\\n \"l\": 0.0\\n },\\n \"position\": {\\n \"omega\": -145.451,\\n \"chi\": 0.0,\\n \"phi\": 90.0,\\n \"tth\": 69.0966\\n },\\n \"wavelength\": 1.54,\\n \"orientation_reflection\": true,\\n \"flag\": 1\\n },\\n {\\n \"reflection\": {\\n \"h\": 0.0,\\n \"k\": 0.0,\\n \"l\": 4.0\\n },\\n \"position\": {\\n \"omega\": -145.451,\\n \"chi\": 90.0,\\n \"phi\": 0.0,\\n \"tth\": 69.0966\\n },\\n \"wavelength\": 1.54,\\n \"orientation_reflection\": true,\\n \"flag\": 1\\n }\\n ],\\n \"UB\": [\\n [\\n 1.2217304763701863e-05,\\n -0.9999999999253688,\\n -1.7623547209572502e-15\\n ],\\n [\\n -1.4926257043558267e-10,\\n -1.4349296274686127e-42,\\n -1.0000000000000002\\n ],\\n [\\n 0.9999999999253688,\\n 1.221730476364063e-05,\\n -1.492626316575311e-10\\n ]\\n ],\\n \"U\": [\\n [\\n 1.2217304763701863e-05,\\n -0.9999999999253688,\\n -1.8235863128158895e-15\\n ],\\n [\\n -1.4926257043558267e-10,\\n -9.139696455822134e-27,\\n -1.0000000000000002\\n ],\\n [\\n 0.9999999999253688,\\n 1.2217304763701863e-05,\\n -1.4926257042444305e-10\\n ]\\n ]\\n }\\n },\\n \"name\": \"e4c\",\\n \"datetime\": \"2023-11-01 16:40\",\\n \"wavelength_angstrom\": 1.54,\\n \"energy_keV\": 8.050921974025975,\\n \"hklpy_version\": \"1.0.5.dev122+g473f61f\",\\n \"library_version\": \"v5.0.0.3001\",\\n \"python_class\": \"SimulatedE4CV\",\\n \"other\": {}\\n}\\n'\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "r = requests.get(\n", + " \"https://raw.githubusercontent.com/bluesky/hklpy/main/hkl/tests/data/e4c-config.json\"\n", + ")\n", + "test_e4c_config = r.text\n", + "print(f\"{test_e4c_config=}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Preview: JSON string\n", + "\n", + "The `agent.preview(structure)` method shows the sample information (name,\n", + "crystal lattice parameters, and number of orientation reflections) from the\n", + "configuration `structure` object, without loading it into the diffractometer.\n", + "\n", + "`agent.preview()` works with these types of `structure`: \n", + "\n", + "- JSON string\n", + "- YAML string\n", + "- Python dictionary\n", + "- file (pathlib object)\n", + "\n", + "Here, `test_e4c_config` is recognized as a JSON string." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "name: e4c\n", + "date: 2023-11-01 16:40\n", + "geometry: E4CV\n", + "\n", + "Table of Samples\n", + "= ========= ========= ========= ========= ===== ==== ===== =====\n", + "# sample a b c alpha beta gamma #refl\n", + "= ========= ========= ========= ========= ===== ==== ===== =====\n", + "1 main 1.54 1.54 1.54 90.0 90.0 90.0 0 \n", + "2 vibranium 6.2831853 6.2831853 6.2831853 90.0 90.0 90.0 3 \n", + "= ========= ========= ========= ========= ===== ==== ===== =====\n", + "\n" + ] + } + ], + "source": [ + "print(type(test_e4c_config))\n", + "print(agent.preview(test_e4c_config))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "See that `test_e4c_config` has an additional sample: `vibranium` (looks cubic\n", + "with a lattice constant $a_o=2\\pi$ which is right since *vibranium* is\n", + "fictional). Three orientation reflections are provided. Next, show the\n", + "additional details." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name: e4c\n", + "date: 2023-11-01 16:40\n", + "geometry: E4CV\n", + "\n", + "Table of Samples\n", + "= ========= ========= ========= ========= ===== ==== ===== =====\n", + "# sample a b c alpha beta gamma #refl\n", + "= ========= ========= ========= ========= ===== ==== ===== =====\n", + "1 main 1.54 1.54 1.54 90.0 90.0 90.0 0 \n", + "2 vibranium 6.2831853 6.2831853 6.2831853 90.0 90.0 90.0 3 \n", + "= ========= ========= ========= ========= ===== ==== ===== =====\n", + "\n", + "\n", + "Table of Reflections for Sample: vibranium\n", + "= === === === ======== ==== ==== ======= ========== =======\n", + "# h k l omega chi phi tth wavelength orient?\n", + "= === === === ======== ==== ==== ======= ========== =======\n", + "1 4.0 0.0 0.0 -145.451 0.0 0.0 69.0966 1.54 False \n", + "2 0.0 4.0 0.0 -145.451 0.0 90.0 69.0966 1.54 True \n", + "3 0.0 0.0 4.0 -145.451 90.0 0.0 69.0966 1.54 True \n", + "= === === === ======== ==== ==== ======= ========== =======\n", + "\n", + "\n", + "Table of Axis Constraints\n", + "===== ========= ========== ===== ====\n", + "axis low_limit high_limit value fit?\n", + "===== ========= ========== ===== ====\n", + "omega -100.0 100.0 0.0 True\n", + "chi -100.0 100.0 0.0 True\n", + "phi -100.0 100.0 0.0 True\n", + "tth -100.0 100.0 0.0 True\n", + "===== ========= ========== ===== ====\n", + "\n" + ] + } + ], + "source": [ + "# options to show constraints and list of reflections\n", + "print(agent.preview(test_e4c_config, show_constraints=True, show_reflections=True))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the high and low limits in the constraints are now $\\pm100$. In the default, they were $\\pm180$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Restore: JSON string\n", + "\n", + "Let's restore the `test_e4c_config` configuration to our `sim4c` diffractometer.\n", + "This is possible because we have identical matches of the diffractometer and the\n", + "configuration:\n", + "\n", + "- name of geometry\n", + "- name of computation engine\n", + "- name of back-end support library\n", + "- names and exact order of reciprocal-space axes\n", + "- names and exact order of canonical real-space axes\n", + "\n", + "(Other validation steps are applied to make sure that the data in the\n", + "configuration is specified correctly.)\n", + "\n", + "Once restore is complete, print all the diffractometer settings, as before using `sim4c.pa()`.\n", + "\n", + "Note: The `agent.restore()` method does not move any axes of the diffractometer.\n", + "It only changes configuration settings in Python memory." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===================== ===============================================================================\n", + "term value \n", + "===================== ===============================================================================\n", + "diffractometer sim4c \n", + "geometry E4CV \n", + "class SimulatedE4CV \n", + "energy (keV) 8.05092 \n", + "wavelength (angstrom) 1.54000 \n", + "calc engine hkl \n", + "mode bissector \n", + "positions ===== ======= \n", + " name value \n", + " ===== ======= \n", + " omega 0.00000 \n", + " chi 0.00000 \n", + " phi 0.00000 \n", + " tth 0.00000 \n", + " ===== ======= \n", + "constraints ===== ========= ========== ===== ==== \n", + " axis low_limit high_limit value fit \n", + " ===== ========= ========== ===== ==== \n", + " omega -100.0 100.0 0.0 True \n", + " chi -100.0 100.0 0.0 True \n", + " phi -100.0 100.0 0.0 True \n", + " tth -100.0 100.0 0.0 True \n", + " ===== ========= ========== ===== ==== \n", + "sample: vibranium ================= =============================================================\n", + " term value \n", + " ================= =============================================================\n", + " unit cell edges a=6.283185307179586, b=6.283185307179586, c=6.283185307179586\n", + " unit cell angles alpha=90.0, beta=90.0, gamma=90.0 \n", + " ref 1 (hkl) h=4.0, k=0.0, l=0.0 \n", + " ref 1 positioners omega=-145.45100, chi=0.00000, phi=0.00000, tth=69.09660 \n", + " ref 2 (hkl) h=0.0, k=4.0, l=0.0 \n", + " ref 2 positioners omega=-145.45100, chi=0.00000, phi=90.00000, tth=69.09660 \n", + " ref 3 (hkl) h=0.0, k=0.0, l=4.0 \n", + " ref 3 positioners omega=-145.45100, chi=90.00000, phi=0.00000, tth=69.09660 \n", + " [U] [[ 1.22173048e-05 -1.00000000e+00 -1.82358631e-15] \n", + " [-1.49262570e-10 -9.13969646e-27 -1.00000000e+00] \n", + " [ 1.00000000e+00 1.22173048e-05 -1.49262570e-10]] \n", + " [UB] [[ 1.22173048e-05 -1.00000000e+00 -1.76235472e-15] \n", + " [-1.49262570e-10 -1.43492963e-42 -1.00000000e+00] \n", + " [ 1.00000000e+00 1.22173048e-05 -1.49262632e-10]] \n", + " ================= =============================================================\n", + "===================== ===============================================================================\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent.restore(test_e4c_config)\n", + "sim4c.pa()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Observe the list of three *vibranium* sample reflections. Two of them, as\n", + "noted, have been used to compute the **UB** matrix." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Restore: file\n", + "\n", + "We can swap back to the default settings by restoring from the temporary\n", + "configuration file we created above. This resets the constraints and removes\n", + "the *vibranium* sample." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===================== ====================================================================\n", + "term value \n", + "===================== ====================================================================\n", + "diffractometer sim4c \n", + "geometry E4CV \n", + "class SimulatedE4CV \n", + "energy (keV) 8.05092 \n", + "wavelength (angstrom) 1.54000 \n", + "calc engine hkl \n", + "mode bissector \n", + "positions ===== ======= \n", + " name value \n", + " ===== ======= \n", + " omega 0.00000 \n", + " chi 0.00000 \n", + " phi 0.00000 \n", + " tth 0.00000 \n", + " ===== ======= \n", + "constraints ===== ========= ========== ===== ==== \n", + " axis low_limit high_limit value fit \n", + " ===== ========= ========== ===== ==== \n", + " omega -180.0 180.0 0.0 True \n", + " chi -180.0 180.0 0.0 True \n", + " phi -180.0 180.0 0.0 True \n", + " tth -180.0 180.0 0.0 True \n", + " ===== ========= ========== ===== ==== \n", + "sample: main ================ ===================================================\n", + " term value \n", + " ================ ===================================================\n", + " unit cell edges a=1.54, b=1.54, c=1.54 \n", + " unit cell angles alpha=90.0, beta=90.0, gamma=90.0 \n", + " [U] [[1. 0. 0.] \n", + " [0. 1. 0.] \n", + " [0. 0. 1.]] \n", + " [UB] [[ 4.07999046e+00 -2.49827363e-16 -2.49827363e-16] \n", + " [ 0.00000000e+00 4.07999046e+00 -2.49827363e-16] \n", + " [ 0.00000000e+00 0.00000000e+00 4.07999046e+00]]\n", + " ================ ===================================================\n", + "===================== ====================================================================\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent.restore(config_file)\n", + "sim4c.pa()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 700a452ad9a163d5f40ab4f3730c638cfe897dff Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Thu, 16 Nov 2023 13:03:13 -0600 Subject: [PATCH 3/7] DOC #288 --- .../examples/notebooks/how_configuration.ipynb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/source/examples/notebooks/how_configuration.ipynb b/docs/source/examples/notebooks/how_configuration.ipynb index dded152a..e739330f 100644 --- a/docs/source/examples/notebooks/how_configuration.ipynb +++ b/docs/source/examples/notebooks/how_configuration.ipynb @@ -15,10 +15,8 @@ "\n", "It is a local choice how and where to save this configuration. The first step\n", "is to define a common structure to be exported and restored by *hklpy*. The\n", - "structure would be stored in any of a variety of possibilities. A session could\n", - "restore the configuration automatically from the chosen storage. Later, the\n", - "session would export the configuration both automatically, and to a different\n", - "location at the choice of the user.\n", + "structure would be stored in any of a variety of possibilities (some of these\n", + "are provided by *hklpy*, the others are suggestions).\n", "\n", "- text file: stored in:\n", " - the local file directory\n", @@ -28,14 +26,20 @@ "- EPICS [Channel\n", " Access](https://epics-controls.org/resources-and-support/documents/ca/) string\n", " [*waveform*](https://epics.anl.gov/base/R7-0/3-docs/waveformRecord.html) record\n", - "- EPICS [pvAccess](https://epics-controls.org/resources-and-support/documents/pvaccess/)\n", - "structure\n", + "- EPICS [pvAccess](https://epics-controls.org/resources-and-support/documents/pvaccess/) structure\n", "- in-memory cache, such as:\n", " - [redis](https://redis.io)\n", "- database, such as:\n", " - [MySQL](https://mysql.com)\n", " - [SQLite](https://sqlite.org)\n", - "- Python object (such as used in this documentation)\n", + "- Python object: (such as used in this documentation)\n", + " - dictionary\n", + " - JSON string\n", + " - YAML string\n", + "\n", + "A session could restore the configuration automatically from the chosen storage.\n", + "Later, the session would export the configuration both automatically, and to a\n", + "different location at the choice of the user.\n", "\n", "**Objective**\n", "\n", From 78d8f0407d6a3508abf82278f38ef5d98a5165b1 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Fri, 17 Nov 2023 23:21:57 -0600 Subject: [PATCH 4/7] MNT #288 compact float format --- hkl/configuration.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hkl/configuration.py b/hkl/configuration.py index f9efdade..48dde2fe 100644 --- a/hkl/configuration.py +++ b/hkl/configuration.py @@ -566,6 +566,9 @@ def _preview(self, data, show_constraints=False, show_reflections=False): if not isinstance(data, dict): raise TypeError(f"Cannot interpret configuration data: {type(data)}") + def float_format(v): + return f"{round(v, SIGNIFICANT_DIGITS):g}" + text = ( f"name: {data.get('name', '-n/a-')}" f"\ndate: {data.get('datetime', '-n/a-')}" @@ -579,7 +582,7 @@ def _preview(self, data, show_constraints=False, show_reflections=False): sample = data["samples"][sname] row = [i, sname] for v in sample["lattice"].values(): - row.append(f"{round(v, SIGNIFICANT_DIGITS)}") + row.append(float_format(v)) row.append(len(sample["reflections"])) table.addRow(row) text += f"\n\n{title}\n{table}" @@ -598,9 +601,9 @@ def _preview(self, data, show_constraints=False, show_reflections=False): table.addLabel("orient?") for i, refl in enumerate(sample["reflections"], start=1): row = [i] - row += [f"{round(v, SIGNIFICANT_DIGITS)}" for v in refl["reflection"].values()] - row += [f"{round(v, SIGNIFICANT_DIGITS)}" for v in refl["position"].values()] - row.append(str(refl["wavelength"])) + row += [float_format(v) for v in refl["reflection"].values()] + row += [float_format(v) for v in refl["position"].values()] + row.append(f"{round(refl['wavelength'], SIGNIFICANT_DIGITS):g}") row.append(str(refl["orientation_reflection"])) table.addRow(row) text += f"\n\n{title}\n{table}" @@ -612,7 +615,7 @@ def _preview(self, data, show_constraints=False, show_reflections=False): for aname, constraint in data["constraints"].items(): row = [aname] for k in "low_limit high_limit value".split(): - row.append(f"{round(constraint[k], SIGNIFICANT_DIGITS)}") + row.append(float_format(constraint[k])) row.append(f"{constraint['fit']}") table.addRow(row) text += f"\n\n{title}\n{table}" @@ -686,6 +689,13 @@ def reciprocal_axes_names(self): def model(self) -> DCConfiguration: """Return validated diffractometer configuration object.""" diffractometer = self.diffractometer + + # either an empty dict or maps renamed axes to canonical + xref = dict(diffractometer.calc._axis_name_to_original) + + def canonical_name(axis): + return xref.get(axis, axis) + data = { "name": diffractometer.name, "geometry": diffractometer.calc._geometry.name_get(), @@ -703,7 +713,7 @@ def model(self) -> DCConfiguration: "library_version": libhkl.VERSION, # fmt: off "constraints": { - axis: { + canonical_name(axis): { parm: getattr(constraint, parm) for parm in "low_limit high_limit value fit".split() } From 105ae1453136d24915e508e7bedaa392ed1e5fd3 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Fri, 17 Nov 2023 23:22:11 -0600 Subject: [PATCH 5/7] TST #288 --- hkl/tests/test_configuration.py | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/hkl/tests/test_configuration.py b/hkl/tests/test_configuration.py index e04f8f0f..f955a4d3 100644 --- a/hkl/tests/test_configuration.py +++ b/hkl/tests/test_configuration.py @@ -401,3 +401,52 @@ def test_export_to_file(e4cv, tmp_path): assert path.exists() agent.restore(path) # just to be safe here + + +def test_preview(e4cv): + agent = DiffractometerConfiguration(e4cv) + + report = agent.preview(agent.export()).splitlines() + assert report[0] == f"name: {e4cv.name}" + assert report[2] == f"geometry: {e4cv.geometry_name.get()}" + assert report[4] == "Table of Samples" + assert "====" in report[5] + assert "sample" in report[6] + assert "main" in report[8] + assert len(report) == 10 + + report = agent.preview(agent.export(), show_reflections=True).splitlines() + assert len(report) == 10 # no reflections, no new info + + report = agent.preview(agent.export(), show_constraints=True).splitlines() + assert len(report) == 10 + 11 # new lines + assert report[12] == "Table of Axis Constraints" + assert "====" in report[13] + assert "fit?" in report[14] + assert report[16].startswith("omega") + assert report[19].startswith("tth") + + main = e4cv.calc.sample # the default sample + m_100 = main.add_reflection(1, 0, 0, (-45, 0, 0, 0)) + m_010 = main.add_reflection(0, 1, 0, (45, 0, 0, 0)) + m_001 = main.add_reflection(0, 0, 1, (45, 0, 90, 0)) + main.compute_UB(m_100, m_010) + report = agent.preview(agent.export(), show_reflections=True).splitlines() + assert len(report) == 10 + 10 # new lines + assert report[12] == f"Table of Reflections for Sample: {main.name}" + assert report[14].split()[1] == "h" + assert report[14].split()[-3] == "tth" + assert report[14].split()[-2] == "wavelength" + assert report[14].split()[-1] == "orient?" + assert report[16].split()[1] == "1" + assert report[16].split()[2] == "0" + assert report[16].split()[3] == "0" + assert report[16].split()[-1] == "True" + assert report[17].split()[1] == "0" + assert report[17].split()[2] == "1" + assert report[17].split()[3] == "0" + assert report[17].split()[-1] == "True" + assert report[18].split()[1] == "0" + assert report[18].split()[2] == "0" + assert report[18].split()[3] == "1" + assert report[18].split()[-1] == "False" From 11a9a365c9a95b077e49182eeddda243675b8f77 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Fri, 17 Nov 2023 23:24:19 -0600 Subject: [PATCH 6/7] DOC #288 rst syntax --- docs/source/examples/notebooks/var_nslsii_tardis.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/examples/notebooks/var_nslsii_tardis.ipynb b/docs/source/examples/notebooks/var_nslsii_tardis.ipynb index 0a13fb3b..82664173 100644 --- a/docs/source/examples/notebooks/var_nslsii_tardis.ipynb +++ b/docs/source/examples/notebooks/var_nslsii_tardis.ipynb @@ -11,7 +11,7 @@ "connections with the EPICS motors have been replaced with \n", "simulated motors for this example.\n", "\n", - "Using the [`E6C`](https://people.debian.org/~picca/hkl/hkl.html#orge5e0490)\n", + "Using the [E6C](https://people.debian.org/~picca/hkl/hkl.html#orge5e0490)\n", "geometry from [*libhkl*](https://people.debian.org/~picca/hkl/hkl.html),\n", "@cmazzoli found that, in this geometry, with the \"lifting_detector_mu\"\n", "mode, the following mapping applies:\n", From 27f2dc3631974ea4064993db6d2782ced1df19f6 Mon Sep 17 00:00:00 2001 From: Pete R Jemian Date: Fri, 17 Nov 2023 23:27:11 -0600 Subject: [PATCH 7/7] TST #288 remove unused symbol --- hkl/tests/test_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkl/tests/test_configuration.py b/hkl/tests/test_configuration.py index f955a4d3..deb6bf22 100644 --- a/hkl/tests/test_configuration.py +++ b/hkl/tests/test_configuration.py @@ -429,7 +429,7 @@ def test_preview(e4cv): main = e4cv.calc.sample # the default sample m_100 = main.add_reflection(1, 0, 0, (-45, 0, 0, 0)) m_010 = main.add_reflection(0, 1, 0, (45, 0, 0, 0)) - m_001 = main.add_reflection(0, 0, 1, (45, 0, 90, 0)) + main.add_reflection(0, 0, 1, (45, 0, 90, 0)) main.compute_UB(m_100, m_010) report = agent.preview(agent.export(), show_reflections=True).splitlines() assert len(report) == 10 + 10 # new lines