{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# 2D Continuous scan\n\nData on a regular two-dimensional grid which is the combination\nof an image and a histogram.\n\nThe x-axis defines the bin edges along the second dimension.\nThe y-axis defines the grid coordinates along the first dimension.\n\nThis is a typical 2D scanning example where a sample is scanned through\na focussed X-ray beam in two dimensions. The x motor is scanned continuously\nand both motors have an encoder which records the actual motor position as\nopposed to the requested or `set` position.\n\n.. code::\n\n @NX_class = \"NXroot\"\n @default = \"scan1\"\n scan1:\n @NX_class = \"NXentry\"\n @default = \"data\"\n data:\n @NX_class = \"NXdata\"\n @axes = [\"y_set\", \"x_set\"]\n @x_encoder_indices = [0, 1]\n @y_encoder_indices = 0\n @signal = \"z\"\n z: NX_FLOAT64[16,6]\n x_encoder: NX_FLOAT64[16,7]\n y_encoder: NX_FLOAT64[16]\n x_set: NX_FLOAT64[6]\n y_set: NX_FLOAT64[16]\n\nExplanation:\n\n1. ``@axes`` has two values which corresponds to the signal rank of two.\n\n2. ``x_set`` and ``y_set`` are the default axes which can be used by readers\n that cannot handle multi-dimensional coordinates.\n\n3. ``x_set_indices`` and ``y_set_indices`` are omitted because they would be equal to\n the position of ``\"x_set\"`` and ``\"y_set\"`` in ``@axes``.\n\n4. The first dimension is spanned by three axes ``y_set``, ``y_encoder`` and ``x_encoder``.\n The axes ``y_set`` and ``y_encoder`` have as many values are there are data points\n along the first dimension. This is because the y motor moves one step after each scan\n of the x motor.\n\n5. The second dimension is spanned by two axes ``x_set`` and ``x_encoder``. Since\n the x motor is scanned continuously, the encoder records the edge of every bin\n on which an data is recorded yielding 7 values instead of 6 along the second dimension.\n\n6. The ``x_encoder`` spans the first dimensions because the actual x edges are slightly\n different for every x motion at each y position. Since the y motor does not move while\n scanning x, there is no need for ``y_encoder`` to span the second dimension because\n the value along this dimension remains constant.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Data\nimport numpy as np\n\nx_set = np.linspace(-3, 3, 7)\ny_set = np.linspace(-3, 3, 16)\n\nrstate = np.random.RandomState(42)\nnoise_x = 0.1 * (x_set[1] - x_set[0])\nnoise_y = 0.1 * (y_set[1] - y_set[0])\nx_encoder = x_set[np.newaxis, :] + rstate.normal(0, noise_x, (len(y_set), len(x_set)))\ny_encoder = y_set + rstate.normal(0, noise_y, len(y_set))\n\nnx = len(x_set) - 1\nny = len(y_set)\nz = np.zeros((ny, nx))\nxx = np.linspace(-3, 3, 200)\nfor i in range(ny):\n zi = (1 - xx / 2 + xx**5 + y_encoder[i] ** 3) * np.exp(-(xx**2) - y_encoder[i] ** 2)\n z[i, :], _ = np.histogram(xx, bins=x_encoder[i, :], weights=zi)\n\n# Plot\nimport matplotlib.pyplot as plt # noqa E402\n\nplt.style.use(\"_mpl-gallery-nogrid\")\n\nfig, ax = plt.subplots()\n\ny_set_new = np.empty_like(y_set, shape=(len(y_set) + 1,))\ny_set_new[0] = 2 * y_set[0] - y_set[1]\ny_set_new[1:-1] = (y_set[:-1] + y_set[1:]) / 2\ny_set_new[-1] = 2 * y_set[-1] - y_set[-2]\n\nmesh = ax.pcolormesh(x_set, y_set_new, z, linewidth=0.7)\n\nplt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.8.16" } }, "nbformat": 4, "nbformat_minor": 0 }