{ "cells": [ { "cell_type": "markdown", "id": "81b96c07-56ce-47b8-8a63-bbbb27263f5c", "metadata": { "nbsphinx": "hidden" }, "source": [ "This notebook is part of the `kikuchipy` documentation https://kikuchipy.org.\n", "Links to the documentation won't work from the notebook." ] }, { "cell_type": "markdown", "id": "3f42fed2-753b-431b-9469-b3351176f757", "metadata": {}, "source": [ "# PC calibration: \"moving-screen\" technique\n", "\n", "The projection center (PC) describing the position of the EBSD detector relative to the beam-sample interaction volume can be estimated by the \"moving-screen\" technique Hjelen et al..\n", "In this tutorial, we test this technique to get a rough estimate of the PC.\n", "\n", "The technique assumes that the PC vector from the detector to the sample, shown in the [top figure in the reference frames tutorial](reference_frames.ipynb#Detector-sample-geometry), is normal to the detector screen as well as the incoming electron beam.\n", "It will therefore intersect the screen at a position independent of the detector distance (DD).\n", "To find this position, we need two EBSD patterns acquired with a stationary beam but a known difference $\\Delta z$ in DD, say 5 mm.\n", "\n", "First, the goal is to find the pattern position that does not shift between the two camera positions, ($PC_x$, $PC_y$).\n", "This point can be estimated in fractions of screen width and height, respectively, by selecting the same pattern features in both patterns.\n", "The two points of each pattern feature can then be used to form a straight line, and two or more such lines should intersect at ($PC_x$, $PC_y$).\n", "\n", "Second, the DD ($PC_z$) can be estimated from the same points.\n", "After finding the distances $L_{in}$ and $L_{out}$ between two points (features) in both patterns (in = operating position, out = 5 mm from operating position), the DD can be found from the relation\n", "\n", "$$\n", "\\mathrm{DD} = \\frac{\\Delta z}{L_{out}/L_{in} - 1},\n", "$$\n", "\n", "where DD is given in the same unit as $\\Delta z$.\n", "If also the detector pixel size $\\delta$ is known (e.g. 46 mm / 508 px), $PC_z$ can be given in the fraction of the detector screen height\n", "\n", "$$\n", "PC_z = \\frac{\\mathrm{DD}}{N_r \\delta b},\n", "$$\n", "\n", "where $N_r$ is the number of detector rows and $b$ is the binning factor.\n", "\n", "Let's first import necessary libraries" ] }, { "cell_type": "code", "execution_count": null, "id": "4d17ba0c-17d7-42bc-9792-bef0aac7e8d6", "metadata": {}, "outputs": [], "source": [ "# Exchange inline for notebook or qt5 (from pyqt) for interactive plotting\n", "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from diffsims.crystallography import ReciprocalLatticeVector\n", "from orix.crystal_map import PhaseList\n", "import kikuchipy as kp" ] }, { "cell_type": "markdown", "id": "f01ebf69-77e9-40bf-a219-e937a4516c08", "metadata": {}, "source": [ "We will find an estimate of the PC from two single crystal silicon patterns.\n", "These are included in the [kikuchipy.data](../reference/generated/kikuchipy.data.rst) module" ] }, { "cell_type": "code", "execution_count": null, "id": "35f5fdb8-360d-47d2-851a-d1ad0d49c9f5", "metadata": {}, "outputs": [], "source": [ "s_in = kp.data.si_ebsd_moving_screen(0, allow_download=True)\n", "s_in.remove_static_background()\n", "s_in.remove_dynamic_background()\n", "\n", "s_out5mm = kp.data.si_ebsd_moving_screen(5, allow_download=True)\n", "s_out5mm.remove_static_background()\n", "s_out5mm.remove_dynamic_background()" ] }, { "cell_type": "markdown", "id": "2ad933d0-31d8-44e9-8591-60eca7867039", "metadata": {}, "source": [ "As a first approximation, we can find the detector pixel positions of the same features in both patterns manually with Matplotlib.\n", "Cursor pixel coordinates are displayed in the upper right part of the Matplotlib window when plotting with an interactive backend (e.g. qt5 or notebook)." ] }, { "cell_type": "code", "execution_count": null, "id": "b6b7634b-2295-49f1-bbb7-ab567486209b", "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(14, 7), layout=\"tight\")\n", "for ax, data in zip(axes, [s_in.data, s_out5mm.data]):\n", " ax.imshow(data, cmap=\"gray\")" ] }, { "cell_type": "markdown", "id": "32839589-ac31-4965-be22-fc7b3a8e94fe", "metadata": {}, "source": [ "In this example, we choose the positions of three zone axes.\n", "The PC calibration is performed by creating an [PCCalibrationMovingScreen](../reference/generated/kikuchipy.detectors.PCCalibrationMovingScreen.rst) instance" ] }, { "cell_type": "code", "execution_count": null, "id": "24657337-00fe-4e78-bf33-820b699b636a", "metadata": {}, "outputs": [], "source": [ "cal = kp.detectors.PCCalibrationMovingScreen(\n", " pattern_in=s_in.data,\n", " pattern_out=s_out5mm.data,\n", " points_in=[(109, 131), (390, 139), (246, 232)],\n", " points_out=[(77, 146), (424, 156), (246, 269)],\n", " delta_z=5,\n", " px_size=None, # Default\n", " convention=\"tsl\", # Default\n", ")\n", "cal" ] }, { "cell_type": "markdown", "id": "e219623f-366c-49b5-97da-262cf10e4c14", "metadata": {}, "source": [ "We see that ($PC_x$, $PC_y$) = (0.5123, 0.8606), while DD = 21.7 mm.\n", "To get $PC_z$ in fractions of detector height, we have to provide the detector pixel size $\\delta$ upon initialization, or set it directly and recalculate the PC" ] }, { "cell_type": "code", "execution_count": null, "id": "65cefee2-3cda-447c-bc01-52f490464136", "metadata": {}, "outputs": [], "source": [ "cal.px_size = 90e-3 # mm/px\n", "cal" ] }, { "cell_type": "markdown", "id": "1185a0bd-a281-45e4-8780-fee70a0d0100", "metadata": {}, "source": [ "We can visualize the estimation by using the convenience method [PCCalibrationMovingScreen.plot()](../reference/generated/kikuchipy.detectors.PCCalibrationMovingScreen.plot.rst)" ] }, { "cell_type": "code", "execution_count": null, "id": "805760aa-350f-4dbe-8d26-421c499d060c", "metadata": {}, "outputs": [], "source": [ "cal.plot()" ] }, { "cell_type": "markdown", "id": "b98843b2-33d6-47a2-9780-856149d6b229", "metadata": {}, "source": [ "As expected, the three lines in the right figure meet at approimately the same point.\n", "We can replot the three images and zoom in on the PC to see how close they are to each other" ] }, { "cell_type": "code", "execution_count": null, "id": "9bd67169-35af-4df5-bad4-632f9c855836", "metadata": {}, "outputs": [], "source": [ "# PCy defined from top to bottom, otherwise \"tsl\", defined from bottom to top\n", "cal.convention = \"bruker\"\n", "pcx, pcy, _ = cal.pc\n", "\n", "# Use two standard deviations of all $PC_x$ estimates as the axis limits\n", "# (scaled with pattern shape)\n", "two_std = 2 * np.std(cal.pcx_all, axis=0)\n", "\n", "fig = cal.plot(return_figure=True)\n", "ax2 = fig.axes[2]\n", "ax2.set_xlim([cal.ncols * (pcx - two_std), cal.ncols * (pcx + two_std)])\n", "ax2.set_ylim([cal.nrows * (pcy - two_std), cal.nrows * (pcy + two_std)])\n", "fig.subplots_adjust(wspace=0.05)" ] }, { "cell_type": "markdown", "id": "d0cdf7ed-73cc-4ae1-936c-6c38e2b977f4", "metadata": {}, "source": [ "We can use this PC estimate as an initial guess when refining the PC using Hough indexing available from [PyEBSDIndex](https://pyebsdindex.readthedocs.io).\n", "See the [Hough indexing tutorial](hough_indexing.ipynb) for details.\n", "\n", "
\n", "\n", "Note\n", "\n", "PyEBSDIndex is an optional dependency of kikuchipy, and can be installed with both `pip` and `conda` (from `conda-forge`).\n", "To install PyEBSDIndex, see their [installation instructions](https://pyebsdindex.readthedocs.io/en/latest/user/installation.html).\n", "\n", "
\n", "\n", "Create a detector with the correct shape and sample tilt, adding the PC estimate" ] }, { "cell_type": "code", "execution_count": null, "id": "5d480da6-3c92-4360-bb84-7019f1881a6e", "metadata": {}, "outputs": [], "source": [ "det = kp.detectors.EBSDDetector(cal.shape, sample_tilt=70, pc=cal.pc)\n", "det" ] }, { "cell_type": "markdown", "id": "54c167c4-ea00-48ec-8eb6-3999774c6ccf", "metadata": {}, "source": [ "Create an EBSDIndexer for use with PyEBSDIndex" ] }, { "cell_type": "code", "execution_count": null, "id": "0913f503-1a8f-4352-a41c-e1069fa4108b", "metadata": {}, "outputs": [], "source": [ "phase_list = PhaseList(names=\"si\", space_groups=227)\n", "print(phase_list)\n", "\n", "indexer = det.get_indexer(phase_list)" ] }, { "cell_type": "markdown", "id": "396abf7b-5a0b-489e-aeb6-2cfac0e3210c", "metadata": {}, "source": [ "Optimize the PC via Hough indexing and plot the difference between the estimated and optimized PCs" ] }, { "cell_type": "code", "execution_count": null, "id": "975c42e5-c305-4be8-a765-f91db4fc9797", "metadata": {}, "outputs": [], "source": [ "det_ref = s_in.hough_indexing_optimize_pc(pc0=det.pc, indexer=indexer, method=\"PSO\")\n", "\n", "print(det.pc - det_ref.pc)" ] }, { "cell_type": "markdown", "id": "23451cba-9139-4a54-9635-cdb389653d03", "metadata": {}, "source": [ "Index the pattern via Hough indexing" ] }, { "cell_type": "code", "execution_count": null, "id": "fb498fd8-266f-4eb3-992c-14f3bbdd38c9", "metadata": {}, "outputs": [], "source": [ "indexer.PC = det_ref.pc\n", "\n", "xmap = s_in.hough_indexing(phase_list, indexer=indexer, verbose=2)" ] }, { "cell_type": "code", "execution_count": null, "id": "f8364954-458d-4a62-9354-92fa0d763dc4", "metadata": {}, "outputs": [], "source": [ "print(xmap)\n", "print(xmap.fit)" ] }, { "cell_type": "markdown", "id": "cabd4d6b-e21f-495e-a521-7391940c3427", "metadata": {}, "source": [ "Create a simulator with the five $\\{hkl\\}$ families (reflectors) $\\{111\\}$, $\\{200\\}$, $\\{220\\}$, $\\{222\\}$, and $\\{311\\}$" ] }, { "cell_type": "code", "execution_count": null, "id": "7b25c6fd-dc92-4f6b-ba4d-6bf7bca8e0be", "metadata": { "tags": [] }, "outputs": [], "source": [ "ref = ReciprocalLatticeVector(\n", " phase=xmap.phases[0],\n", " hkl=[[1, 1, 1], [2, 0, 0], [2, 2, 0], [2, 2, 2], [3, 1, 1]],\n", ")\n", "ref = ref.symmetrise()\n", "\n", "simulator = kp.simulations.KikuchiPatternSimulator(ref)\n", "\n", "sim = simulator.on_detector(det_ref, xmap.rotations)" ] }, { "cell_type": "markdown", "id": "a57f7fbb-9fbf-4e5c-b93e-d719b93afb0a", "metadata": {}, "source": [ "Plot a [geometrical simulation](geometrical_ebsd_simulations.ipynb) on top of the pattern using Kikuchi band centers" ] }, { "cell_type": "code", "execution_count": null, "id": "40c08dca-d30a-46c9-8f88-63227ec2abc2", "metadata": { "nbsphinx-thumbnail": { "tooltip": "Estimate the projection center (PC) using the moving-screen technique" }, "tags": [ "nbsphinx-thumbnail" ] }, "outputs": [], "source": [ "sim.plot(\n", " coordinates=\"gnomonic\",\n", " pattern=s_in.data,\n", " zone_axes_labels=False,\n", " zone_axes=False,\n", ")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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": 5 }