{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Modeling of the thickness of the sensor\n",
    "\n",
    "In this notebook we will re-use the experiment done at ID28 and previously calibrated and model in 3D the detector.\n",
    "\n",
    "This detector is a Pilatus 1M with a 450µm thick silicon sensor. Let's first have a look at the absorption coefficients of this sensor material: https://physics.nist.gov/PhysRefData/XrayMassCoef/ElemTab/z14.html\n",
    "\n",
    "First we retieve the results of the previous step, then calculate the absorption efficiency:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "wavelength: 6.968e-11m,\t dist: 2.845e-01m,\t poni1: 8.865e-02m,\t poni2: 8.931e-02m,\t energy: 17.793keV\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "start_time = time.perf_counter()\n",
    "from matplotlib.pyplot import subplots\n",
    "import numpy\n",
    "import fabio, pyFAI, pyFAI.units, pyFAI.detectors, pyFAI.azimuthalIntegrator\n",
    "import json\n",
    "with open(\"id28.json\") as f:\n",
    "    calib = json.load(f)\n",
    "\n",
    "thickness = 450e-6\n",
    "wavelength = calib[\"wavelength\"]\n",
    "dist = calib[\"param\"][calib['param_names'].index(\"dist\")]\n",
    "poni1 = calib[\"param\"][calib['param_names'].index(\"poni1\")]\n",
    "poni2 = calib[\"param\"][calib['param_names'].index(\"poni2\")]\n",
    "energy = pyFAI.units.hc/(wavelength*1e10)\n",
    "print(\"wavelength: %.3em,\\t dist: %.3em,\\t poni1: %.3em,\\t poni2: %.3em,\\t energy: %.3fkeV\" % \n",
    "      (wavelength, dist, poni1, poni2, energy))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Absorption coeficient at 17.8 keV"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "µ = 1537.024174 m^-1 hence absorption efficiency for 450µm: 49.9 %\n"
     ]
    }
   ],
   "source": [
    "# density from https://en.wikipedia.org/wiki/Silicon\n",
    "rho = 2.3290 # g/cm^3\n",
    "\n",
    "#Absorption from https://physics.nist.gov/PhysRefData/XrayMassCoef/ElemTab/z14.html\n",
    "# Nota: enegies are in MeV !\n",
    "Si_abs = \"\"\"\n",
    "   2.00000E-03  2.777E+03  2.669E+03 \n",
    "   3.00000E-03  9.784E+02  9.516E+02 \n",
    "   4.00000E-03  4.529E+02  4.427E+02 \n",
    "   5.00000E-03  2.450E+02  2.400E+02 \n",
    "   6.00000E-03  1.470E+02  1.439E+02 \n",
    "   8.00000E-03  6.468E+01  6.313E+01 \n",
    "   1.00000E-02  3.389E+01  3.289E+01 \n",
    "   1.50000E-02  1.034E+01  9.794E+00 \n",
    "   2.00000E-02  4.464E+00  4.076E+00 \n",
    "   3.00000E-02  1.436E+00  1.164E+00 \n",
    "   4.00000E-02  7.012E-01  4.782E-01 \n",
    "   5.00000E-02  4.385E-01  2.430E-01 \n",
    "   6.00000E-02  3.207E-01  1.434E-01 \n",
    "   8.00000E-02  2.228E-01  6.896E-02 \n",
    "   1.00000E-01  1.835E-01  4.513E-02 \n",
    "   1.50000E-01  1.448E-01  3.086E-02 \n",
    "   2.00000E-01  1.275E-01  2.905E-02 \n",
    "   3.00000E-01  1.082E-01  2.932E-02 \n",
    "   4.00000E-01  9.614E-02  2.968E-02 \n",
    "   5.00000E-01  8.748E-02  2.971E-02 \n",
    "   6.00000E-01  8.077E-02  2.951E-02 \n",
    "   8.00000E-01  7.082E-02  2.875E-02 \n",
    "   1.00000E+00  6.361E-02  2.778E-02 \n",
    "   1.25000E+00  5.688E-02  2.652E-02 \n",
    "   1.50000E+00  5.183E-02  2.535E-02 \n",
    "   2.00000E+00  4.480E-02  2.345E-02 \n",
    "   3.00000E+00  3.678E-02  2.101E-02 \n",
    "   4.00000E+00  3.240E-02  1.963E-02 \n",
    "   5.00000E+00  2.967E-02  1.878E-02 \n",
    "   6.00000E+00  2.788E-02  1.827E-02 \n",
    "   8.00000E+00  2.574E-02  1.773E-02 \n",
    "   1.00000E+01  2.462E-02  1.753E-02 \n",
    "   1.50000E+01  2.352E-02  1.746E-02 \n",
    "   2.00000E+01  2.338E-02  1.757E-02 \"\"\"\n",
    "data = numpy.array([[float(i) for i in line.split()] for line in Si_abs.split(\"\\n\") if line])\n",
    "energy_tab, mu_over_rho, mu_en_over_rho = data.T\n",
    "abs_18 = numpy.interp(energy, energy_tab*1e3, mu_en_over_rho) \n",
    "mu = abs_18*rho*1e+2\n",
    "eff = 1.0-numpy.exp(-mu*thickness)\n",
    "\n",
    "print(\"µ = %f m^-1 hence absorption efficiency for 450µm: %.1f %%\"%(mu, eff*100))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "/* global mpl */\n",
       "window.mpl = {};\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(\n",
       "            '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",
       "\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 = document.createElement('div');\n",
       "    this.root.setAttribute('style', 'display: inline-block');\n",
       "    this._root_extra_style(this.root);\n",
       "\n",
       "    parent_element.appendChild(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 (fig.ratio !== 1) {\n",
       "            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.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 = document.createElement('div');\n",
       "    titlebar.classList =\n",
       "        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n",
       "    var titletext = document.createElement('div');\n",
       "    titletext.classList = 'ui-dialog-title';\n",
       "    titletext.setAttribute(\n",
       "        'style',\n",
       "        'width: 100%; text-align: center; padding: 3px;'\n",
       "    );\n",
       "    titlebar.appendChild(titletext);\n",
       "    this.root.appendChild(titlebar);\n",
       "    this.header = titletext;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = (this.canvas_div = document.createElement('div'));\n",
       "    canvas_div.setAttribute(\n",
       "        'style',\n",
       "        'border: 1px solid #ddd;' +\n",
       "            'box-sizing: content-box;' +\n",
       "            'clear: both;' +\n",
       "            'min-height: 1px;' +\n",
       "            'min-width: 1px;' +\n",
       "            'outline: 0;' +\n",
       "            'overflow: hidden;' +\n",
       "            'position: relative;' +\n",
       "            'resize: both;'\n",
       "    );\n",
       "\n",
       "    function on_keyboard_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.key_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    canvas_div.addEventListener(\n",
       "        'keydown',\n",
       "        on_keyboard_event_closure('key_press')\n",
       "    );\n",
       "    canvas_div.addEventListener(\n",
       "        'keyup',\n",
       "        on_keyboard_event_closure('key_release')\n",
       "    );\n",
       "\n",
       "    this._canvas_extra_style(canvas_div);\n",
       "    this.root.appendChild(canvas_div);\n",
       "\n",
       "    var canvas = (this.canvas = document.createElement('canvas'));\n",
       "    canvas.classList.add('mpl-canvas');\n",
       "    canvas.setAttribute('style', 'box-sizing: content-box;');\n",
       "\n",
       "    this.context = canvas.getContext('2d');\n",
       "\n",
       "    var backingStore =\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        this.context.webkitBackingStorePixelRatio ||\n",
       "        this.context.mozBackingStorePixelRatio ||\n",
       "        this.context.msBackingStorePixelRatio ||\n",
       "        this.context.oBackingStorePixelRatio ||\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        1;\n",
       "\n",
       "    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "    if (this.ratio !== 1) {\n",
       "        fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n",
       "    }\n",
       "\n",
       "    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n",
       "        'canvas'\n",
       "    ));\n",
       "    rubberband_canvas.setAttribute(\n",
       "        'style',\n",
       "        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n",
       "    );\n",
       "\n",
       "    var resizeObserver = new ResizeObserver(function (entries) {\n",
       "        var nentries = entries.length;\n",
       "        for (var i = 0; i < nentries; i++) {\n",
       "            var entry = entries[i];\n",
       "            var width, height;\n",
       "            if (entry.contentBoxSize) {\n",
       "                if (entry.contentBoxSize instanceof Array) {\n",
       "                    // Chrome 84 implements new version of spec.\n",
       "                    width = entry.contentBoxSize[0].inlineSize;\n",
       "                    height = entry.contentBoxSize[0].blockSize;\n",
       "                } else {\n",
       "                    // Firefox implements old version of spec.\n",
       "                    width = entry.contentBoxSize.inlineSize;\n",
       "                    height = entry.contentBoxSize.blockSize;\n",
       "                }\n",
       "            } else {\n",
       "                // Chrome <84 implements even older version of spec.\n",
       "                width = entry.contentRect.width;\n",
       "                height = entry.contentRect.height;\n",
       "            }\n",
       "\n",
       "            // Keep the size of the canvas and rubber band canvas in sync with\n",
       "            // the canvas container.\n",
       "            if (entry.devicePixelContentBoxSize) {\n",
       "                // Chrome 84 implements new version of spec.\n",
       "                canvas.setAttribute(\n",
       "                    'width',\n",
       "                    entry.devicePixelContentBoxSize[0].inlineSize\n",
       "                );\n",
       "                canvas.setAttribute(\n",
       "                    'height',\n",
       "                    entry.devicePixelContentBoxSize[0].blockSize\n",
       "                );\n",
       "            } else {\n",
       "                canvas.setAttribute('width', width * fig.ratio);\n",
       "                canvas.setAttribute('height', height * fig.ratio);\n",
       "            }\n",
       "            canvas.setAttribute(\n",
       "                'style',\n",
       "                'width: ' + width + 'px; height: ' + height + 'px;'\n",
       "            );\n",
       "\n",
       "            rubberband_canvas.setAttribute('width', width);\n",
       "            rubberband_canvas.setAttribute('height', height);\n",
       "\n",
       "            // And update the size in Python. We ignore the initial 0/0 size\n",
       "            // that occurs as the element is placed into the DOM, which should\n",
       "            // otherwise not happen due to the minimum size styling.\n",
       "            if (width != 0 && height != 0) {\n",
       "                fig.request_resize(width, height);\n",
       "            }\n",
       "        }\n",
       "    });\n",
       "    resizeObserver.observe(canvas_div);\n",
       "\n",
       "    function on_mouse_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.mouse_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousedown',\n",
       "        on_mouse_event_closure('button_press')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseup',\n",
       "        on_mouse_event_closure('button_release')\n",
       "    );\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousemove',\n",
       "        on_mouse_event_closure('motion_notify')\n",
       "    );\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseenter',\n",
       "        on_mouse_event_closure('figure_enter')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseleave',\n",
       "        on_mouse_event_closure('figure_leave')\n",
       "    );\n",
       "\n",
       "    canvas_div.addEventListener('wheel', function (event) {\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        on_mouse_event_closure('scroll')(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.appendChild(canvas);\n",
       "    canvas_div.appendChild(rubberband_canvas);\n",
       "\n",
       "    this.rubberband_context = rubberband_canvas.getContext('2d');\n",
       "    this.rubberband_context.strokeStyle = '#000000';\n",
       "\n",
       "    this._resize_canvas = function (width, height, forward) {\n",
       "        if (forward) {\n",
       "            canvas_div.style.width = width + 'px';\n",
       "            canvas_div.style.height = height + 'px';\n",
       "        }\n",
       "    };\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n",
       "        event.preventDefault();\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 toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'mpl-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'mpl-button-group';\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",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'mpl-button-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        var button = (fig.buttons[name] = document.createElement('button'));\n",
       "        button.classList = 'mpl-widget';\n",
       "        button.setAttribute('role', 'button');\n",
       "        button.setAttribute('aria-disabled', 'false');\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "\n",
       "        var icon_img = document.createElement('img');\n",
       "        icon_img.src = '_images/' + image + '.png';\n",
       "        icon_img.srcset = '_images/' + image + '_large.png 2x';\n",
       "        icon_img.alt = tooltip;\n",
       "        button.appendChild(icon_img);\n",
       "\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    var fmt_picker = document.createElement('select');\n",
       "    fmt_picker.classList = 'mpl-widget';\n",
       "    toolbar.appendChild(fmt_picker);\n",
       "    this.format_dropdown = fmt_picker;\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = document.createElement('option');\n",
       "        option.selected = fmt === mpl.default_extension;\n",
       "        option.innerHTML = fmt;\n",
       "        fmt_picker.appendChild(option);\n",
       "    }\n",
       "\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_message = function (type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function () {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1], msg['forward']);\n",
       "        fig.send_message('refresh', {});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n",
       "    var x0 = msg['x0'] / fig.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n",
       "    var x1 = msg['x1'] / fig.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0,\n",
       "        0,\n",
       "        fig.canvas.width / fig.ratio,\n",
       "        fig.canvas.height / fig.ratio\n",
       "    );\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch (cursor) {\n",
       "        case 0:\n",
       "            cursor = 'pointer';\n",
       "            break;\n",
       "        case 1:\n",
       "            cursor = 'default';\n",
       "            break;\n",
       "        case 2:\n",
       "            cursor = 'crosshair';\n",
       "            break;\n",
       "        case 3:\n",
       "            cursor = 'move';\n",
       "            break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_message = function (fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n",
       "    for (var key in msg) {\n",
       "        if (!(key in fig.buttons)) {\n",
       "            continue;\n",
       "        }\n",
       "        fig.buttons[key].disabled = !msg[key];\n",
       "        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n",
       "    if (msg['mode'] === 'PAN') {\n",
       "        fig.buttons['Pan'].classList.add('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    } else if (msg['mode'] === 'ZOOM') {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.add('active');\n",
       "    } else {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message('ack', {});\n",
       "};\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function (fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = 'image/png';\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src\n",
       "                );\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data\n",
       "            );\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        } else if (\n",
       "            typeof evt.data === 'string' &&\n",
       "            evt.data.slice(0, 21) === 'data:image/png;base64'\n",
       "        ) {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig['handle_' + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\n",
       "                \"No handler for the '\" + msg_type + \"' message type: \",\n",
       "                msg\n",
       "            );\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\n",
       "                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n",
       "                    e,\n",
       "                    e.stack,\n",
       "                    msg\n",
       "                );\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "};\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function (e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e) {\n",
       "        e = window.event;\n",
       "    }\n",
       "    if (e.target) {\n",
       "        targ = e.target;\n",
       "    } else if (e.srcElement) {\n",
       "        targ = e.srcElement;\n",
       "    }\n",
       "    if (targ.nodeType === 3) {\n",
       "        // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "    }\n",
       "\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    var boundingRect = targ.getBoundingClientRect();\n",
       "    var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n",
       "    var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n",
       "\n",
       "    return { x: x, y: y };\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys(original) {\n",
       "    return Object.keys(original).reduce(function (obj, key) {\n",
       "        if (typeof original[key] !== 'object') {\n",
       "            obj[key] = original[key];\n",
       "        }\n",
       "        return obj;\n",
       "    }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function (event, name) {\n",
       "    var canvas_pos = mpl.findpos(event);\n",
       "\n",
       "    if (name === 'button_press') {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * this.ratio;\n",
       "    var y = canvas_pos.y * this.ratio;\n",
       "\n",
       "    this.send_message(name, {\n",
       "        x: x,\n",
       "        y: y,\n",
       "        button: event.button,\n",
       "        step: event.step,\n",
       "        guiEvent: simpleKeys(event),\n",
       "    });\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.key_event = function (event, name) {\n",
       "    // Prevent repeat events\n",
       "    if (name === 'key_press') {\n",
       "        if (event.which === this._key) {\n",
       "            return;\n",
       "        } else {\n",
       "            this._key = event.which;\n",
       "        }\n",
       "    }\n",
       "    if (name === 'key_release') {\n",
       "        this._key = null;\n",
       "    }\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which !== 17) {\n",
       "        value += 'ctrl+';\n",
       "    }\n",
       "    if (event.altKey && event.which !== 18) {\n",
       "        value += 'alt+';\n",
       "    }\n",
       "    if (event.shiftKey && event.which !== 16) {\n",
       "        value += 'shift+';\n",
       "    }\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
       "    if (name === 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message('toolbar_button', { name: name });\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";/* global mpl */\n",
       "\n",
       "var comm_websocket_adapter = function (comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function () {\n",
       "        comm.close();\n",
       "    };\n",
       "    ws.send = function (m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function (msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data']);\n",
       "    });\n",
       "    return ws;\n",
       "};\n",
       "\n",
       "mpl.mpl_figure_comm = function (comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = document.getElementById(id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm);\n",
       "\n",
       "    function ondownload(figure, _format) {\n",
       "        window.open(figure.canvas.toDataURL());\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element;\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error('Failed to find cell for figure', id, fig);\n",
       "        return;\n",
       "    }\n",
       "    fig.cell_info[0].output_area.element.one(\n",
       "        'cleared',\n",
       "        { fig: fig },\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function (fig, msg) {\n",
       "    var width = fig.canvas.width / fig.ratio;\n",
       "    fig.cell_info[0].output_area.element.off(\n",
       "        'cleared',\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable();\n",
       "    fig.parent_element.innerHTML =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "    fig.close_ws(fig, msg);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.close_ws = function (fig, msg) {\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width / this.ratio;\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message('ack', {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () {\n",
       "        fig.push_to_output();\n",
       "    }, 1000);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'btn-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'btn-group';\n",
       "    var button;\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",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'btn-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        button = fig.buttons[name] = document.createElement('button');\n",
       "        button.classList = 'btn btn-default';\n",
       "        button.href = '#';\n",
       "        button.title = name;\n",
       "        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message pull-right';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = document.createElement('div');\n",
       "    buttongrp.classList = 'btn-group inline pull-right';\n",
       "    button = document.createElement('button');\n",
       "    button.classList = 'btn btn-mini btn-primary';\n",
       "    button.href = '#';\n",
       "    button.title = 'Stop Interaction';\n",
       "    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n",
       "    button.addEventListener('click', function (_evt) {\n",
       "        fig.handle_close(fig, {});\n",
       "    });\n",
       "    button.addEventListener(\n",
       "        'mouseover',\n",
       "        on_mouseover_closure('Stop Interaction')\n",
       "    );\n",
       "    buttongrp.appendChild(button);\n",
       "    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n",
       "    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._remove_fig_handler = function (event) {\n",
       "    var fig = event.data.fig;\n",
       "    fig.close_ws(fig, {});\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (el) {\n",
       "    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (el) {\n",
       "    // this is important to make the div 'focusable\n",
       "    el.setAttribute('tabindex', 0);\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    } else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager) {\n",
       "        manager = IPython.keyboard_manager;\n",
       "    }\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which === 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "};\n",
       "\n",
       "mpl.find_output_cell = function (html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i = 0; i < ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code') {\n",
       "            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] === html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "};\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel !== null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target(\n",
       "        'matplotlib',\n",
       "        mpl.mpl_figure_comm\n",
       "    );\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4nOzdd3hUdfr+8QkYikFFxYKwTuig4krUiOULoquua8W1/VhZVt1VYqHo6oSiUUiwo7ACagABFVGUohM6JAQSeofQSwihBxJC+szcvz9iRkYSmHCSnCnv13Wd6zKHM3Me4uM+956Z8zkWAQAAIKhYzC4AAAAANYsACAAAEGQIgAAAAEGGAAgAABBkCIAAAABBhgAIAAAQZAiAAAAAQYYACAAAEGQIgAAAAEGGAAgAABBkCIAAAABBhgAIAAAQZAiAAAAAQYYACAAAEGQIgAAAAEGGAAgAABBkCIAAAABBhgAIAAAQZAiAAAAAQYYACAAAEGQIgAAAAEGGAAgAABBkCIAAAABBhgAIAAAQZAiAAAAAQYYACAAAEGQIgAAAAEGGAAgAABBkCIAAAABBhgAIAAAQZAiAAAAAQYYACAAAEGQIgAAAAEGGAAgAABBkCIAAAABBhgAIAAAQZAiAQJBZunSpHn30Uf3pT39SnTp1dPnll6tjx4567bXXPI7r3LmzOnfu7LHPYrEoJibG/XNiYqIsFosSExOrv3AfsnHjRkVFRaljx446//zzK/wdlP1+KtpefPHFs57rwIEDevnll9WsWTPVq1dPV199tZ577jmlp6ef9bVff/21LBaLVqxYcS5/Tbe1a9fKYrHIZrNVeMy2bdtksVj06quvGjoXgJpBAASCiN1uV61atXTXXXfp+++/V1JSkr7//nu9/vrratKkicexmzZt0qZNmzz2/TEA5uTkaMmSJcrJyamJ8g3Jz8/X//73P3Xp0kWXXHKJateurcaNG+uRRx7RtGnTKvVe48aNU+PGjfW3v/1NDz30UIUBsOz388ftn//8pywWi2bNmnXG8xQWFqpVq1Zq1KiRRowYocTERH3xxRe64oor1KRJE504ceKMr6+qAChJN954oxo3biyHw1Hun/fr108Wi0Vr1641fC4A1Y8ACASRTp06qUWLFiopKTntz5xO51lf/8cA6C+SkpJ01VVXqXHjxnrrrbf0448/avHixZo2bZr69u2rRo0a6Z577tGRI0e8er9Tf1eTJ0+u1FVQl8ul5s2by2q1nvV3PnfuXFksFo0ePdpj/8SJE2WxWDRlypQzvr4qA+DIkSNlsVj066+/nvZnDodDTZo00Y033mj4PABqBgEQCCLXXnutbrnlFq+ONfIR8NKlS/Xggw/qkksuUd26ddW8eXP17t3b45hFixbprrvuUoMGDVS/fn3deuutstvtHseUBZgFCxaoZ8+euvTSS3XJJZeoa9euyszM9OrvMX/+fNWpU0fvvPOOiouLyz0mKytLjzzyiDp06FDpq5mVDYDz58+XxWLRO++8c9Zjk5KSZLFYNHnyZI/9M2bMkMVi0YwZM874+vIC4P79+xUREaGWLVtq27ZtkkqvVL7++usKDw9XaGiorrrqKvXu3VsnT550vy47O1v169fXY489dtp5yuoZMWLEWf9OAHwDARAIIv/+97/d39NaunRphYFIOvcAOGvWLIWGhur666/XuHHjtGDBAo0dO1ZPP/20+5ikpCSFhobqxhtv1A8//KBp06bp3nvvVUhIiCZNmuQ+rizANG/eXK+++qpmz56t0aNH6+KLL1aXLl3O+vfNzs7WZZddpmHDhlV4jNPplNPpVHFxse666y698sorZ33fU1U2AHbr1k21atXy6jt8JSUluvHGG3Xttddq+fLlys3N1apVq3TDDTcoIiLijP/+pNMD4IYNG/SnP/1Jt956q/tqZ15enm644QY1atRIQ4cO1bx58zRs2DBddNFFuuuuu+Ryudzv98wzzyg0NFSHDx/2OM8TTzyhevXq6fjx4179DgCYjwAIBJGjR4/qjjvucN+EEBoaqttuu03vvfeecnNzPY491wDYokULtWjRQgUFBRXW0bFjR11++eUe53Q4HLruuuvUtGlTd+goCzAvvfSSx+s//PBDWSwWHThw4Ix/39jYWN12223unwsLC/Xqq6+qUaNGatCggZ5//nm98cYb6tGjh6TSmzvq169/1u/WnaoyAfD48eOqV6+e7rvvPq/f/8SJE+7vGZZtd955p7Kyss762lMD4Ny5c3XhhRfq8ccf9/h3895776lWrVqnfUz8008/nXaVsezf99ChQ937srKyVLduXf3jH//w+u8EwHwEQCAIrVixQu+//74ef/xxNWrUSBaLReHh4R7fgTuXALh161ZZLBYNGTKkwnOfPHlSISEhp4U6Sfrggw9ksVi0efNmSb8HmD/eLDFr1ixZLBYtXbr0jH/Pm266SV9//bX75759++qyyy7T2LFjNW/ePD399NOqW7euOwBKUrNmzTRnzpwzvu+pKhMAP//883I/0q1IcXGx7r//fv3pT39SfHy8kpOTNX78eLVq1UoRERHKzs4+4+vLfn+vvPKKQkND9dprr3lc0ZOk22+/Xddff71KSko8ttzcXIWEhOjNN990H+tyudSiRQu1b9/evW/48OGyWCyaP3++V38nAL6BAAgEueLiYvXt21cWi0VvvPGGe/+5BMDFixfLYrFowoQJFZ4vIyNDFotFgwcPPu3PvvnmG1ksFi1evFhSxTcxeLv8zIUXXqhVq1ZJKg0vYWFh+vbbb91/7nA41KpVK48A2LFjR3333XdnfN9TVSYAdujQQZdddtlZP7otM2rUqHL//jt37vTqe4Rlv79GjRrpwgsvVFpa2mnHtGzZ8oxL1Tz33HMex8fFxXnU1KFDBzVr1uy0YAnAtxEAASg7O1sWi0X333+/e191XgGsVavWGa8AbtmyRZLxAFi/fn33UjaHDh2SxWI5LQQ9+eSTHgGwadOmmj179hnf91TeBsDVq1fLYrHo9ddf9/q9X3zxRdWuXbvcu4UvvfRSPf7442d8fdnvb/bs2YqMjNRll12mNWvWeBzTsWNHtW/fXitWrCh32717t8fxmZmZql27tqKiotzrA5YX5gH4NgIgEET2799f7v4lS5bIYrHo+eefd+8z8h3Ali1bqrCwsMI6br31Vl155ZXKz89373M6nWrfvn253wE81wDYpk0bTZ06VVLpDRWhoaGn3Tl78803uwPgvHnzdMEFF1TLdwBffvnlcgPombz77rvlftRdFrT79Olzxtef+vs7ceKEOnXqpIYNG2rJkiXuY2JjY3X++edr165dXtf1wAMPqGHDhvrPf/6jWrVqae/evV6/FoBvIAACQaR9+/a6//77NXLkSC1YsEDz5s3Txx9/rMaNG6tBgwZav369+1ijdwHfcMMNGj9+vBITEzV+/Hh169bNfUzZXcC33HKLJk+erOnTp+u+++6r8C7gcw2Ar776qp566in3z08++aSuu+46rVmzRllZWfroo49ksVj09NNPa/LkyWrUqJGGDx9+1t9jXl6eJk+erMmTJ+v11193fxw7efLkcpdmKSgo0MUXX+xxQ8ofJSUlqXbt2nr33Xfd+/bu3auGDRuqSZMmGjVqlBYsWKDRo0erefPmCgsLc18prcgff3/5+fn661//qgYNGmjBggWSSq/IdujQQU2bNtUn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\" width=\"640\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "depth = numpy.linspace(0, 1000, 100)\n",
    "res = numpy.exp(-mu*depth*1e-6)\n",
    "fig, ax = subplots()\n",
    "ax.plot(depth, res, \"-\")\n",
    "ax.set_xlabel(\"Depth (µm)\")\n",
    "ax.set_ylabel(\"Residual signal\")\n",
    "ax.set_title(\"Silicon @ 17.8 keV\")\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is consistent with:\n",
    "http://henke.lbl.gov/optical_constants/filter2.html\n",
    "\n",
    "Now we can model the detector\n",
    "\n",
    "## Modeling of the detector:\n",
    "\n",
    "The detector is seen as a 2D array of voxel. Let vox, voy and voz be the dimention of the detector in the three dimentions.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Detector Pilatus 1M\t PixelSize= 1.720e-04, 1.720e-04 m\n",
      "0.000172 0.000172 0.00045\n"
     ]
    }
   ],
   "source": [
    "detector= pyFAI.detector_factory(calib[\"detector\"])\n",
    "print(detector)\n",
    "\n",
    "vox = detector.pixel2 # this is not a typo\n",
    "voy = detector.pixel1 # x <--> axis 2\n",
    "voz = thickness\n",
    "\n",
    "print(vox, voy, voz)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The intensity grabbed in this voxel is the triple integral of the absorbed signal coming from this pixel or from the neighboring ones.\n",
    "\n",
    "There are 3 ways to perform this intergral:\n",
    "* Volumetric analytic integral. Looks feasible with a change of variable in the depth\n",
    "* Slice per slice, the remaining intensity depand on the incidence angle + pixel splitting between neighbooring pixels\n",
    "* raytracing: the decay can be solved analytically for each ray, one has to throw many ray to average out the signal.\n",
    "\n",
    "For sake of simplicity, this integral will be calculated numerically using this raytracing algorithm.\n",
    "http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&rep=rep1&type=pdf\n",
    "\n",
    "Knowing the input position for a X-ray on the detector and its propagation vector, this algorithm allows us to calculate  the length of the path in all voxel it crosses in a fairly efficient way.\n",
    "\n",
    "To speed up the calculation, we will use a few tricks:\n",
    "* One ray never crosses more than 16 pixels, which is reasonable considering the incidance angle \n",
    "* we use numba to speed-up the calculation of loops in python\n",
    "* We will allocate the needed memory by chuncks of 1 million elements\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numba import jit \n",
    "\n",
    "BLOCK_SIZE = 1<<20 # 1 million\n",
    "BUFFER_SIZE = 16 \n",
    "BIG = numpy.finfo(numpy.float32).max\n",
    "\n",
    "mask = numpy.load(\"mask.npy\").astype(numpy.int8)\n",
    "from scipy.sparse import csr_matrix, csc_matrix, linalg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(array([0, 0, 1, 1], dtype=int32), array([0, 1, 1, 2], dtype=int32), array([0.00029791, 0.00029791, 0.00059583, 0.00059583], dtype=float32))\n",
      "The slowest run took 10.92 times longer than the fastest. This could mean that an intermediate result is being cached.\n",
      "5.25 µs ± 7.05 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "10.8 µs ± 59.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
     ]
    }
   ],
   "source": [
    "@jit\n",
    "def calc_one_ray(entx, enty, \n",
    "                 kx, ky, kz,\n",
    "                 vox, voy, voz):\n",
    "    \"\"\"For a ray, entering at position (entx, enty), with a propagation vector (kx, ky,kz),\n",
    "    calculate the length spent in every voxel where energy is deposited from a bunch of photons comming in the detector \n",
    "    at a given position and and how much energy they deposit in each voxel. \n",
    "    \n",
    "    Direct implementation of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&rep=rep1&type=pdf\n",
    "    \n",
    "    :param entx, enty: coordinate of the entry point in meter (2 components, x,y)\n",
    "    :param kx, ky, kz: vector with the direction of the photon (3 components, x,y,z)\n",
    "    :param vox, voy, voz: size of the voxel in meter (3 components, x,y,z)\n",
    "    :return: coordinates voxels in x, y and length crossed when leaving the associated voxel\n",
    "    \"\"\"\n",
    "    array_x = numpy.empty(BUFFER_SIZE, dtype=numpy.int32)\n",
    "    array_x[:] = -1\n",
    "    array_y = numpy.empty(BUFFER_SIZE, dtype=numpy.int32)\n",
    "    array_y[:] = -1\n",
    "    array_len = numpy.empty(BUFFER_SIZE, dtype=numpy.float32)\n",
    "    \n",
    "    #normalize the input propagation vector\n",
    "    n = numpy.sqrt(kx*kx + ky*ky + kz*kz)\n",
    "    kx /= n\n",
    "    ky /= n\n",
    "    kz /= n\n",
    "    \n",
    "    #assert kz>0\n",
    "    step_X = -1 if kx<0.0 else 1\n",
    "    step_Y = -1 if ky<0.0 else 1\n",
    "    \n",
    "    #assert vox>0\n",
    "    #assert voy>0\n",
    "    #assert voz>0\n",
    "        \n",
    "    X = int(entx//vox)\n",
    "    Y = int(enty//voy)\n",
    "    \n",
    "    if kx>0.0:\n",
    "        t_max_x = ((entx//vox+1)*(vox)-entx)/ kx\n",
    "    elif kx<0.0:\n",
    "        t_max_x = ((entx//vox)*(vox)-entx)/ kx\n",
    "    else:\n",
    "        t_max_x = BIG\n",
    "\n",
    "    if ky>0.0:\n",
    "        t_max_y = ((enty//voy+1)*(voy)-enty)/ ky\n",
    "    elif ky<0.0:\n",
    "        t_max_y = ((enty//voy)*(voy)-enty)/ ky\n",
    "    else:\n",
    "        t_max_y = BIG\n",
    "    \n",
    "    #Only one case for z as the ray is travelling in one direction only\n",
    "    t_max_z = voz / kz\n",
    "       \n",
    "    t_delta_x = abs(vox/kx) if kx!=0 else BIG\n",
    "    t_delta_y = abs(voy/ky) if ky!=0 else BIG\n",
    "    t_delta_z = voz/kz\n",
    "    \n",
    "    finished = False\n",
    "    last_id = 0\n",
    "    array_x[last_id] = X\n",
    "    array_y[last_id] = Y\n",
    "    \n",
    "    while not finished:\n",
    "        if t_max_x < t_max_y:\n",
    "            if t_max_x < t_max_z:\n",
    "                array_len[last_id] = t_max_x\n",
    "                last_id+=1\n",
    "                X += step_X\n",
    "                array_x[last_id] = X\n",
    "                array_y[last_id] = Y\n",
    "                t_max_x += t_delta_x\n",
    "            else:\n",
    "                array_len[last_id] = t_max_z\n",
    "                finished = True\n",
    "        else:\n",
    "            if t_max_y < t_max_z:\n",
    "                array_len[last_id] = t_max_y\n",
    "                last_id+=1\n",
    "                Y += step_Y\n",
    "                array_x[last_id] = X\n",
    "                array_y[last_id] = Y                \n",
    "                t_max_y += t_delta_y\n",
    "            else:\n",
    "                array_len[last_id] = t_max_z\n",
    "                finished = True\n",
    "        if last_id>=array_len.size-1:\n",
    "            print(\"resize arrays\")\n",
    "            old_size = len(array_len)\n",
    "            new_size = (old_size//BUFFER_SIZE+1)*BUFFER_SIZE\n",
    "            new_array_x = numpy.empty(new_size, dtype=numpy.int32)\n",
    "            new_array_x[:] = -1\n",
    "            new_array_y = numpy.empty(new_size, dtype=numpy.int32)\n",
    "            new_array_y[:] = -1\n",
    "            new_array_len = numpy.empty(new_size, dtype=numpy.float32)\n",
    "            new_array_x[:old_size] = array_x\n",
    "            new_array_y[:old_size] = array_y\n",
    "            new_array_len[:old_size] = array_len\n",
    "            array_x = new_array_x\n",
    "            array_y = new_array_y\n",
    "            array_len = new_array_len\n",
    "    return array_x[:last_id], array_y[:last_id], array_len[:last_id]\n",
    "\n",
    "print(calc_one_ray(0.0,0.0, 1,1,1, 172e-6, 172e-6, 450e-6))\n",
    "import random\n",
    "%timeit calc_one_ray(10+random.random(),11+random.random(),\\\n",
    "                     random.random()-0.5,random.random()-0.5,0.5+random.random(), \\\n",
    "                     vox, voy, voz)\n",
    "%timeit calc_one_ray.py_func(10+random.random(),11+random.random(),\\\n",
    "                     random.random()-0.5,random.random()-0.5,0.5+random.random(), \\\n",
    "                     vox, voy, voz)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we are able to perform raytracing for any ray comming in the detector, we can calculate the contribution to the neighboring pixels, using the absorption law (the length travelled is already known). \n",
    "To average-out the signal, we will sample a few dozens of rays per pixel to get an approximatation of the volumic integrale. \n",
    "\n",
    "Now we need to store the results so that this transformation can be represented as a sparse matrix multiplication:\n",
    "\n",
    "b = M.a\n",
    "\n",
    "Where b is the recorded image (blurred) and a is the \"perfect\" signal. \n",
    "M being the sparse matrix where every pixel of a gives a limited number of contribution to b.\n",
    "\n",
    "Each pixel in *b* is represented by one line in *M* and we store the indices of *a* of interest with the coefficients of the matrix.\n",
    "So if a pixel i,j contributes to (i,j), (i+1,j), (i+1,j+1), there are only 3 elements in the line. \n",
    "This is advantagous for storage.\n",
    "\n",
    "We will use the CSR sparse matrix representation:\n",
    "https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29\n",
    "where there are 3 arrays:\n",
    "* data: containing the actual non zero values\n",
    "* indices: for a given line, it contains the column number of the assocated data (at the same indice)\n",
    "* idptr: this array contains the index of the start of every line.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numba.experimental import jitclass\n",
    "from numba import int8, int32, int64, float32, float64\n",
    "spec = [(\"vox\",float64),(\"voy\",float64),(\"voz\",float64),(\"mu\",float64),\n",
    "        (\"dist\",float64),(\"poni1\",float64),(\"poni2\",float64),\n",
    "        (\"width\", int64),(\"height\", int64),(\"mask\", int8[:,:]),\n",
    "        (\"sampled\", int64), (\"data\", float32[:]),(\"indices\", int32[:]),(\"idptr\", int32[:]),\n",
    "       ]\n",
    "@jitclass(spec)\n",
    "class ThickDetector(object):\n",
    "    \"Calculate the point spread function as function of the geometry of the experiment\"\n",
    "    \n",
    "    def __init__(self, vox, voy, thickness, mask, mu, \n",
    "                 dist, poni1, poni2):\n",
    "        \"\"\"Constructor of the class:\n",
    "        \n",
    "        :param vox, voy: detector pixel size in the plane\n",
    "        :param thickness: thickness of the sensor in meters\n",
    "        :param mask: \n",
    "        :param mu: absorption coefficient of the sensor material\n",
    "        :param dist: sample detector distance as defined in the geometry-file\n",
    "        :param poni1, poni2: coordinates of the PONI as defined in the geometry \n",
    "        \"\"\"\n",
    "        self.vox = vox\n",
    "        self.voy = voy\n",
    "        self.voz = thickness\n",
    "        self.mu = mu\n",
    "        self.dist=dist\n",
    "        self.poni1 = poni1\n",
    "        self.poni2 = poni2\n",
    "        self.width = mask.shape[-1]\n",
    "        self.height = mask.shape[0]\n",
    "        self.mask = mask\n",
    "        self.sampled = 0\n",
    "        self.data = numpy.zeros(BLOCK_SIZE, dtype=numpy.float32)\n",
    "        self.indices = numpy.zeros(BLOCK_SIZE,dtype=numpy.int32)\n",
    "        self.idptr = numpy.zeros(self.width*self.height+1, dtype=numpy.int32)\n",
    "        \n",
    "    def calc_one_ray(self, entx, enty):\n",
    "        \"\"\"For a ray, entering at position (entx, enty), with a propagation vector (kx, ky,kz),\n",
    "        calculate the length spent in every voxel where energy is deposited from a bunch of photons comming in the detector \n",
    "        at a given position and and how much energy they deposit in each voxel. \n",
    "\n",
    "        Direct implementation of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&rep=rep1&type=pdf\n",
    "\n",
    "        :param entx, enty: coordinate of the entry point in meter (2 components, x,y)\n",
    "        :return: coordinates voxels in x, y and length crossed when leaving the associated voxel\n",
    "        \"\"\"\n",
    "        array_x = numpy.empty(BUFFER_SIZE, dtype=numpy.int32)\n",
    "        array_x[:] = -1\n",
    "        array_y = numpy.empty(BUFFER_SIZE, dtype=numpy.int32)\n",
    "        array_y[:] = -1\n",
    "        array_len = numpy.empty(BUFFER_SIZE, dtype=numpy.float32)\n",
    "\n",
    "        #normalize the input propagation vector\n",
    "        kx = entx - self.poni2\n",
    "        ky = enty - self.poni1\n",
    "        kz = self.dist\n",
    "        n = numpy.sqrt(kx*kx + ky*ky + kz*kz)\n",
    "        kx /= n\n",
    "        ky /= n\n",
    "        kz /= n\n",
    "\n",
    "        step_X = -1 if kx<0.0 else 1\n",
    "        step_Y = -1 if ky<0.0 else 1\n",
    "\n",
    "        X = int(entx/self.vox)\n",
    "        Y = int(enty/self.voy)\n",
    "\n",
    "        if kx>0.0:\n",
    "            t_max_x = ((entx//self.vox+1)*(self.vox)-entx)/ kx\n",
    "        elif kx<0.0:\n",
    "            t_max_x = ((entx//self.vox)*(self.vox)-entx)/ kx\n",
    "        else:\n",
    "            t_max_x = BIG\n",
    "\n",
    "        if ky>0.0:\n",
    "            t_max_y = ((enty//self.voy+1)*(self.voy)-enty)/ ky\n",
    "        elif ky<0.0:\n",
    "            t_max_y = ((enty//self.voy)*(self.voy)-enty)/ ky\n",
    "        else:\n",
    "            t_max_y = BIG\n",
    "\n",
    "        #Only one case for z as the ray is travelling in one direction only\n",
    "        t_max_z = self.voz / kz\n",
    "\n",
    "        t_delta_x = abs(self.vox/kx) if kx!=0 else BIG\n",
    "        t_delta_y = abs(self.voy/ky) if ky!=0 else BIG\n",
    "        t_delta_z = self.voz/kz\n",
    "\n",
    "        finished = False\n",
    "        last_id = 0\n",
    "        array_x[last_id] = X\n",
    "        array_y[last_id] = Y\n",
    "\n",
    "        while not finished:\n",
    "            if t_max_x < t_max_y:\n",
    "                if t_max_x < t_max_z:\n",
    "                    array_len[last_id] = t_max_x\n",
    "                    last_id+=1\n",
    "                    X += step_X\n",
    "                    array_x[last_id] = X\n",
    "                    array_y[last_id] = Y\n",
    "                    t_max_x += t_delta_x\n",
    "                else:\n",
    "                    array_len[last_id] = t_max_z\n",
    "                    last_id+=1\n",
    "                    finished = True\n",
    "            else:\n",
    "                if t_max_y < t_max_z:\n",
    "                    array_len[last_id] = t_max_y\n",
    "                    last_id+=1\n",
    "                    Y += step_Y\n",
    "                    array_x[last_id] = X\n",
    "                    array_y[last_id] = Y                \n",
    "                    t_max_y += t_delta_y\n",
    "                else:\n",
    "                    array_len[last_id] = t_max_z\n",
    "                    last_id+=1\n",
    "                    finished = True\n",
    "            if last_id>=array_len.size-1:\n",
    "                print(\"resize arrays\")\n",
    "                old_size = len(array_len)\n",
    "                new_size = (old_size//BUFFER_SIZE+1)*BUFFER_SIZE\n",
    "                new_array_x = numpy.empty(new_size, dtype=numpy.int32)\n",
    "                new_array_x[:] = -1\n",
    "                new_array_y = numpy.empty(new_size, dtype=numpy.int32)\n",
    "                new_array_y[:] = -1\n",
    "                new_array_len = numpy.empty(new_size, dtype=numpy.float32)\n",
    "                new_array_x[:old_size] = array_x\n",
    "                new_array_y[:old_size] = array_y\n",
    "                new_array_len[:old_size] = array_len\n",
    "                array_x = new_array_x\n",
    "                array_y = new_array_y\n",
    "                array_len = new_array_len\n",
    "        return array_x[:last_id], array_y[:last_id], array_len[:last_id]\n",
    "\n",
    "    def one_pixel(self, row, col, sample):\n",
    "        \"\"\"calculate the contribution of one pixel to the sparse matrix and populate it.\n",
    "\n",
    "        :param row: row index of the pixel of interest\n",
    "        :param col: column index of the pixel of interest\n",
    "        :param sample: Oversampling rate, 10 will thow 10x10 ray per pixel\n",
    "\n",
    "        :return: the extra number of pixel allocated\n",
    "        \"\"\"\n",
    "        if self.mask[row, col]:\n",
    "            return (numpy.empty(0, dtype=numpy.int32),\n",
    "                    numpy.empty(0, dtype=numpy.float32))\n",
    "\n",
    "        counter = 0\n",
    "        tmp_size = 0\n",
    "        last_buffer_size = BUFFER_SIZE\n",
    "        tmp_idx = numpy.empty(last_buffer_size, dtype=numpy.int32)\n",
    "        tmp_idx[:] = -1\n",
    "        tmp_coef = numpy.zeros(last_buffer_size, dtype=numpy.float32)\n",
    "\n",
    "        pos = row * self.width + col\n",
    "        start = self.idptr[pos]\n",
    "        for i in range(sample):\n",
    "            posx = (col+1.0*i/sample)*vox\n",
    "            for j in range(sample):\n",
    "                posy = (row+1.0*j/sample)*voy\n",
    "                array_x, array_y, array_len = self.calc_one_ray(posx, posy)\n",
    "\n",
    "                rem = 1.0\n",
    "                for i in range(array_x.size):\n",
    "                    x = array_x[i]\n",
    "                    y = array_y[i]\n",
    "                    l = array_len[i]\n",
    "                    if (x<0) or (y<0) or (y>=self.height) or (x>=self.width):\n",
    "                        break\n",
    "                    elif (self.mask[y, x]):\n",
    "                        continue\n",
    "                    idx = x + y*self.width\n",
    "                    dos = numpy.exp(-self.mu*l)\n",
    "                    value = rem - dos\n",
    "                    rem = dos\n",
    "                    for j in range(last_buffer_size):\n",
    "                        if tmp_size >= last_buffer_size:\n",
    "                            #Increase buffer size\n",
    "                            new_buffer_size = last_buffer_size + BUFFER_SIZE\n",
    "                            new_idx = numpy.empty(new_buffer_size, dtype=numpy.int32)\n",
    "                            new_coef = numpy.zeros(new_buffer_size, dtype=numpy.float32)\n",
    "                            new_idx[:last_buffer_size] = tmp_idx\n",
    "                            new_idx[last_buffer_size:] = -1\n",
    "                            new_coef[:last_buffer_size] = tmp_coef\n",
    "                            last_buffer_size = new_buffer_size\n",
    "                            tmp_idx = new_idx\n",
    "                            tmp_coef = new_coef\n",
    "\n",
    "                        if tmp_idx[j] == idx:\n",
    "                            tmp_coef[j] += value\n",
    "                            break\n",
    "                        elif tmp_idx[j] < 0:\n",
    "                            tmp_idx[j] = idx\n",
    "                            tmp_coef[j] = value\n",
    "                            tmp_size +=1\n",
    "                            break     \n",
    "        return tmp_idx[:tmp_size], tmp_coef[:tmp_size]\n",
    "\n",
    "    def calc_csr(self, sample):\n",
    "        \"\"\"Calculate the CSR matrix for the whole image\n",
    "        :param sample: Oversampling factor\n",
    "        :return: CSR matrix\n",
    "        \"\"\"\n",
    "        size = self.width * self.height\n",
    "        allocated_size = BLOCK_SIZE\n",
    "        idptr = numpy.zeros(size+1, dtype=numpy.int32) \n",
    "        indices = numpy.zeros(allocated_size, dtype=numpy.int32)\n",
    "        data = numpy.zeros(allocated_size, dtype=numpy.float32)\n",
    "        self.sampled = sample*sample\n",
    "        pos = 0\n",
    "        start = 0\n",
    "        for row in range(self.height):\n",
    "            for col in range(self.width):    \n",
    "                line_idx, line_coef = self.one_pixel(row, col, sample)\n",
    "                line_size = line_idx.size\n",
    "                if line_size == 0:\n",
    "                    new_size = 0\n",
    "                    pos+=1\n",
    "                    idptr[pos] = start\n",
    "                    continue\n",
    "\n",
    "                stop = start + line_size\n",
    "                \n",
    "                if stop >= allocated_size:\n",
    "                    new_buffer_size = allocated_size +  BLOCK_SIZE\n",
    "                    new_idx = numpy.zeros(new_buffer_size, dtype=numpy.int32)\n",
    "                    new_coef = numpy.zeros(new_buffer_size, dtype=numpy.float32)\n",
    "                    new_idx[:allocated_size] = indices\n",
    "                    new_coef[:allocated_size] = data\n",
    "                    allocated_size = new_buffer_size\n",
    "                    indices = new_idx\n",
    "                    data = new_coef\n",
    "\n",
    "                indices[start:stop] = line_idx\n",
    "                data[start:stop] = line_coef\n",
    "                pos+=1\n",
    "                idptr[pos] = stop\n",
    "                start = stop\n",
    "    \n",
    "        last = idptr[-1]\n",
    "        self.data = data\n",
    "        self.indices = indices\n",
    "        self.idptr = idptr\n",
    "        return (self.data[:last]/self.sampled, indices[:last], idptr)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 2.74 s, sys: 3.98 ms, total: 2.75 s\n",
      "Wall time: 2.75 s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(array([0., 0., 0., ..., 0., 0., 0.], dtype=float32),\n",
       " array([      2,       2,       4, ..., 1023180, 1023181, 1023182],\n",
       "       dtype=int32),\n",
       " array([      0,       0,       0, ..., 1902581, 1902582, 1902583],\n",
       "       dtype=int32))"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "thick = ThickDetector(vox,voy, thickness=thickness, mu=mu, dist=dist, poni1=poni1, poni2=poni2, mask=mask)\n",
    "%time thick.calc_csr(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 25.5 s, sys: 60.4 ms, total: 25.5 s\n",
      "Wall time: 25.4 s\n"
     ]
    }
   ],
   "source": [
    "thick = ThickDetector(vox,voy, thickness=thickness, mu=mu, dist=dist, poni1=poni1, poni2=poni2, mask=mask)\n",
    "%time pre_csr = thick.calc_csr(8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Validation of the CSR matrix obtained:\n",
    "\n",
    "For this we will build a simple 2D image with one pixel in a regular grid and calculate the effect of the transformation calculated previously on it. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "/* global mpl */\n",
       "window.mpl = {};\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(\n",
       "            '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",
       "\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 = document.createElement('div');\n",
       "    this.root.setAttribute('style', 'display: inline-block');\n",
       "    this._root_extra_style(this.root);\n",
       "\n",
       "    parent_element.appendChild(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 (fig.ratio !== 1) {\n",
       "            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.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 = document.createElement('div');\n",
       "    titlebar.classList =\n",
       "        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n",
       "    var titletext = document.createElement('div');\n",
       "    titletext.classList = 'ui-dialog-title';\n",
       "    titletext.setAttribute(\n",
       "        'style',\n",
       "        'width: 100%; text-align: center; padding: 3px;'\n",
       "    );\n",
       "    titlebar.appendChild(titletext);\n",
       "    this.root.appendChild(titlebar);\n",
       "    this.header = titletext;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = (this.canvas_div = document.createElement('div'));\n",
       "    canvas_div.setAttribute(\n",
       "        'style',\n",
       "        'border: 1px solid #ddd;' +\n",
       "            'box-sizing: content-box;' +\n",
       "            'clear: both;' +\n",
       "            'min-height: 1px;' +\n",
       "            'min-width: 1px;' +\n",
       "            'outline: 0;' +\n",
       "            'overflow: hidden;' +\n",
       "            'position: relative;' +\n",
       "            'resize: both;'\n",
       "    );\n",
       "\n",
       "    function on_keyboard_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.key_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    canvas_div.addEventListener(\n",
       "        'keydown',\n",
       "        on_keyboard_event_closure('key_press')\n",
       "    );\n",
       "    canvas_div.addEventListener(\n",
       "        'keyup',\n",
       "        on_keyboard_event_closure('key_release')\n",
       "    );\n",
       "\n",
       "    this._canvas_extra_style(canvas_div);\n",
       "    this.root.appendChild(canvas_div);\n",
       "\n",
       "    var canvas = (this.canvas = document.createElement('canvas'));\n",
       "    canvas.classList.add('mpl-canvas');\n",
       "    canvas.setAttribute('style', 'box-sizing: content-box;');\n",
       "\n",
       "    this.context = canvas.getContext('2d');\n",
       "\n",
       "    var backingStore =\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        this.context.webkitBackingStorePixelRatio ||\n",
       "        this.context.mozBackingStorePixelRatio ||\n",
       "        this.context.msBackingStorePixelRatio ||\n",
       "        this.context.oBackingStorePixelRatio ||\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        1;\n",
       "\n",
       "    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "    if (this.ratio !== 1) {\n",
       "        fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n",
       "    }\n",
       "\n",
       "    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n",
       "        'canvas'\n",
       "    ));\n",
       "    rubberband_canvas.setAttribute(\n",
       "        'style',\n",
       "        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n",
       "    );\n",
       "\n",
       "    var resizeObserver = new ResizeObserver(function (entries) {\n",
       "        var nentries = entries.length;\n",
       "        for (var i = 0; i < nentries; i++) {\n",
       "            var entry = entries[i];\n",
       "            var width, height;\n",
       "            if (entry.contentBoxSize) {\n",
       "                if (entry.contentBoxSize instanceof Array) {\n",
       "                    // Chrome 84 implements new version of spec.\n",
       "                    width = entry.contentBoxSize[0].inlineSize;\n",
       "                    height = entry.contentBoxSize[0].blockSize;\n",
       "                } else {\n",
       "                    // Firefox implements old version of spec.\n",
       "                    width = entry.contentBoxSize.inlineSize;\n",
       "                    height = entry.contentBoxSize.blockSize;\n",
       "                }\n",
       "            } else {\n",
       "                // Chrome <84 implements even older version of spec.\n",
       "                width = entry.contentRect.width;\n",
       "                height = entry.contentRect.height;\n",
       "            }\n",
       "\n",
       "            // Keep the size of the canvas and rubber band canvas in sync with\n",
       "            // the canvas container.\n",
       "            if (entry.devicePixelContentBoxSize) {\n",
       "                // Chrome 84 implements new version of spec.\n",
       "                canvas.setAttribute(\n",
       "                    'width',\n",
       "                    entry.devicePixelContentBoxSize[0].inlineSize\n",
       "                );\n",
       "                canvas.setAttribute(\n",
       "                    'height',\n",
       "                    entry.devicePixelContentBoxSize[0].blockSize\n",
       "                );\n",
       "            } else {\n",
       "                canvas.setAttribute('width', width * fig.ratio);\n",
       "                canvas.setAttribute('height', height * fig.ratio);\n",
       "            }\n",
       "            canvas.setAttribute(\n",
       "                'style',\n",
       "                'width: ' + width + 'px; height: ' + height + 'px;'\n",
       "            );\n",
       "\n",
       "            rubberband_canvas.setAttribute('width', width);\n",
       "            rubberband_canvas.setAttribute('height', height);\n",
       "\n",
       "            // And update the size in Python. We ignore the initial 0/0 size\n",
       "            // that occurs as the element is placed into the DOM, which should\n",
       "            // otherwise not happen due to the minimum size styling.\n",
       "            if (width != 0 && height != 0) {\n",
       "                fig.request_resize(width, height);\n",
       "            }\n",
       "        }\n",
       "    });\n",
       "    resizeObserver.observe(canvas_div);\n",
       "\n",
       "    function on_mouse_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.mouse_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousedown',\n",
       "        on_mouse_event_closure('button_press')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseup',\n",
       "        on_mouse_event_closure('button_release')\n",
       "    );\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousemove',\n",
       "        on_mouse_event_closure('motion_notify')\n",
       "    );\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseenter',\n",
       "        on_mouse_event_closure('figure_enter')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseleave',\n",
       "        on_mouse_event_closure('figure_leave')\n",
       "    );\n",
       "\n",
       "    canvas_div.addEventListener('wheel', function (event) {\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        on_mouse_event_closure('scroll')(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.appendChild(canvas);\n",
       "    canvas_div.appendChild(rubberband_canvas);\n",
       "\n",
       "    this.rubberband_context = rubberband_canvas.getContext('2d');\n",
       "    this.rubberband_context.strokeStyle = '#000000';\n",
       "\n",
       "    this._resize_canvas = function (width, height, forward) {\n",
       "        if (forward) {\n",
       "            canvas_div.style.width = width + 'px';\n",
       "            canvas_div.style.height = height + 'px';\n",
       "        }\n",
       "    };\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n",
       "        event.preventDefault();\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 toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'mpl-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'mpl-button-group';\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",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'mpl-button-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        var button = (fig.buttons[name] = document.createElement('button'));\n",
       "        button.classList = 'mpl-widget';\n",
       "        button.setAttribute('role', 'button');\n",
       "        button.setAttribute('aria-disabled', 'false');\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "\n",
       "        var icon_img = document.createElement('img');\n",
       "        icon_img.src = '_images/' + image + '.png';\n",
       "        icon_img.srcset = '_images/' + image + '_large.png 2x';\n",
       "        icon_img.alt = tooltip;\n",
       "        button.appendChild(icon_img);\n",
       "\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    var fmt_picker = document.createElement('select');\n",
       "    fmt_picker.classList = 'mpl-widget';\n",
       "    toolbar.appendChild(fmt_picker);\n",
       "    this.format_dropdown = fmt_picker;\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = document.createElement('option');\n",
       "        option.selected = fmt === mpl.default_extension;\n",
       "        option.innerHTML = fmt;\n",
       "        fmt_picker.appendChild(option);\n",
       "    }\n",
       "\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_message = function (type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function () {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1], msg['forward']);\n",
       "        fig.send_message('refresh', {});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n",
       "    var x0 = msg['x0'] / fig.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n",
       "    var x1 = msg['x1'] / fig.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0,\n",
       "        0,\n",
       "        fig.canvas.width / fig.ratio,\n",
       "        fig.canvas.height / fig.ratio\n",
       "    );\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch (cursor) {\n",
       "        case 0:\n",
       "            cursor = 'pointer';\n",
       "            break;\n",
       "        case 1:\n",
       "            cursor = 'default';\n",
       "            break;\n",
       "        case 2:\n",
       "            cursor = 'crosshair';\n",
       "            break;\n",
       "        case 3:\n",
       "            cursor = 'move';\n",
       "            break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_message = function (fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n",
       "    for (var key in msg) {\n",
       "        if (!(key in fig.buttons)) {\n",
       "            continue;\n",
       "        }\n",
       "        fig.buttons[key].disabled = !msg[key];\n",
       "        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n",
       "    if (msg['mode'] === 'PAN') {\n",
       "        fig.buttons['Pan'].classList.add('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    } else if (msg['mode'] === 'ZOOM') {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.add('active');\n",
       "    } else {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message('ack', {});\n",
       "};\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function (fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = 'image/png';\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src\n",
       "                );\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data\n",
       "            );\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        } else if (\n",
       "            typeof evt.data === 'string' &&\n",
       "            evt.data.slice(0, 21) === 'data:image/png;base64'\n",
       "        ) {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig['handle_' + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\n",
       "                \"No handler for the '\" + msg_type + \"' message type: \",\n",
       "                msg\n",
       "            );\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\n",
       "                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n",
       "                    e,\n",
       "                    e.stack,\n",
       "                    msg\n",
       "                );\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "};\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function (e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e) {\n",
       "        e = window.event;\n",
       "    }\n",
       "    if (e.target) {\n",
       "        targ = e.target;\n",
       "    } else if (e.srcElement) {\n",
       "        targ = e.srcElement;\n",
       "    }\n",
       "    if (targ.nodeType === 3) {\n",
       "        // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "    }\n",
       "\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    var boundingRect = targ.getBoundingClientRect();\n",
       "    var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n",
       "    var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n",
       "\n",
       "    return { x: x, y: y };\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys(original) {\n",
       "    return Object.keys(original).reduce(function (obj, key) {\n",
       "        if (typeof original[key] !== 'object') {\n",
       "            obj[key] = original[key];\n",
       "        }\n",
       "        return obj;\n",
       "    }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function (event, name) {\n",
       "    var canvas_pos = mpl.findpos(event);\n",
       "\n",
       "    if (name === 'button_press') {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * this.ratio;\n",
       "    var y = canvas_pos.y * this.ratio;\n",
       "\n",
       "    this.send_message(name, {\n",
       "        x: x,\n",
       "        y: y,\n",
       "        button: event.button,\n",
       "        step: event.step,\n",
       "        guiEvent: simpleKeys(event),\n",
       "    });\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.key_event = function (event, name) {\n",
       "    // Prevent repeat events\n",
       "    if (name === 'key_press') {\n",
       "        if (event.which === this._key) {\n",
       "            return;\n",
       "        } else {\n",
       "            this._key = event.which;\n",
       "        }\n",
       "    }\n",
       "    if (name === 'key_release') {\n",
       "        this._key = null;\n",
       "    }\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which !== 17) {\n",
       "        value += 'ctrl+';\n",
       "    }\n",
       "    if (event.altKey && event.which !== 18) {\n",
       "        value += 'alt+';\n",
       "    }\n",
       "    if (event.shiftKey && event.which !== 16) {\n",
       "        value += 'shift+';\n",
       "    }\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
       "    if (name === 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message('toolbar_button', { name: name });\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";/* global mpl */\n",
       "\n",
       "var comm_websocket_adapter = function (comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function () {\n",
       "        comm.close();\n",
       "    };\n",
       "    ws.send = function (m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function (msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data']);\n",
       "    });\n",
       "    return ws;\n",
       "};\n",
       "\n",
       "mpl.mpl_figure_comm = function (comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = document.getElementById(id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm);\n",
       "\n",
       "    function ondownload(figure, _format) {\n",
       "        window.open(figure.canvas.toDataURL());\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element;\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error('Failed to find cell for figure', id, fig);\n",
       "        return;\n",
       "    }\n",
       "    fig.cell_info[0].output_area.element.one(\n",
       "        'cleared',\n",
       "        { fig: fig },\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function (fig, msg) {\n",
       "    var width = fig.canvas.width / fig.ratio;\n",
       "    fig.cell_info[0].output_area.element.off(\n",
       "        'cleared',\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable();\n",
       "    fig.parent_element.innerHTML =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "    fig.close_ws(fig, msg);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.close_ws = function (fig, msg) {\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width / this.ratio;\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message('ack', {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () {\n",
       "        fig.push_to_output();\n",
       "    }, 1000);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'btn-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'btn-group';\n",
       "    var button;\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",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'btn-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        button = fig.buttons[name] = document.createElement('button');\n",
       "        button.classList = 'btn btn-default';\n",
       "        button.href = '#';\n",
       "        button.title = name;\n",
       "        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message pull-right';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = document.createElement('div');\n",
       "    buttongrp.classList = 'btn-group inline pull-right';\n",
       "    button = document.createElement('button');\n",
       "    button.classList = 'btn btn-mini btn-primary';\n",
       "    button.href = '#';\n",
       "    button.title = 'Stop Interaction';\n",
       "    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n",
       "    button.addEventListener('click', function (_evt) {\n",
       "        fig.handle_close(fig, {});\n",
       "    });\n",
       "    button.addEventListener(\n",
       "        'mouseover',\n",
       "        on_mouseover_closure('Stop Interaction')\n",
       "    );\n",
       "    buttongrp.appendChild(button);\n",
       "    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n",
       "    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._remove_fig_handler = function (event) {\n",
       "    var fig = event.data.fig;\n",
       "    fig.close_ws(fig, {});\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (el) {\n",
       "    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (el) {\n",
       "    // this is important to make the div 'focusable\n",
       "    el.setAttribute('tabindex', 0);\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    } else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager) {\n",
       "        manager = IPython.keyboard_manager;\n",
       "    }\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which === 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "};\n",
       "\n",
       "mpl.find_output_cell = function (html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i = 0; i < ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code') {\n",
       "            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] === html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "};\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel !== null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target(\n",
       "        'matplotlib',\n",
       "        mpl.mpl_figure_comm\n",
       "    );\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,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\" width=\"800\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "dummy_image = numpy.ones(mask.shape, dtype=\"float32\")\n",
    "dummy_image[::5,::5] = 10\n",
    "#dummy_image[mask] = -1\n",
    "csr = csr_matrix(pre_csr)\n",
    "dummy_blurred = csr.T.dot(dummy_image.ravel()).reshape(mask.shape)\n",
    "fix, ax = subplots(2,2, figsize=(8,8))\n",
    "ax[0,0].imshow(dummy_image)\n",
    "ax[0,1].imshow(dummy_blurred)\n",
    "ax[1,1].imshow(csr.dot(dummy_blurred.ravel()).reshape(mask.shape))\n",
    "pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "ax[0,0].set_xlim(964,981)\n",
    "ax[0,0].set_ylim(0,16)\n",
    "ax[0,1].set_xlim(964,981)\n",
    "ax[0,1].set_ylim(0,16)\n",
    "ax[1,1].set_xlim(964,981)\n",
    "ax[1,1].set_ylim(0,16)\n",
    "pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Least squares refinement of the pseudo-inverse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 525 ms, sys: 8.02 ms, total: 533 ms\n",
      "Wall time: 532 ms\n",
      "(1, 30, 0.004615145479236351, 0.0005086039822656203, 2.1357008830168027, 4.83377332078211, 2175.569201750044)\n"
     ]
    }
   ],
   "source": [
    "blured = dummy_blurred.ravel()\n",
    "\n",
    "# Invert this matrix: see https://arxiv.org/abs/1006.0758\n",
    "\n",
    "%time res = linalg.lsmr(csr.T, blured)\n",
    "\n",
    "restored = res[0].reshape(mask.shape)\n",
    "ax[1,0].imshow(restored)\n",
    "ax[1,0].set_xlim(964,981)\n",
    "ax[1,0].set_ylim(0,16)\n",
    "\n",
    "print(res[1:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pseudo inverse with positivitiy constrain and poissonian noise (MLEM)\n",
    "\n",
    "The MLEM algorithm was initially developed within the framework of reconstruction of\n",
    "images in emission tomography [Shepp and Vardi, 1982], [Vardi et al., 1985], [Lange and\n",
    "Carson, 1984]. Nowadays, this algorithm is employed in numerous tomographic reconstruction\n",
    "problems and often associated to regularization techniques. It is based on the iterative\n",
    "maximization of the log-likelihood function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "/* global mpl */\n",
       "window.mpl = {};\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(\n",
       "            '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",
       "\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 = document.createElement('div');\n",
       "    this.root.setAttribute('style', 'display: inline-block');\n",
       "    this._root_extra_style(this.root);\n",
       "\n",
       "    parent_element.appendChild(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 (fig.ratio !== 1) {\n",
       "            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.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 = document.createElement('div');\n",
       "    titlebar.classList =\n",
       "        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n",
       "    var titletext = document.createElement('div');\n",
       "    titletext.classList = 'ui-dialog-title';\n",
       "    titletext.setAttribute(\n",
       "        'style',\n",
       "        'width: 100%; text-align: center; padding: 3px;'\n",
       "    );\n",
       "    titlebar.appendChild(titletext);\n",
       "    this.root.appendChild(titlebar);\n",
       "    this.header = titletext;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = (this.canvas_div = document.createElement('div'));\n",
       "    canvas_div.setAttribute(\n",
       "        'style',\n",
       "        'border: 1px solid #ddd;' +\n",
       "            'box-sizing: content-box;' +\n",
       "            'clear: both;' +\n",
       "            'min-height: 1px;' +\n",
       "            'min-width: 1px;' +\n",
       "            'outline: 0;' +\n",
       "            'overflow: hidden;' +\n",
       "            'position: relative;' +\n",
       "            'resize: both;'\n",
       "    );\n",
       "\n",
       "    function on_keyboard_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.key_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    canvas_div.addEventListener(\n",
       "        'keydown',\n",
       "        on_keyboard_event_closure('key_press')\n",
       "    );\n",
       "    canvas_div.addEventListener(\n",
       "        'keyup',\n",
       "        on_keyboard_event_closure('key_release')\n",
       "    );\n",
       "\n",
       "    this._canvas_extra_style(canvas_div);\n",
       "    this.root.appendChild(canvas_div);\n",
       "\n",
       "    var canvas = (this.canvas = document.createElement('canvas'));\n",
       "    canvas.classList.add('mpl-canvas');\n",
       "    canvas.setAttribute('style', 'box-sizing: content-box;');\n",
       "\n",
       "    this.context = canvas.getContext('2d');\n",
       "\n",
       "    var backingStore =\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        this.context.webkitBackingStorePixelRatio ||\n",
       "        this.context.mozBackingStorePixelRatio ||\n",
       "        this.context.msBackingStorePixelRatio ||\n",
       "        this.context.oBackingStorePixelRatio ||\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        1;\n",
       "\n",
       "    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "    if (this.ratio !== 1) {\n",
       "        fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n",
       "    }\n",
       "\n",
       "    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n",
       "        'canvas'\n",
       "    ));\n",
       "    rubberband_canvas.setAttribute(\n",
       "        'style',\n",
       "        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n",
       "    );\n",
       "\n",
       "    var resizeObserver = new ResizeObserver(function (entries) {\n",
       "        var nentries = entries.length;\n",
       "        for (var i = 0; i < nentries; i++) {\n",
       "            var entry = entries[i];\n",
       "            var width, height;\n",
       "            if (entry.contentBoxSize) {\n",
       "                if (entry.contentBoxSize instanceof Array) {\n",
       "                    // Chrome 84 implements new version of spec.\n",
       "                    width = entry.contentBoxSize[0].inlineSize;\n",
       "                    height = entry.contentBoxSize[0].blockSize;\n",
       "                } else {\n",
       "                    // Firefox implements old version of spec.\n",
       "                    width = entry.contentBoxSize.inlineSize;\n",
       "                    height = entry.contentBoxSize.blockSize;\n",
       "                }\n",
       "            } else {\n",
       "                // Chrome <84 implements even older version of spec.\n",
       "                width = entry.contentRect.width;\n",
       "                height = entry.contentRect.height;\n",
       "            }\n",
       "\n",
       "            // Keep the size of the canvas and rubber band canvas in sync with\n",
       "            // the canvas container.\n",
       "            if (entry.devicePixelContentBoxSize) {\n",
       "                // Chrome 84 implements new version of spec.\n",
       "                canvas.setAttribute(\n",
       "                    'width',\n",
       "                    entry.devicePixelContentBoxSize[0].inlineSize\n",
       "                );\n",
       "                canvas.setAttribute(\n",
       "                    'height',\n",
       "                    entry.devicePixelContentBoxSize[0].blockSize\n",
       "                );\n",
       "            } else {\n",
       "                canvas.setAttribute('width', width * fig.ratio);\n",
       "                canvas.setAttribute('height', height * fig.ratio);\n",
       "            }\n",
       "            canvas.setAttribute(\n",
       "                'style',\n",
       "                'width: ' + width + 'px; height: ' + height + 'px;'\n",
       "            );\n",
       "\n",
       "            rubberband_canvas.setAttribute('width', width);\n",
       "            rubberband_canvas.setAttribute('height', height);\n",
       "\n",
       "            // And update the size in Python. We ignore the initial 0/0 size\n",
       "            // that occurs as the element is placed into the DOM, which should\n",
       "            // otherwise not happen due to the minimum size styling.\n",
       "            if (width != 0 && height != 0) {\n",
       "                fig.request_resize(width, height);\n",
       "            }\n",
       "        }\n",
       "    });\n",
       "    resizeObserver.observe(canvas_div);\n",
       "\n",
       "    function on_mouse_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.mouse_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousedown',\n",
       "        on_mouse_event_closure('button_press')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseup',\n",
       "        on_mouse_event_closure('button_release')\n",
       "    );\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousemove',\n",
       "        on_mouse_event_closure('motion_notify')\n",
       "    );\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseenter',\n",
       "        on_mouse_event_closure('figure_enter')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseleave',\n",
       "        on_mouse_event_closure('figure_leave')\n",
       "    );\n",
       "\n",
       "    canvas_div.addEventListener('wheel', function (event) {\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        on_mouse_event_closure('scroll')(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.appendChild(canvas);\n",
       "    canvas_div.appendChild(rubberband_canvas);\n",
       "\n",
       "    this.rubberband_context = rubberband_canvas.getContext('2d');\n",
       "    this.rubberband_context.strokeStyle = '#000000';\n",
       "\n",
       "    this._resize_canvas = function (width, height, forward) {\n",
       "        if (forward) {\n",
       "            canvas_div.style.width = width + 'px';\n",
       "            canvas_div.style.height = height + 'px';\n",
       "        }\n",
       "    };\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n",
       "        event.preventDefault();\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 toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'mpl-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'mpl-button-group';\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",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'mpl-button-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        var button = (fig.buttons[name] = document.createElement('button'));\n",
       "        button.classList = 'mpl-widget';\n",
       "        button.setAttribute('role', 'button');\n",
       "        button.setAttribute('aria-disabled', 'false');\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "\n",
       "        var icon_img = document.createElement('img');\n",
       "        icon_img.src = '_images/' + image + '.png';\n",
       "        icon_img.srcset = '_images/' + image + '_large.png 2x';\n",
       "        icon_img.alt = tooltip;\n",
       "        button.appendChild(icon_img);\n",
       "\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    var fmt_picker = document.createElement('select');\n",
       "    fmt_picker.classList = 'mpl-widget';\n",
       "    toolbar.appendChild(fmt_picker);\n",
       "    this.format_dropdown = fmt_picker;\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = document.createElement('option');\n",
       "        option.selected = fmt === mpl.default_extension;\n",
       "        option.innerHTML = fmt;\n",
       "        fmt_picker.appendChild(option);\n",
       "    }\n",
       "\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_message = function (type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function () {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1], msg['forward']);\n",
       "        fig.send_message('refresh', {});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n",
       "    var x0 = msg['x0'] / fig.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n",
       "    var x1 = msg['x1'] / fig.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0,\n",
       "        0,\n",
       "        fig.canvas.width / fig.ratio,\n",
       "        fig.canvas.height / fig.ratio\n",
       "    );\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch (cursor) {\n",
       "        case 0:\n",
       "            cursor = 'pointer';\n",
       "            break;\n",
       "        case 1:\n",
       "            cursor = 'default';\n",
       "            break;\n",
       "        case 2:\n",
       "            cursor = 'crosshair';\n",
       "            break;\n",
       "        case 3:\n",
       "            cursor = 'move';\n",
       "            break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_message = function (fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n",
       "    for (var key in msg) {\n",
       "        if (!(key in fig.buttons)) {\n",
       "            continue;\n",
       "        }\n",
       "        fig.buttons[key].disabled = !msg[key];\n",
       "        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n",
       "    if (msg['mode'] === 'PAN') {\n",
       "        fig.buttons['Pan'].classList.add('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    } else if (msg['mode'] === 'ZOOM') {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.add('active');\n",
       "    } else {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message('ack', {});\n",
       "};\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function (fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = 'image/png';\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src\n",
       "                );\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data\n",
       "            );\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        } else if (\n",
       "            typeof evt.data === 'string' &&\n",
       "            evt.data.slice(0, 21) === 'data:image/png;base64'\n",
       "        ) {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig['handle_' + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\n",
       "                \"No handler for the '\" + msg_type + \"' message type: \",\n",
       "                msg\n",
       "            );\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\n",
       "                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n",
       "                    e,\n",
       "                    e.stack,\n",
       "                    msg\n",
       "                );\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "};\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function (e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e) {\n",
       "        e = window.event;\n",
       "    }\n",
       "    if (e.target) {\n",
       "        targ = e.target;\n",
       "    } else if (e.srcElement) {\n",
       "        targ = e.srcElement;\n",
       "    }\n",
       "    if (targ.nodeType === 3) {\n",
       "        // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "    }\n",
       "\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    var boundingRect = targ.getBoundingClientRect();\n",
       "    var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n",
       "    var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n",
       "\n",
       "    return { x: x, y: y };\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys(original) {\n",
       "    return Object.keys(original).reduce(function (obj, key) {\n",
       "        if (typeof original[key] !== 'object') {\n",
       "            obj[key] = original[key];\n",
       "        }\n",
       "        return obj;\n",
       "    }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function (event, name) {\n",
       "    var canvas_pos = mpl.findpos(event);\n",
       "\n",
       "    if (name === 'button_press') {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * this.ratio;\n",
       "    var y = canvas_pos.y * this.ratio;\n",
       "\n",
       "    this.send_message(name, {\n",
       "        x: x,\n",
       "        y: y,\n",
       "        button: event.button,\n",
       "        step: event.step,\n",
       "        guiEvent: simpleKeys(event),\n",
       "    });\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.key_event = function (event, name) {\n",
       "    // Prevent repeat events\n",
       "    if (name === 'key_press') {\n",
       "        if (event.which === this._key) {\n",
       "            return;\n",
       "        } else {\n",
       "            this._key = event.which;\n",
       "        }\n",
       "    }\n",
       "    if (name === 'key_release') {\n",
       "        this._key = null;\n",
       "    }\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which !== 17) {\n",
       "        value += 'ctrl+';\n",
       "    }\n",
       "    if (event.altKey && event.which !== 18) {\n",
       "        value += 'alt+';\n",
       "    }\n",
       "    if (event.shiftKey && event.which !== 16) {\n",
       "        value += 'shift+';\n",
       "    }\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
       "    if (name === 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message('toolbar_button', { name: name });\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";/* global mpl */\n",
       "\n",
       "var comm_websocket_adapter = function (comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function () {\n",
       "        comm.close();\n",
       "    };\n",
       "    ws.send = function (m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function (msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data']);\n",
       "    });\n",
       "    return ws;\n",
       "};\n",
       "\n",
       "mpl.mpl_figure_comm = function (comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = document.getElementById(id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm);\n",
       "\n",
       "    function ondownload(figure, _format) {\n",
       "        window.open(figure.canvas.toDataURL());\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element;\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error('Failed to find cell for figure', id, fig);\n",
       "        return;\n",
       "    }\n",
       "    fig.cell_info[0].output_area.element.one(\n",
       "        'cleared',\n",
       "        { fig: fig },\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function (fig, msg) {\n",
       "    var width = fig.canvas.width / fig.ratio;\n",
       "    fig.cell_info[0].output_area.element.off(\n",
       "        'cleared',\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable();\n",
       "    fig.parent_element.innerHTML =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "    fig.close_ws(fig, msg);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.close_ws = function (fig, msg) {\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width / this.ratio;\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message('ack', {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () {\n",
       "        fig.push_to_output();\n",
       "    }, 1000);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'btn-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'btn-group';\n",
       "    var button;\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",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'btn-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        button = fig.buttons[name] = document.createElement('button');\n",
       "        button.classList = 'btn btn-default';\n",
       "        button.href = '#';\n",
       "        button.title = name;\n",
       "        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message pull-right';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = document.createElement('div');\n",
       "    buttongrp.classList = 'btn-group inline pull-right';\n",
       "    button = document.createElement('button');\n",
       "    button.classList = 'btn btn-mini btn-primary';\n",
       "    button.href = '#';\n",
       "    button.title = 'Stop Interaction';\n",
       "    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n",
       "    button.addEventListener('click', function (_evt) {\n",
       "        fig.handle_close(fig, {});\n",
       "    });\n",
       "    button.addEventListener(\n",
       "        'mouseover',\n",
       "        on_mouseover_closure('Stop Interaction')\n",
       "    );\n",
       "    buttongrp.appendChild(button);\n",
       "    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n",
       "    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._remove_fig_handler = function (event) {\n",
       "    var fig = event.data.fig;\n",
       "    fig.close_ws(fig, {});\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (el) {\n",
       "    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (el) {\n",
       "    // this is important to make the div 'focusable\n",
       "    el.setAttribute('tabindex', 0);\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    } else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager) {\n",
       "        manager = IPython.keyboard_manager;\n",
       "    }\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which === 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "};\n",
       "\n",
       "mpl.find_output_cell = function (html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i = 0; i < ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code') {\n",
       "            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] === html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "};\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel !== null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target(\n",
       "        'matplotlib',\n",
       "        mpl.mpl_figure_comm\n",
       "    );\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAMgCAYAAADbcAZoAAAgAElEQVR4nOy9eXyU5b3+/8TsAUUjW9iCQgBxQbRlsWoqovEoKKfaoi9FrAvuWz2g/WKNVg1FLbZaFfFY6lblKFAFEQWNkVaOYgGLWlu1FCNibatEXIIs1+8PfzOHycZN5s4z933n/X69rj94Mpl5novk+nyuTGYSCQAAAAAAICaiTJ8AAAAAAAC0HyggAAAAAAAQGxQQAAAAAACIDQoIAAAAAADEBgUEAAAAAABigwICAAAAAACxQQEBAAAAAIDYoIAAAAAAAEBsUEAAAAAAACA2KCAAAAAAABAbFBAAAAAAAIgNCggAAAAAAMQGBQQAAAAAAGKDAgIAAAAAALFBAQEAAAAAgNiggAAAAAAAQGxQQAAAAAAAIDYoIAAAAAAAEBsUEAAAAAAAiA0KCAAAAAAAxAYFBAAAAAAAYoMCAgAAAAAAsUEBAQAAAACA2KCAAAAAAABAbFBAAAAAAAAgNiggAAAAAAAQGxQQAAAAAACIDQoIAAAAAADEBgUEAAAAAABigwICAAAAAACxQQEBAAAAAIDYoIAAAAAAAEBsUEAAAAAAACA2KCAAAAAAABAbFBAAAAAAAIgNCggAAAAAAMQGBQQAAAAAAGKDAgIAAAAAALFBAQEAAAAAgNiggAAAAAAAQGxQQAAAAAAAIDYoIAAAAAAAEBsUEAAAAAAAiA0KCAAAAAAAxAYFBAAAAAAAYoMCAgAAAAAAsUEBAQAAAACA2KCAAAAAAABAbFBAAAAAAAAgNiggAAAAAAAQGxQQAAAAAACIDQoIAAAAAADEBgUEAAAAAABigwICAAAAAACxQQEBAAAAAIDYoIAAAAAAAEBsUEAAAAAAACA2KCAAAAAAABAbFBAAAAAAAIgNCggAAAAAAMQGBQQAAAAAAGKDAgIAAAAAALFBAQEAAAAAgNiggAAAAAAAQGxQQAAAAAAAIDYoIAAAAAAAEBsUEAAAAAAAiA0KCAAAAAAAxAYFBAAAAAAAYoMCAgAAAAAAsUEBAQAAAACA2KCAAAAAAABAbFBAAAAAAAAgNiggAAAAAAAQGxQQAAAAAACIDQoIAAAAAADEBgUEAAAAAABigwICAAAAAACxQQEBAAAAAIDYoIAAAAAAAEBsUEAAAAAAACA2KCAAAAAAABAbFBAIitmzZyuKoqTy8/PVrVs3ffe731VVVZX+8Y9/ZPoUM0bCm7Vr12b6VACc5/XXX9dZZ52lvn37Kj8/Xx06dNDQoUM1ffp0/fvf/8706Rmzdu1aRVGk2bNnx/7YpplTWVmpKGof68gDDzygzp0767PPPkseKy0t1cSJE2M/l+rqakVRpMcff3ynt/Xx/yiKIlVWVib//d///d/q0aOHPv/888ydFCTx66sJYCckBt7s2bO1fPlyvfTSS3riiSd0xRVXqFOnTiouLtaSJUsyfZoZ4eOPP9by5ctVX1+f6VMBcJpZs2YpJydH+++/v+666y5VV1frueeeU1VVlfbZZx+NGzcu06dojA8FpLa2VsuXL4/npDLIF198oZ49e+rWW29NOb5y5Uq9++67sZ9PeysgW7ZsUVlZma677rrMnRQk8eurCWAnJAbeihUrGn1s3bp16t27t3bffXd99NFHGTg7AHCdl19+WdnZ2TruuOOaLOubN2/Wk08+mYEzax0+FJD2wt13362CggJ9+umnmT4VSZktIF9++aW2b9/e5Me++OILK4/RsIBI0m233aZOnTpZewxoPRQQCIqWCogk/c///I+iKNINN9yQPFZeXq7y8vJGt504caJKS0uT/04M8ltuuUU/+9nPVFpaqoKCApWXl+svf/mLvv76a1199dUqKSnRHnvsoXHjxjX6la/S0lKdcMIJWrBggQ4++GAVFBRo0KBBWrBgQfL8Bw0apKKiIn37299OuY4HH3xQURTp5ZdfbnSuN9xwg3JycrR+/fqderPjMlBeXq79999fL7/8skaOHKmCggKVlpbq17/+tSRp4cKFGjp0qAoLC3XAAQfomWeeSbnPd955R2eddZb69++vwsJC9ejRQ2PGjNGf/vSnRo//xhtv6JhjjlFhYaE6d+6siy66SAsXLlQURaqurk657ZIlSzRq1CjtvvvuKiws1GGHHaalS5c2e20AthgzZoxycnL0/vvvG91+27Ztmj59ugYOHKi8vDx16dJFEyZMUG1tbcrtEt9rr776qg4//HAVFhZqn3320bRp07Rt2zZJ3zxLmZubq2uvvbbR4/z5z39WFEX65S9/mTy2Zs0anXjiidpzzz2Vn5+vIUOG6De/+U3K5zUsIPPnz1cURU1+P919992Kokivv/568tiKFSs0duxY7bXXXsrPz9fBBx+sOXPmNPrc5cuX67DDDlN+fr5KSkp0zTXXaNasWa3+Fax0sjJx3uPHj0/mdGlpqU499VT9/e9/b/T4y5Yt04gRI5Sfn68ePXro2muv1X333dfkuT/22GMaMWKEioqK1KFDBx177LFauXJli9eX4MADD9T3v//9RsdNfwVr+/btuuuuuzRkyBAVFBRozz331Mknn6z33nvP6PEbkiggDz30kK688kp169ZNBQUFOvLIIxtdU1P/R00t+E1dT2L2PPvss/rhD3+ozp07K4oiffXVV8nvi5qaGo0cOVKFhYUaP368JKmurk5XXXWV+vbtq9zcXPXo0UOXX355o1+hqqur07nnnqvi4mJ16NBBFRUV+stf/tLk+W3YsEFZWVm6//77W+UZ2IMCAkGxswLy+eefKzs7W0cffXTy2K4WkNLSUo0dO1YLFy7Uww8/rG7dumnAgAGaMGGCzj77bD3zzDOaOXOmOnbsqLFjx6bcZ2lpqXr16qUDDjhAjz76qBYtWqThw4crNzdX1113nb7zne9o3rx5mj9/vgYMGKBu3brpyy+/lPTNT167d++u008/PeU+t2zZoh49ejQ52JrypmEB2XvvvTVw4EDdf//9evbZZzVmzJhkSTvwwAOT55kY0DuWnJqaGl111VV64oknVFNTo/nz52vcuHEqLCzU22+/nbzdhx9+qL333lt9+vTRb37zGy1atEgTJkxQ3759GxWQhx56SFlZWRo3bpzmzZunBQsWaMyYMcrOzqaEQJuydetWFRUVafjw4cafM2nSJEVRpEsuuUSLFy/WzJkz1aVLF/Xu3Vv//Oc/k7dLfK+VlZVp5syZWrJkiS666CJFUaQHHnggebv//M//VO/evZOlJMGUKVOUl5enf/3rX5Kkt99+W7vvvrv69eunBx98UE8//bROO+00RVGk6dOnJz+vYQHZsmWLunbt2ihHJGnYsGE65JBDkv9+4YUXlJeXpyOOOEJz5szR4sWLddZZZzV6RuXNN99UUVGRBg8erEcffVRPPvmkKioq1KdPn7QKSGuzUpIef/xxXXfddZo/f75qamr02GOPqby8XF26dEn5f3n99ddVUFCggw46SI899pieeuopHX/88cls2vHcb775ZmVlZenss8/WwoULNW/ePI0cOVIdOnTQm2++2eI11tbWKooi3X333Y0+ZlpAzjvvPOXm5uqqq67S4sWL9dvf/laDBg1St27dWvWsfqKA9O7dWyeddJIWLFighx9+WP3799cee+yRUmxsFJCePXtq0qRJeuaZZ/TEE09o69atKi8vV3FxsXr37q0777xT1dXVqqmp0RdffKGDDz5YnTt31owZM7R06VL98pe/VKdOnTRq1Kjksyfbt2/XUUcdpfz8fN1888167rnnVFlZqX333bfZ89tvv/30ve99b5f9ArtQQCAodlZAJKlbt27ab7/9kv/e1QIyZMiQlOXgF7/4haIo0oknnpjy+VdccYWiKFJdXV3yWGlpqQoLC/XBBx8kj61evVpRFKmkpCTlaeHf/e53iqJITz31VPJYZWWl8vLyUp5ZmTNnjqIoUk1NTbPXLDVfQKIo0muvvZY89u9//1vZ2dkqLCxMKRuJ87zjjjuafYytW7fq66+/VllZma688srk8cmTJysrK6vRkK6oqEgpIF988YWKi4sbFbdt27ZpyJAhGjZsWIvXCJAOH330kaIo0qmnnmp0+8SzEhdddFHK8VdeeUVRFOn//b//lzyW+F575ZVXUm47ePBgVVRUJP/91FNPKYoiPffcc8ljW7duVY8ePXTyyScnj5166qnKz89v9EzNf/zHf6ioqEgbN26U1PSvYP3oRz9SYWFh8jaS9NZbbymKIt15553JY4MGDdLQoUO1ZcuWlMcYM2aMSkpKkjk4fvx4FRYWpizBW7du1aBBg9IqIOlkZUO2bt2qzz//XB06dEh5Fun73/++OnTokFJKtm3bpsGDB6ec+/vvv6+cnBxdeumlKfe7adMmde/eXT/4wQ9avMZETv/v//5vo4+ZFJDly5criiL9/Oc/TzleW1urwsJCTZkypcXPb4pEATnkkENSfh3q73//u3Jzc3Xuuecmj9koIGeeeWaj2ya+L55//vmU49OmTdNuu+3WaJY/8cQTiqJIixYtkiQ988wzjZ4ZlL4pi82d3+mnn65u3bo1Og7xQgGBoDApIF27dk2rgPz4xz9Oud2zzz6rKIp07733phy/9957FUWR1qxZkzxWWlqqkSNHptxu8+bNiqJIp512WsrxxFPIOy4EH330kfLy8nTTTTcljx1xxBE68MADm73eBM0VkJKSkka3LSkpafY8r7rqquSxLVu26Oabb9Z+++2n3NzclHcgO+6445K3GzZsWJPn+Jvf/CalgCxZskRRFOmJJ57Qli1bUnT11VcrKyuLdzCBNmNXC0jiV5ZeffXVRh/bb7/9Up5JKS8vV/fu3Rvd7tRTT9WgQYOS/96yZYu6d++ekgdPP/20oijS008/nTzWtWtXHX/88Y3uL7HoJn5dsqkC8sYbbzTKrMmTJys/Pz/5Dl/vvPOOoijSbbfd1uh7MXHdb731VvJcxowZ0+hcEktrawtIOlm5adMmTZkyRf369VN2dnZKNl1wwQXJ23Xt2rXRDzwk6frrr08598SvZK1YsaKRH+PHj1fXrl1bvMbbb79dURTpb3/7W6OPmRSQqVOnKisrS//4xz8aPf6IESNa9cOZRAG57bbbGn2svLxc/fr1S/7bRgFp6rVT5eXl2muvvRod/853vqODDjqo0bVu2rRJWVlZycI1ZcoURVGUfGYwQeLrvqnzu/LKK5WVldWoWEO8UEAgKOL4FayG72DS3Av5mjqXxO81NySKIl188cUpx5p7vAkTJqh3797aunWrXn/99SbLT1O09BqQhpie56WXXqrddttNP/7xj7V48WK98sorWrFihYYMGZLiab9+/TR69OhG95f46VWigDz88MMpi0JTMv3dfIBdZVd/BevGG29UFEWNXu8hSUcffbT69++f/Hdz32sNc0b6pgzs+GLl73//+yopKdHWrVuTt8nOztY555zT6P6WLVumKIr08MMPS2r+Rejf/va3NWLEiOR1l5SUpPwU//e///1Ovxdfeuml5Lns+NPyBPfcc0/arwFpiGlWjh07VkVFRZo2bZqWLl2qV199VStWrFCXLl1SlmPTc7/pppta9GK33XZr8RqrqqoURZE+/PDDRh8zKSDnnntui4+/7777tvj5TZGYXYmvlR0ZP3689txzz+S/bRSQpop6eXm5Bg8e3Oh4//79W7zes88+W5J0zjnnKCcnp9Hnf/XVV82e349//GNFUaRNmzY1+hjEBwUEgmJnBSTx08Ebb7wxeayioiI5iHfkhBNOcLKAvPbaa4qiSHPnztV5552nPffc0+hZgbYoIHvttZfOOuusRrfr2bNnSgFp7hmQxDklCsjixYuTP8lcsWJFk9q8efNOrxWgtYwdO1Y5OTlNloqG7OwZkB1zZVcKSOLXoe655x598sknys/P19VXX51ym509A7J48WJJzReQHZ/FSLwZxI5vMvH2228nn/Ft7nsx8bcs2uoZkNZm5caN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+EfyPsrLy3Xeeedpw4YNSW3cuDFTl5Qxdubljv5s2LBBv/71r5WVlaX33nsveR8XXHCBevbsqSVLlmjlypU66qijNGTIEG3dujVTl5URbHjJ1+U37MzLc889V/369VN1dbXWrl2re++9V9nZ2frd736XvA++LluGHLUHOWoPctQe5KhfUEAc58svv1R2drYWLlyYcnzIkCGaOnWqJGn8+PE644wzWryf8vJyXX755W12nj5g4mVDTjrpJI0aNSr5740bNyo3N1ePPfZY8tj69eu12267afHixW1z4g5iw0uJr0vJzMv9999fP/3pT1M+fsghh+jaa6+VxNflziBH7UGO2oMctQc56h8UEMf57LPPFEWRli5dmnJ8xIgRKi8v17Zt29SxY0f99Kc/1bHHHqsuXbpo2LBhmj9/fsrty8vL1blzZ+29994aPHiwrrrqKn322WdxXkrG2ZmXDfnoo4+Uk5OjRx55JHns+eefVxRF+uSTT1Jue9BBB+m6665rk/N2ERteSnxdSmZenn/++frWt76lDz74QNu3b9cLL7ygjh07atmyZZL4utwZ5Kg9yFF7kKP2IEf9gwLiASNHjlR5ebnWr1+vrVu36qGHHlJWVpYGDBigDRs2KIoiFRUVacaMGVq1apWmTZumrKwsvfjii8n7mDVrlpYsWaI1a9bo0UcfVd++fTV69OgMXlVmaMnLhkyfPl177bWXvvrqq+SxRx55RHl5eY1ue8wxx2jSpElteu6uka6XEl+XCXbm5ebNm3XmmWcqiiLl5OQoLy9PDz74YPLz+brcOeSoPchRe5Cj9iBH/YIC4gHvvvuujjzySEVRpOzsbH3729/W6aefrv3220/r169XFEU67bTTUj5n7NixOvXUU5u9z9dee01RFOmPf/xjW5++U7TkZUMGDhyoSy65JOVYcwE1evRonX/++W123i6SrpdNwddl017eeuutGjBggJ566im9/vrruvPOO9WxY0ctWbJEEl+XJpCj9iBH7UGO2oMc9QsKiEd8/vnn+vDDDyV982Kr448/Xps3b1ZOTo5uvPHGlNtOmTJFhx12WLP3tX379ka/69ieaMrLHXnppZcURZFWr16dcpynaBvTWi+bgq/Lxl5++eWXys3NbfS7zeecc44qKiok8XW5K5Cj9iBH7UGO2oMc9QMKiId88skn6tSpk+69915J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unTRU089pWXLlumyyy5TEAR68MEHU/ezISCdO3fWSSedpOeff15vvfWWPvjgg9TPTufOnTVp0iS99tpreumll/T1118b9/6qY4wbN06/+c1v9Oyzz6p79+6puoAbICDgDZ9++qmCINCll15qdP8//elPCoJAP/rRj9Ju//3vf68gCPSTn/wkdVvVX6refPPNtPtWNdlu3bpp//79af/Wu3dvnXLKKTX+qjRmzBgVFBSk/jJ51VVXKTc3N+0vY9Wp7yVYpo8zfvx4tW7dWp9++mnqPl9//bV69+4dSkBat26t7du3p25bt26dgiBQQUFB2stDli5dqiAI9PLLL9f5GF9//bX27Nmjtm3bpv1F7eKLL1bbtm3TpOTAgQPq27dv2ve+bds25eTk6Kabbko7bkVFhTp27KhLLrmk3nMEaAxVi8x//dd/6auvvlJFRYVef/11dezYUUOGDEn7nTT9Pa3v5UN19aE77rij1pfxXH/99crKytJHH33U4LlUHbv6S1SvvfZatWjRQlu3bpVUd887cOCAOnXqpG9/+9tpz7pUVFSoQ4cOOuuss2rUrer39m9/+5vy8/M1duzYtMc+cOCA+vfvr4EDB6ZuGzZsmI444oiUUNRGXTVszOOcccYZ6tSpU+ov65K0e/du5efnNyggX3zxhVq3bq3zzjsv7fZt27apVatW+sEPflCjFtUltjYaeglWXl5e6v+TJO3du1f5+fmaPHly6rbJkyerXbt2afeTpIceekhBEDT4R5rCwkJlZWVp3bp1abePHDlShx9+eKrn2xCQ9u3b17iOqqpeV1xxRdrtpr2/6uf01FNPTXvZ1p///Gfl5uYiIA6BgIA3NFZA5s2bpyAItHr16hr/1qdPn7RnUoqKinTkkUfWuF9Vk73lllvSbt+4caOCINBDDz2kr776Ki1Vj1slHAUFBRo1alS932tdAtKYx+nQoYPGjBlT49hVzb+pAnLmmWem3bZv377UX/4O5aOPPlIQBHr44YdTt1VUVGjatGnq1q2bsrOz015acN1116Xu16FDhxpLgyT99Kc/Tfveq16StWbNmhr1GD9+vDp06FDvOQI0hrpe7tOnT5+0dypqzO9pQwJSWx8aOHCg+vbtW+P2qj+mPProo5K+Wb4OfexDX1pVVFSkww47rMYxql4C+swzz0iqu+dt2LBBQRBo9uzZNY5x/fXXq0WLFqnltLqALF++XEEQ6KWXXqpRn9tvv11ZWVnas2eP/va3vyk7O1uTJk2q8RiHUlcNTR9nz549atGihW688cYax544cWKDArJs2TIFQaBf//rXNf7tu9/9rr71rW+l/tumgAwaNKjG7YMGDdK5556b+u/OnTtr7NixNc7/j3/8o4Ig0Lx58+r9HgoLC9WvX78at1edR9UzXTYE5Hvf+16dj1NdlE17f9XP6UMPPVTj2EVFRQiIQyAg4A2NfQnWvffeqyAIalzvIUnDhw9X9+7dU/9dVFRU64CvarLVh+5//Md/1LqYHJq3335bkpSTk6Orrrqq3u+1LgFpzONkZ2frmmuuqXHsRx99NJSA1HZBYvWXbUh/r9WhT9OPHTtWbdq00axZs7RixQqtXr1aa9as0THHHJP2WmjT733GjBn11qJFixb1niNAY6hahhYsWKA1a9borbfe0uTJkxUEQdrS15jf04YEpLY+1K1bNw0fPrzG7WVlZQqCQDNmzJBU8zqCQ5etoqKitJ5XRdUzxXPnzpVUd89755130kTlUKp6bdUzpdUFpOpC7Pqybds2bd++XUEQ6J577qnxGIdSVw1NH6d63Q7l9ttvb1BAnnnmmbRl/FCuvvpq5eTkpP7bpoDU1ouLiopUVFSU+u+cnJx6z7+h2hYWFmrEiBE1bn/ttdcUBIGWLl0qyY6AVH91gvT3elX/w6Fp76/6OX322WdrHLvqekRwAwQEvGLs2LHKycmpVSqq09AzIIf+NanqAs3q1LZUS9KHH36oIAh05513as2aNbVm9+7dksI9A9KYx2muZ0CaKiC7du1SVlaWfvrTn6bdr7KyUtnZ2WkCUtczINW/98ceeyz1F8666gFgi7qWx2uuuUZBEOjFF1+U1Ljf04YEpLY+1NAzII899pikbz5P6NDHfP/999OO3ZhnQKr3vDDPgLz++usKgm+eHa2rPvv27dOXX34Z6hkQ08fZs2ePsrKymu0ZkI4dO6b+O2oB6dixo0aNGlXn+e/YsaPe7yHMMyCTJ09OO/cqbrjhhloFpPoMOfRxqtfLtPfzDIg/ICDgFYe+De++fftq/Pv+/ftT1yBULQU333xz2n1Wr16tIAg0ffr01G2NFRBJ6tGjR43XANdG1TUgH374YZ33ef/99xUEtT89bvo4zXUNSFMFpLy8XEFQ852Cqi7gPVRATK8B2bJli3JycvTAAw/Uey4ANqhrGfr888915JFHqk+fPqnrIUx/T19++WUFQaBly5bV+Le6+tCdd96pIAj0hz/8Ie32G264odmuAane8w4cOKDOnTvr5JNPTntt/Z49e9ShQwedffbZqduqC0hFRYWOOOIIowvmhw0bpiOPPDKtF1Snrho25nFsXANywQUXpN1eVlamVq1a6bLLLkvd1hgB+Zd/+RcFQVDr9YKmAnLNNdeoU6dOTfqMmqrHqesakMMOO6zea0BmzZqlFi1apM2gffv2qXv37qEFxLT3HzhwQAUFBRowYADXgDgOAgLeUfVBhP369dMjjzyiVatWafny5Zo9e7a6d++e9kGEkyZNUlZWlqZOnao33nhDjz/+uDp06KDjjjtOn332Wep+TRGQt956S61atdKoUaP0/PPPq6SkREuWLNHMmTN10UUXpe4QSJ0QAAAgAElEQVRX9S5YHTp00Ny5c/Xmm29q0aJFuvbaa/WnP/1J0jcXT7Zu3Vpnn322Vq5cmfaXKtPHWb9+vVq3bq2+ffum3klq9OjROu644zIiIJI0ZMgQ5efn65e//KWWL1+uu+66SwUFBTriiCPSBGTdunWpd8F64YUXUu+CVVhYqCAI0i6onDlzpnJycjR58mQtWbJEq1at0gsvvKBbb71Vd999d73nCNAY6lseZ8+enfbMgenv6ccff6wgCHThhRfqnXfe0Zo1a1K9qK4+VPUuWB07dtT8+fNT72CVlZVV68tYauPQd8F6+OGH9cYbb2jKlCkKgvR30qqv51W9C9Z5552nf//3f9evf/1rnX766cbvgtWiRQuNHz9eL774okpKSvTSSy/pn/7pn9KuB6t6F6yuXbtq/vz5euutt7Rw4UJNmDAh9SxSfTU0fZzf/va3atGihQYPHqwlS5bopZde0umnn57qlw1R9S5YP/zhD7Vs2TI988wz6t69e9q7YB1aCxMBqXo2avLkyfrd736X9syZqYB88sknKiwsVO/evTVv3jy9+eabevXVV/XII4/o/PPPb/DVA9XfBeu1115LvQvWoct/bQLy8ccfKzc3V+ecc45effVVLVq0SEVFRTrhhBNCC4hk3vufeOIJBcE374L1yiuv8C5YjoKAgJesW7dOEydOVJcuXdSyZUu1bdtWp5xyiu6+++60d0+p+hyQnj17Kjc3V0cf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\" width=\"800\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fix, ax = subplots(2,2, figsize=(8,8))\n",
    "ax[0,0].imshow(dummy_image)\n",
    "ax[0,1].imshow(dummy_blurred)\n",
    "ax[1,1].imshow(csr.dot(dummy_blurred.ravel()).reshape(mask.shape))\n",
    "ax[0,0].set_xlim(964,981)\n",
    "ax[0,0].set_ylim(0,16)\n",
    "ax[0,0].set_title(\"Dummy image\")\n",
    "ax[0,1].set_xlim(964,981)\n",
    "ax[0,1].set_ylim(0,16)\n",
    "ax[0,1].set_title(\"Convolved image (i.e. blurred)\")\n",
    "ax[1,1].set_xlim(964,981)\n",
    "ax[1,1].set_ylim(0,16)\n",
    "ax[1,1].set_title(\"Retro-projected of the blurred\")\n",
    "ax[1,0].set_title(\"Corrected image\")\n",
    "pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def iterMLEM_scipy(F, M, R):\n",
    "    \"Implement one step of MLEM\"\n",
    "    #res = F * (R.T.dot(M))/R.dot(F)# / M.sum(axis=-1)\n",
    "    norm = 1/R.T.dot(numpy.ones_like(F)) \n",
    "    cor = R.T.dot(M/R.dot(F))\n",
    "    res = norm * F * cor\n",
    "    res[numpy.isnan(res)] = 1.0\n",
    "    return res\n",
    "\n",
    "def deconv_MLEM(csr, data, thres=0.2, maxiter=1000):\n",
    "    R = csr.T\n",
    "    msk = data<0\n",
    "    img = data.astype(\"float32\")\n",
    "    img[msk] = 0.0 # set masked values to 0, negative values could induce errors\n",
    "    M = img.ravel()\n",
    "    #F0 = numpy.random.random(data.size)#M#\n",
    "    F0 = R.T.dot(M)\n",
    "    F1 = iterMLEM_scipy(F0, M, R)\n",
    "    delta = abs(F1-F0).max()\n",
    "    for i in range(maxiter):\n",
    "        if delta<thres:\n",
    "            break\n",
    "        F2 = iterMLEM_scipy(F1, M, R)\n",
    "        delta = abs(F1-F2).max()\n",
    "        if i%100==0:\n",
    "            print(i, delta)\n",
    "        F1 = F2\n",
    "        i+=1\n",
    "    print(i, delta)\n",
    "    return F2.reshape(img.shape)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "<ipython-input-15-446968d39087>:4: RuntimeWarning: divide by zero encountered in true_divide\n",
      "  norm = 1/R.T.dot(numpy.ones_like(F))\n",
      "<ipython-input-15-446968d39087>:5: RuntimeWarning: invalid value encountered in true_divide\n",
      "  cor = R.T.dot(M/R.dot(F))\n",
      "<ipython-input-15-446968d39087>:6: RuntimeWarning: invalid value encountered in multiply\n",
      "  res = norm * F * cor\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 1.7867398\n",
      "100 0.0016492605\n",
      "200 0.00027656555\n",
      "262 9.918213e-05\n",
      "CPU times: user 3.99 s, sys: 12 ms, total: 4 s\n",
      "Wall time: 4 s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(0.0, 16.0)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time res = deconv_MLEM(csr, dummy_blurred, 1e-4)\n",
    "\n",
    "ax[1,0].imshow(res)\n",
    "ax[1,0].set_xlim(964,981)\n",
    "ax[1,0].set_ylim(0,16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Conclusion of the raytracing part:\n",
    "\n",
    "We are able to simulate the path and the absorption of the photon in the thickness of the detector. \n",
    "Numba helped substentially to make the raytracing calculation much faster. \n",
    "The signal of each pixel is indeed spread on the neighboors, depending on the position of the PONI and this effect can be inverted using sparse-matrix pseudo-inversion. \n",
    "The MLEM can garanteee that the total signal is conserved and that no pixel gets negative value.\n",
    "\n",
    "We will now save this sparse matrix to file in order to be able to re-use it in next notebook. But before saving it, it makes sense to spend some time in generating a high quality sparse matrix in throwing thousand rays per pixel in a grid of 64x64."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 26min 42s, sys: 5.23 s, total: 26min 47s\n",
      "Wall time: 26min 42s\n"
     ]
    }
   ],
   "source": [
    "%time pre_csr = thick.calc_csr(64)\n",
    "hq_csr = csr_matrix(pre_csr)\n",
    "from scipy.sparse import save_npz\n",
    "save_npz(\"csr.npz\",hq_csr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total execution time: 1650.399 s\n"
     ]
    }
   ],
   "source": [
    "print(f\"Total execution time: {time.perf_counter()-start_time:.3f} s\")"
   ]
  }
 ],
 "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.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}