{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Selection of a calibrant\n", "\n", "In this tutorial we will see how to select a calibrant for a given experimental setup.\n", "\n", "## Experimental setup\n", "\n", "The experimental setup is a classical protein crystallography setup with:\n", "\n", " * Large Pilatus 6M detector on a translation table\n", " * The small and intense beam of ~50 microns in size has a wavelength of 1 Angstrom\n", " * The detector is in *normal* condition: orthogonal to the beam and centered in the middle of the detector.\n", "\n", "The scientist in charge of this beamline want to ensure all encoders are working properly and needs to validate the setup for distances between 10cm and 80cm. \n", "He will buy reference material from NIST but he does not know which calibrant is best suited for his setup. \n", "We will assume all reference material sold by NIST are equally suitable for ray position (no issue with grain size, ...).\n", "\n", "The calibration works best in pyFAI if more than one Debye-Scherrer ring is seen on the detector.\n", "\n", "## Define the detector" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PyFAI version 0.18.0\n", "Detector Pilatus 6M\t PixelSize= 1.720e-04, 1.720e-04 m\n" ] } ], "source": [ "import time\n", "start_time = time.time()\n", "import pyFAI, pyFAI.detectors\n", "print(\"PyFAI version\", pyFAI.version)\n", "dete = pyFAI.detectors.Pilatus6M()\n", "print(dete)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Select reference materials\n", "\n", "The NIST sells different [Standard Refrence Materials](http://www.nist.gov/mml/mmsd/upload/852_08_81b.pdf), among them \n", "Silicon (SRM640), Lanthanum hexaboride (SRM660), Alumina (SRM676) and Ceria (SRM674) are commonly used. \n", "Many other exists: Cr203, TiO2, Zn0, SiO2, ... Evaluating them is left as an exercise.\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Calibrants available: C14H30O, Si_SRM640c, LaB6, cristobaltite, Ni, Au, CeO2, Si_SRM640a, ZnO, alpha_Al2O3, Al, Si_SRM640e, Si_SRM640d, quartz, AgBh, Si_SRM640b, TiO2, NaCl, LaB6_SRM660a, mock, PBBA, CuO, LaB6_SRM660c, CrOx, Cr2O3, Si, LaB6_SRM660b, Si_SRM640\n" ] } ], "source": [ "import pyFAI.calibrant\n", "print(pyFAI.calibrant.ALL_CALIBRANTS)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may wonder where the names of the calibrant came from and how they have been established. \n", "\n", "The name of all calibrant available in your version of pyFAI can be listed by just printing out the content of ALL_CALIBRANTS. New calibrant may have been added in pyFAI in more recent releases, just have a look at the [developent web page](https://github.com/silx-kit/pyFAI/tree/master/pyFAI/resources/calibration).\n", "\n", "Most of those calibrant files, which contain the *d-spacing* in Angstrom between Miller plans, have been prepared from the unit cell of the compount, found in publication. This publication is referenced in the header of the file.\n", "If one wishes to regenerate those files, the *pyFAI.calibrant.Cell* class may be used for.\n", "\n", "We will now focus on a subset of calibrant, instanciate them and put them into a dictionnary. The Python construct used here is called *dict-comprehension* and allows the creation and population of a dictionnary in a single line." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Si : Si Calibrant with 708 reflections \n", "LaB6 : LaB6 Calibrant with 1441 reflections \n", "CeO2 : CeO2 Calibrant with 41 reflections \n", "alpha_Al2O3 : alpha_Al2O3 Calibrant with 31 reflections \n" ] } ], "source": [ "cals = dict((name,pyFAI.calibrant.ALL_CALIBRANTS(name)) for name in (\"Si\", \"LaB6\", \"CeO2\", \"alpha_Al2O3\"))\n", "for k,v in cals.items():\n", " print(k,\": \", v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To be able to use those calibrants, one needs to define the wavelength used, here 1 Angstrom." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Si : Si Calibrant with 28 reflections at wavelength 1e-10\n", "LaB6 : LaB6 Calibrant with 59 reflections at wavelength 1e-10\n", "CeO2 : CeO2 Calibrant with 41 reflections at wavelength 1e-10\n", "alpha_Al2O3 : alpha_Al2O3 Calibrant with 31 reflections at wavelength 1e-10\n" ] } ], "source": [ "wl = 1e-10\n", "\n", "for k,v in cals.items():\n", " v.wavelength = wl\n", " print(k,\": \", v)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Short distance images\n", "\n", "The shortest the detector can come to the sample is about 10cm (to leave space for the beamstop).\n", "We will generate images of diffraction at this distance. \n", "\n", "For the display of images we will use *matplotlib* inlined and some utilities from pyFAI to display images." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Detector center at 0.21732217, 0.21181805\n", "Detector Pilatus 6M\t PixelSize= 1.720e-04, 1.720e-04 m\n", "SampleDetDist= 1.000000e-01m\tPONI= 2.173222e-01, 2.118181e-01m\trot1=0.000000 rot2= 0.000000 rot3= 0.000000 rad\n", "DirectBeamDist= 100.000mm\tCenter: x=1231.500, y=1263.501 pix\tTilt=0.000 deg tiltPlanRotation= 0.000 deg\n" ] } ], "source": [ "p1, p2, p3 = dete.calc_cartesian_positions()\n", "poni1 = p1.mean()\n", "poni2 = p2.mean()\n", "print(\"Detector center at %s, %s\"%(poni1, poni2))\n", "from pyFAI.azimuthalIntegrator import AzimuthalIntegrator\n", "ai_short = AzimuthalIntegrator(dist=0.1, poni1=poni1, poni2=poni2,detector=dete)\n", "print(ai_short)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Populating the interactive namespace from numpy and matplotlib\n" ] } ], "source": [ "%pylab nbagg\n", "from pyFAI.gui import jupyter" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\n", "\n", "mpl.get_websocket_type = function() {\n", " if (typeof(WebSocket) !== 'undefined') {\n", " return WebSocket;\n", " } else if (typeof(MozWebSocket) !== 'undefined') {\n", " return MozWebSocket;\n", " } else {\n", " alert('Your browser does not have WebSocket support.' +\n", " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", " 'Firefox 4 and 5 are also supported but you ' +\n", " 'have to enable WebSockets in about:config.');\n", " };\n", "}\n", "\n", "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", " this.id = figure_id;\n", "\n", " this.ws = websocket;\n", "\n", " this.supports_binary = (this.ws.binaryType != undefined);\n", "\n", " if (!this.supports_binary) {\n", " var warnings = document.getElementById(\"mpl-warnings\");\n", " if (warnings) {\n", " warnings.style.display = 'block';\n", " warnings.textContent = (\n", " \"This browser does not support binary websocket messages. \" +\n", " \"Performance may be slow.\");\n", " }\n", " }\n", "\n", " this.imageObj = new Image();\n", "\n", " this.context = undefined;\n", " this.message = undefined;\n", " this.canvas = undefined;\n", " this.rubberband_canvas = undefined;\n", " this.rubberband_context = undefined;\n", " this.format_dropdown = undefined;\n", "\n", " this.image_mode = 'full';\n", "\n", " this.root = $('
');\n", " this._root_extra_style(this.root)\n", " this.root.attr('style', 'display: inline-block');\n", "\n", " $(parent_element).append(this.root);\n", "\n", " this._init_header(this);\n", " this._init_canvas(this);\n", " this._init_toolbar(this);\n", "\n", " var fig = this;\n", "\n", " this.waiting = false;\n", "\n", " this.ws.onopen = function () {\n", " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", " fig.send_message(\"send_image_mode\", {});\n", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\n", " fig.send_message(\"refresh\", {});\n", " }\n", "\n", " this.imageObj.onload = function() {\n", " if (fig.image_mode == 'full') {\n", " // Full images could contain transparency (where diff images\n", " // almost always do), so we need to clear the canvas so that\n", " // there is no ghosting.\n", " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", " };\n", "\n", " this.imageObj.onunload = function() {\n", " fig.ws.close();\n", " }\n", "\n", " this.ws.onmessage = this._make_on_message_function(this);\n", "\n", " this.ondownload = ondownload;\n", "}\n", "\n", "mpl.figure.prototype._init_header = function() {\n", " var titlebar = $(\n", " '');\n", " var titletext = $(\n", " '');\n", " titlebar.append(titletext)\n", " this.root.append(titlebar);\n", " this.header = titletext[0];\n", "}\n", "\n", "\n", "\n", "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "\n", "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "mpl.figure.prototype._init_canvas = function() {\n", " var fig = this;\n", "\n", " var canvas_div = $('');\n", "\n", " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", "\n", " function canvas_keyboard_event(event) {\n", " return fig.key_event(event, event['data']);\n", " }\n", "\n", " canvas_div.keydown('key_press', canvas_keyboard_event);\n", " canvas_div.keyup('key_release', canvas_keyboard_event);\n", " this.canvas_div = canvas_div\n", " this._canvas_extra_style(canvas_div)\n", " this.root.append(canvas_div);\n", "\n", " var canvas = $('');\n", " canvas.addClass('mpl-canvas');\n", " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", "\n", " this.canvas = canvas[0];\n", " this.context = canvas[0].getContext(\"2d\");\n", "\n", " var backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", "\n", " var rubberband = $('');\n", " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", "\n", " var pass_mouse_events = true;\n", "\n", " canvas_div.resizable({\n", " start: function(event, ui) {\n", " pass_mouse_events = false;\n", " },\n", " resize: function(event, ui) {\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " stop: function(event, ui) {\n", " pass_mouse_events = true;\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " });\n", "\n", " function mouse_event_fn(event) {\n", " if (pass_mouse_events)\n", " return fig.mouse_event(event, event['data']);\n", " }\n", "\n", " rubberband.mousedown('button_press', mouse_event_fn);\n", " rubberband.mouseup('button_release', mouse_event_fn);\n", " // Throttle sequential mouse events to 1 every 20ms.\n", " rubberband.mousemove('motion_notify', mouse_event_fn);\n", "\n", " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", "\n", " canvas_div.on(\"wheel\", function (event) {\n", " event = event.originalEvent;\n", " event['data'] = 'scroll'\n", " if (event.deltaY < 0) {\n", " event.step = 1;\n", " } else {\n", " event.step = -1;\n", " }\n", " mouse_event_fn(event);\n", " });\n", "\n", " canvas_div.append(canvas);\n", " canvas_div.append(rubberband);\n", "\n", " this.rubberband = rubberband;\n", " this.rubberband_canvas = rubberband[0];\n", " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", " this.rubberband_context.strokeStyle = \"#000000\";\n", "\n", " this._resize_canvas = function(width, height) {\n", " // Keep the size of the canvas, canvas container, and rubber band\n", " // canvas in synch.\n", " canvas_div.css('width', width)\n", " canvas_div.css('height', height)\n", "\n", " canvas.attr('width', width * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", "\n", " rubberband.attr('width', width);\n", " rubberband.attr('height', height);\n", " }\n", "\n", " // Set the figure to an initial 600x600px, this will subsequently be updated\n", " // upon first draw.\n", " this._resize_canvas(600, 600);\n", "\n", " // Disable right mouse context menu.\n", " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", " return false;\n", " });\n", "\n", " function set_focus () {\n", " canvas.focus();\n", " canvas_div.focus();\n", " }\n", "\n", " window.setTimeout(set_focus, 100);\n", "}\n", "\n", "mpl.figure.prototype._init_toolbar = function() {\n", " var fig = this;\n", "\n", " var nav_element = $('')\n", " nav_element.attr('style', 'width: 100%');\n", " this.root.append(nav_element);\n", "\n", " // Define a callback function for later on.\n", " function toolbar_event(event) {\n", " return fig.toolbar_button_onclick(event['data']);\n", " }\n", " function toolbar_mouse_event(event) {\n", " return fig.toolbar_button_onmouseover(event['data']);\n", " }\n", "\n", " for(var toolbar_ind in mpl.toolbar_items) {\n", " var name = mpl.toolbar_items[toolbar_ind][0];\n", " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", " var image = mpl.toolbar_items[toolbar_ind][2];\n", " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", "\n", " if (!name) {\n", " // put a spacer in here.\n", " continue;\n", " }\n", " var button = $('');\n", " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", " 'ui-button-icon-only');\n", " button.attr('role', 'button');\n", " button.attr('aria-disabled', 'false');\n", " button.click(method_name, toolbar_event);\n", " button.mouseover(tooltip, toolbar_mouse_event);\n", "\n", " var icon_img = $('');\n", " icon_img.addClass('ui-button-icon-primary ui-icon');\n", " icon_img.addClass(image);\n", " icon_img.addClass('ui-corner-all');\n", "\n", " var tooltip_span = $('');\n", " tooltip_span.addClass('ui-button-text');\n", " tooltip_span.html(tooltip);\n", "\n", " button.append(icon_img);\n", " button.append(tooltip_span);\n", "\n", " nav_element.append(button);\n", " }\n", "\n", " var fmt_picker_span = $('');\n", "\n", " var fmt_picker = $('');\n", " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", " fmt_picker_span.append(fmt_picker);\n", " nav_element.append(fmt_picker_span);\n", " this.format_dropdown = fmt_picker[0];\n", "\n", " for (var ind in mpl.extensions) {\n", " var fmt = mpl.extensions[ind];\n", " var option = $(\n", " '', {selected: fmt === mpl.default_extension}).html(fmt);\n", " fmt_picker.append(option)\n", " }\n", "\n", " // Add hover states to the ui-buttons\n", " $( \".ui-button\" ).hover(\n", " function() { $(this).addClass(\"ui-state-hover\");},\n", " function() { $(this).removeClass(\"ui-state-hover\");}\n", " );\n", "\n", " var status_bar = $('