{"action":"created","comment":{"url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments/48027345","id":48027345,"diff_hunk":"@@ -0,0 +1,881 @@\n+# -*- coding: utf-8 -*-\n+# Copyright 2007-2015 The HyperSpy developers\n+#\n+# This file is part of HyperSpy.\n+#\n+#  HyperSpy is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+#  HyperSpy is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with  HyperSpy.  If not, see <http://www.gnu.org/licenses/>.\n+\n+from __future__ import division\n+\n+import warnings\n+import numpy as np\n+import math\n+from contextlib import contextmanager\n+\n+from hyperspy.models.model1D import Model1D\n+from hyperspy._signals.eds import EDSSpectrum\n+from hyperspy.misc.elements import elements as elements_db\n+from hyperspy.misc.eds import utils as utils_eds\n+import hyperspy.components as create_component\n+\n+eV2keV = 1000.\n+sigma2fwhm = 2 * math.sqrt(2 * math.log(2))\n+\n+\n+def _get_weight(element, line, weight_line=None):\n+    if weight_line is None:\n+        weight_line = elements_db[\n+            element]['Atomic_properties']['Xray_lines'][line]['weight']\n+    return lambda x: x * weight_line\n+\n+\n+def _get_iweight(element, line, weight_line=None):\n+    if weight_line is None:\n+        weight_line = elements_db[\n+            element]['Atomic_properties']['Xray_lines'][line]['weight']\n+    return lambda x: x / weight_line\n+\n+\n+def _get_sigma(E, E_ref, units_factor):\n+    \"\"\"Calculates an approximate sigma value, accounting for peak broadening due\n+    to the detector, for a peak at energy E given a known width at a reference\n+    energy.\n+\n+    The factor 2.5 is a constant derived by Fiori & Newbury as references below.\n+\n+    Parameters\n+    ----------\n+    energy_resolution_MnKa : float\n+        Energy resolution of Mn Ka in eV\n+    E : float\n+        Energy of the peak in keV\n+\n+    Returns\n+    -------\n+    float : FWHM of the peak in keV\n+\n+    Notes\n+    -----\n+    This method implements the equation derived by Fiori and Newbury as is\n+    documented in the following:\n+\n+        Fiori, C. E., and Newbury, D. E. (1978). In SEM/1978/I, SEM, Inc.,\n+        AFM O'Hare, Illinois, p. 401.\n+\n+        Goldstein et al. (2003). \"Scanning Electron Microscopy & X-ray\n+        Microanalysis\", Plenum, third edition, p 315.\n+    \"\"\"\n+    energy2sigma_factor = 2.5 / (eV2keV * (sigma2fwhm**2))\n+    return lambda sig_ref: math.sqrt(abs(\n+        energy2sigma_factor * (E - E_ref) * units_factor +\n+        np.power(sig_ref, 2)))\n+\n+\n+def _get_offset(diff):\n+    return lambda E: E + diff\n+\n+\n+def _get_scale(E1, E_ref1, fact):\n+    return lambda E: E1 + fact * (E - E_ref1)\n+\n+\n+@contextmanager\n+def stash_active_state(model):\n+    active_state = []\n+    for component in model:\n+        if component.active_is_multidimensional:\n+            active_state.append(component._active_array)\n+        else:\n+            active_state.append(component.active)\n+    yield\n+    for component in model:\n+        active_s = active_state.pop(0)\n+        if isinstance(active_s, bool):\n+            component.active = active_s\n+        else:\n+            if not component.active_is_multidimensional:\n+                component.active_is_multidimensional = True\n+            component._active_array[:] = active_s\n+\n+\n+class EDSModel(Model1D):\n+    \"\"\"Build and fit a model of an EDS Spectrum.\n+\n+    Parameters\n+    ----------\n+    spectrum : an EDSSpectrum (or any EDSSpectrum subclass) instance.\n+\n+    auto_add_lines : boolean\n+        If True, automatically add Gaussians for all X-rays generated\n+        in the energy range by an element, using the edsmodel.add_family_lines\n+        method.\n+\n+    auto_background : boolean\n+        If True, adds automatically a polynomial order 6 to the model,\n+        using the edsmodel.add_polynomial_background method.\n+\n+    Any extra arguments are passed to the Model creator.\n+\n+    Example\n+    -------\n+    >>> m = create_model(s)\n+    >>> m.fit()\n+    >>> m.fit_background()\n+    >>> m.calibrate_energy_axis('resolution')\n+    >>> m.calibrate_xray_lines('energy',['Au_Ma'])\n+    >>> m.calibrate_xray_lines('sub_weight',['Mn_La'],bound=10)\n+    \"\"\"\n+\n+    def __init__(self, spectrum,\n+                 auto_background=True,\n+                 auto_add_lines=True,\n+                 *args, **kwargs):\n+        Model1D.__init__(self, spectrum, *args, **kwargs)\n+        self.xray_lines = list()\n+        end_energy = self.axes_manager.signal_axes[0].high_value\n+        self.end_energy = min(end_energy, self.spectrum._get_beam_energy())\n+        self.start_energy = self.axes_manager.signal_axes[0].low_value\n+        self.background_components = list()\n+        if auto_background is True:\n+            self.add_polynomial_background()\n+        if auto_add_lines is True:\n+            self.add_family_lines()\n+\n+    @property\n+    def units_factor(self):\n+        units_name = self.axes_manager.signal_axes[0].units\n+        if units_name == 'eV':\n+            return 1000.\n+        elif units_name == 'keV':\n+            return 1.\n+        else:\n+            raise ValueError(\"Energy units, %s, not supported\" %\n+                             str(units_name))\n+\n+    @property\n+    def spectrum(self):\n+        return self._spectrum\n+\n+    @spectrum.setter\n+    def spectrum(self, value):\n+        if isinstance(value, EDSSpectrum):\n+            self._spectrum = value\n+        else:\n+            raise ValueError(\n+                \"This attribute can only contain an EDSSpectrum \"\n+                \"but an object of type %s was provided\" %\n+                str(type(value)))\n+\n+    @property\n+    def _active_xray_lines(self):\n+        return [xray_line for xray_line\n+                in self.xray_lines if xray_line.active]\n+\n+    def add_family_lines(self, xray_lines='from_elements'):\n+        \"\"\"Create the Xray-lines instances and configure them appropiately\n+\n+        If a X-ray line is given, all the the lines of the familiy is added.\n+        For instance if Zn Ka is given, Zn Kb is added too. The main lines\n+        (alpha) is added to self.xray_lines\n+\n+        Parameters\n+        -----------\n+        xray_lines: {None, 'from_elements', list of string}\n+            If None, if `metadata` contains `xray_lines` list of lines use\n+            those. If 'from_elements', add all lines from the elements contains\n+            in `metadata`. Alternatively, provide an iterable containing\n+            a list of valid X-ray lines symbols. (eg. ('Al_Ka','Zn_Ka')).\n+        \"\"\"\n+\n+        only_one = False\n+        only_lines = (\"Ka\", \"La\", \"Ma\")\n+\n+        if xray_lines is None or xray_lines == 'from_elements':\n+            if 'Sample.xray_lines' in self.spectrum.metadata \\\n+                    and xray_lines != 'from_elements':\n+                xray_lines = self.spectrum.metadata.Sample.xray_lines\n+            elif 'Sample.elements' in self.spectrum.metadata:\n+                xray_lines = self.spectrum._get_lines_from_elements(\n+                    self.spectrum.metadata.Sample.elements,\n+                    only_one=only_one,\n+                    only_lines=only_lines)\n+            else:\n+                raise ValueError(\n+                    \"No elements defined, set them with `add_elements`\")\n+\n+        components_names = [xr.name for xr in self.xray_lines]\n+        xray_lines = filter(lambda x: x not in components_names, xray_lines)\n+        xray_lines, xray_not_here = self.spectrum.\\\n+            _get_xray_lines_in_spectral_range(xray_lines)\n+        for xray in xray_not_here:\n+            warnings.warn(\"%s is not in the data energy range.\" % (xray))\n+\n+        for i, xray_line in enumerate(xray_lines):\n+            element, line = utils_eds._get_element_and_line(xray_line)\n+            line_energy, line_FWHM = self.spectrum._get_line_energy(\n+                xray_line,\n+                FWHM_MnKa='auto')\n+            component = create_component.Gaussian()\n+            component.centre.value = line_energy\n+            component.fwhm = line_FWHM\n+            component.centre.free = False\n+            component.sigma.free = False\n+            component.name = xray_line\n+            self.append(component)\n+            self.xray_lines.append(component)\n+            init = True\n+            if init:\n+                self[xray_line].A.map[\n+                    'values'] = self.spectrum[..., line_energy].data * \\\n+                    line_FWHM / self.spectrum.axes_manager[-1].scale\n+                self[xray_line].A.map['is_set'] = (\n+                    np.ones(self.spectrum[..., line_energy].data.shape) == 1)\n+\n+            component.A.ext_force_positive = True\n+            for li in elements_db[element]['Atomic_properties']['Xray_lines']:\n+                if line[0] in li and line != li:\n+                    xray_sub = element + '_' + li\n+                    if self.spectrum.\\\n+                            _get_xray_lines_in_spectral_range(\n+                                [xray_sub])[0] != []:\n+                        line_energy, line_FWHM = self.spectrum.\\\n+                            _get_line_energy(\n+                                xray_sub, FWHM_MnKa='auto')\n+                        component_sub = create_component.Gaussian()\n+                        component_sub.centre.value = line_energy\n+                        component_sub.name = xray_sub\n+                        component_sub.fwhm = line_FWHM\n+                        component_sub.centre.free = False\n+                        component_sub.sigma.free = False\n+                        component_sub.A.twin_function = _get_weight(\n+                            element, li)\n+                        component_sub.A.twin_inverse_function = _get_iweight(\n+                            element, li)\n+                        component_sub.A.twin = component.A\n+                        self.append(component_sub)\n+            self.fetch_stored_values()\n+\n+    @property\n+    def _active_background_components(self):\n+        return [bc for bc in self.background_components\n+                if bc.coefficients.free]\n+\n+    def add_polynomial_background(self, order=6):\n+        \"\"\"\n+        Add a polynomial background.\n+\n+        the background is added to self.background_components\n+\n+        Parameters\n+        ----------\n+        order: int\n+            The order of the polynomial\n+        \"\"\"\n+        background = create_component.Polynomial(order=order)\n+        background.name = 'background_order_' + str(order)\n+        background.isbackground = True\n+        self.append(background)\n+        self.background_components.append(background)\n+\n+    def free_background(self):\n+        \"\"\"\n+        Free the yscale of the background components.\n+        \"\"\"\n+        for component in self.background_components:\n+            component.coefficients.free = True\n+\n+    def fix_background(self):\n+        \"\"\"\n+        Fix the background components.\n+        \"\"\"\n+        for component in self._active_background_components:\n+            component.coefficients.free = False\n+\n+    def enable_xray_lines(self):\n+        \"\"\"Enable the X-ray lines components.\n+\n+        \"\"\"\n+        for component in self.xray_lines:\n+            component.active = True\n+\n+    def disable_xray_lines(self):\n+        \"\"\"Disable the X-ray lines components.\n+\n+        \"\"\"\n+        for component in self._active_xray_lines:\n+            component.active = False\n+\n+    def fit_background(self,\n+                       start_energy=None,\n+                       end_energy=None,\n+                       windows_sigma=[4., 3.],\n+                       kind='single',\n+                       **kwargs):\n+        \"\"\"\n+        Fit the background in the energy range containing no X-ray line.\n+\n+        After the fit, the background is fixed.\n+\n+        Parameters\n+        ----------\n+        start_energy : {float, None}\n+            If float, limit the range of energies from the left to the\n+            given value.\n+        end_energy : {float, None}\n+            If float, limit the range of energies from the right to the\n+            given value.\n+        windows_sigma: list of two float\n+            The upper and lower bounds around each X-ray lines to define\n+            the energy range free of X-ray lines.\n+        kind : {'single', 'multi'}\n+            If 'single' fit only the current location. If 'multi'\n+            use multifit.\n+        **kwargs : extra key word arguments\n+            All extra key word arguments are passed to fit or\n+\n+        See also\n+        --------\n+        free_background\n+        \"\"\"\n+\n+        if end_energy is None:\n+            end_energy = self.end_energy\n+        if start_energy is None:\n+            start_energy = self.start_energy\n+\n+        # disactivate line\n+        self.free_background()\n+        with stash_active_state(self):\n+            self.disable_xray_lines()\n+            self.set_signal_range(start_energy, end_energy)\n+            for component in self:\n+                if component.isbackground is False:\n+                    self.remove_signal_range(\n+                        component.centre.value -\n+                        windows_sigma[0] * component.sigma.value,\n+                        component.centre.value +\n+                        windows_sigma[1] * component.sigma.value)\n+            if kind == 'single':\n+                self.fit(**kwargs)\n+            if kind == 'multi':\n+                self.multifit(**kwargs)\n+            self.reset_signal_range()\n+        self.fix_background()\n+\n+    def _twin_xray_lines_width(self, xray_lines):\n+        \"\"\"\n+        Twin the width of the peaks\n+\n+        The twinning models the energy resolution of the detector\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        \"\"\"\n+        if xray_lines == 'all_alpha':\n+            xray_lines = [compo.name for compo in self.xray_lines]\n+\n+        for i, xray_line in enumerate(xray_lines):\n+            component = self[xray_line]\n+            if i == 0:\n+                component_ref = component\n+                component_ref.sigma.free = True\n+                E_ref = component_ref.centre.value\n+            else:\n+                component.sigma.free = True\n+                E = component.centre.value\n+                component.sigma.twin_function = _get_sigma(\n+                    E, E_ref, self.units_factor)\n+                component.sigma.twin_inverse_function = _get_sigma(\n+                    E_ref, E, self.units_factor)\n+                component.sigma.twin = component_ref.sigma\n+\n+    def _set_energy_resolution(self, xray_lines, *args, **kwargs):\n+        \"\"\"\n+        Adjust the width of all lines and set the fitted energy resolution\n+        to the spectrum\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        \"\"\"\n+        if xray_lines == 'all_alpha':\n+            xray_lines = [compo.name for compo in self.xray_lines]\n+        energy_Mn_Ka, FWHM_MnKa_old = self.spectrum._get_line_energy('Mn_Ka',\n+                                                                     'auto')\n+        FWHM_MnKa_old *= eV2keV / self.units_factor\n+        get_sigma_Mn_Ka = _get_sigma(\n+            energy_Mn_Ka, self[xray_lines[0]].centre.value, self.units_factor)\n+        FWHM_MnKa = get_sigma_Mn_Ka(self[xray_lines[0]].sigma.value\n+                                    ) * eV2keV / self.units_factor * sigma2fwhm\n+        if FWHM_MnKa < 110:\n+            warnings.warn(\"FWHM_MnKa of \" + str(FWHM_MnKa) + \" smaller than\" +\n+                          \"physically possible\")\n+        else:\n+            self.spectrum.set_microscope_parameters(\n+                energy_resolution_MnKa=FWHM_MnKa)\n+            warnings.warn(\"Energy resolution (FWHM at Mn Ka) changed from \"\n+                          + \"%lf to %lf eV\" % (FWHM_MnKa_old, FWHM_MnKa))\n+            for component in self:\n+                if component.isbackground is False:\n+                    line_energy, line_FWHM = self.spectrum._get_line_energy(\n+                        component.name, FWHM_MnKa='auto')\n+                    component.fwhm = line_FWHM\n+\n+    def _twin_xray_lines_scale(self, xray_lines):\n+        \"\"\"\n+        Twin the scale of the peaks\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        \"\"\"\n+        if xray_lines == 'all_alpha':\n+            xray_lines = [compo.name for compo in self.xray_lines]\n+        ax = self.spectrum.axes_manager[-1]\n+        ref = []\n+        for i, xray_line in enumerate(xray_lines):\n+            component = self[xray_line]\n+            if i == 0:\n+                component_ref = component\n+                component_ref.centre.free = True\n+                E_ref = component_ref.centre.value\n+                ref.append(E_ref)\n+            else:\n+                component.centre.free = True\n+                E = component.centre.value\n+                fact = float(ax.value2index(E)) / ax.value2index(E_ref)\n+                component.centre.twin_function = _get_scale(E, E_ref, fact)\n+                component.centre.twin_inverse_function = _get_scale(\n+                    E_ref, E, 1./fact)\n+                component.centre.twin = component_ref.centre\n+                ref.append(E)\n+        return ref\n+\n+    def _set_energy_scale(self, xray_lines, ref):\n+        \"\"\"\n+        Adjust the width of all lines and set the fitted energy resolution\n+        to the spectrum\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        ref: float\n+            the centre of the first line before the fit\n+        \"\"\"\n+        if xray_lines == 'all_alpha':\n+            xray_lines = [compo.name for compo in self.xray_lines]\n+        ax = self.spectrum.axes_manager[-1]\n+        scale_old = self.spectrum.axes_manager[-1].scale\n+        ind = np.argsort(np.array(\n+            [compo.centre.value for compo in self.xray_lines]))[-1]\n+        E = self[xray_lines[ind]].centre.value\n+        scale = (ref[ind] - ax.offset) / ax.value2index(E)\n+        ax.scale = scale\n+        for i, xray_line in enumerate(xray_lines):\n+            component = self[xray_line]\n+            component.centre.value = ref[i]\n+        print \"Scale changed from  %lf to %lf\" % (scale_old, scale)\n+\n+    def _twin_xray_lines_offset(self, xray_lines):\n+        \"\"\"\n+        Twin the offset of the peaks\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        \"\"\"\n+        if xray_lines == 'all_alpha':\n+            xray_lines = [compo.name for compo in self.xray_lines]\n+        ref = []\n+        for i, xray_line in enumerate(xray_lines):\n+            component = self[xray_line]\n+            if i == 0:\n+                component_ref = component\n+                component_ref.centre.free = True\n+                E_ref = component_ref.centre.value\n+                ref.append(E_ref)\n+            else:\n+                component.centre.free = True\n+                E = component.centre.value\n+                diff = E_ref - E\n+                component.centre.twin_function = _get_offset(-diff)\n+                component.centre.twin_inverse_function = _get_offset(diff)\n+                component.centre.twin = component_ref.centre\n+                ref.append(E)\n+        return ref\n+\n+    def _set_energy_offset(self, xray_lines, ref):\n+        \"\"\"\n+        Adjust the width of all lines and set the fitted energy resolution\n+        to the spectrum\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        ref: float\n+            the centre of the first line before the fit\n+        \"\"\"\n+        if xray_lines == 'all_alpha':\n+            xray_lines = [compo.name for compo in self.xray_lines]\n+        diff = self[xray_lines[0]].centre.value - ref[0]\n+        offset_old = self.spectrum.axes_manager[-1].offset\n+        self.spectrum.axes_manager[-1].offset -= diff\n+        offset = self.spectrum.axes_manager[-1].offset\n+        print \"Offset changed from  %lf to %lf\" % (offset_old, offset)\n+        for i, xray_line in enumerate(xray_lines):\n+            component = self[xray_line]\n+            component.centre.value = ref[i]\n+\n+    def calibrate_energy_axis(self,\n+                              calibrate='resolution',\n+                              xray_lines='all_alpha',\n+                              spread_to_all_lines=True,\n+                              **kwargs):\n+        \"\"\"\n+        Calibrate the resolution, the scale or the offset of the energy axis\n+        by fitting.\n+\n+        Parameters\n+        ----------\n+        calibrate: 'resolution' or 'scale' or 'offset'\n+            If 'resolution', fits the width of Gaussians place at all x-ray\n+            lines. The width is given by a model of the detector resolution,\n+            obtained by extrapolation the `energy_resolution_MnKa` in `metadata`\n+            This method will update the value of `energy_resolution_MnKa`.\n+            If 'scale', calibrate the scale of the energy axis\n+            If 'offset', calibrate the offset of the energy axis\n+        xray_lines: list of str or 'all_alpha'\n+            The Xray lines. If 'all_alpha', fit all using all alpha lines\n+        **kwargs : extra key word arguments\n+            All extra key word arguments are passed to fit or\n+            multifit, depending on the value of kind.\n+\n+        \"\"\"\n+\n+        if calibrate == 'resolution':\n+            free = self._twin_xray_lines_width\n+            fix = self.fix_xray_lines_width\n+            scale = self._set_energy_resolution\n+        elif calibrate == 'scale':\n+            free = self._twin_xray_lines_scale\n+            fix = self.fix_xray_lines_energy\n+            scale = self._set_energy_scale\n+        elif calibrate == 'offset':\n+            free = self._twin_xray_lines_offset\n+            fix = self.fix_xray_lines_energy\n+            scale = self._set_energy_offset\n+        ref = free(xray_lines=xray_lines)\n+        self.fit(**kwargs)\n+        fix(xray_lines=xray_lines)\n+        scale(xray_lines=xray_lines, ref=ref)\n+        self.update_plot()\n+\n+    def free_sub_xray_lines_weight(self, xray_lines='all', bound=0.01):\n+        \"\"\"\n+        Free the weight of a sub X-ray lines\n+\n+        Remove the twin on the height of sub-Xray lines (non alpha)\n+\n+        Parameters\n+        ----------\n+        xray_lines: list of str or 'all'\n+            The Xray lines. If 'all', fit all lines\n+        bounds: float\n+            Bound the height of the peak to a fraction of\n+            its height\n+        \"\"\"\n+        def free_twin():","path":"hyperspy/models/edsmodel.py","position":605,"original_position":605,"commit_id":"c29ba200d35248c4b89a36dceb6d817587f76da0","original_commit_id":"c29ba200d35248c4b89a36dceb6d817587f76da0","user":{"login":"dnjohnstone","id":10629656,"avatar_url":"https://avatars.githubusercontent.com/u/10629656?v=3","gravatar_id":"","url":"https://api.github.com/users/dnjohnstone","html_url":"https://github.com/dnjohnstone","followers_url":"https://api.github.com/users/dnjohnstone/followers","following_url":"https://api.github.com/users/dnjohnstone/following{/other_user}","gists_url":"https://api.github.com/users/dnjohnstone/gists{/gist_id}","starred_url":"https://api.github.com/users/dnjohnstone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dnjohnstone/subscriptions","organizations_url":"https://api.github.com/users/dnjohnstone/orgs","repos_url":"https://api.github.com/users/dnjohnstone/repos","events_url":"https://api.github.com/users/dnjohnstone/events{/privacy}","received_events_url":"https://api.github.com/users/dnjohnstone/received_events","type":"User","site_admin":false},"body":"done","created_at":"2015-12-18T14:06:58Z","updated_at":"2015-12-18T14:06:58Z","html_url":"https://github.com/hyperspy/hyperspy/pull/786#discussion_r48027345","pull_request_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786","_links":{"self":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments/48027345"},"html":{"href":"https://github.com/hyperspy/hyperspy/pull/786#discussion_r48027345"},"pull_request":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786"}}},"pull_request":{"url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786","id":53166966,"html_url":"https://github.com/hyperspy/hyperspy/pull/786","diff_url":"https://github.com/hyperspy/hyperspy/pull/786.diff","patch_url":"https://github.com/hyperspy/hyperspy/pull/786.patch","issue_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/786","number":786,"state":"open","locked":false,"title":"NEW: EDSModel","user":{"login":"dnjohnstone","id":10629656,"avatar_url":"https://avatars.githubusercontent.com/u/10629656?v=3","gravatar_id":"","url":"https://api.github.com/users/dnjohnstone","html_url":"https://github.com/dnjohnstone","followers_url":"https://api.github.com/users/dnjohnstone/followers","following_url":"https://api.github.com/users/dnjohnstone/following{/other_user}","gists_url":"https://api.github.com/users/dnjohnstone/gists{/gist_id}","starred_url":"https://api.github.com/users/dnjohnstone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dnjohnstone/subscriptions","organizations_url":"https://api.github.com/users/dnjohnstone/orgs","repos_url":"https://api.github.com/users/dnjohnstone/repos","events_url":"https://api.github.com/users/dnjohnstone/events{/privacy}","received_events_url":"https://api.github.com/users/dnjohnstone/received_events","type":"User","site_admin":false},"body":"Deprecates #473 and is written to be implemented after #536  so although it looks ridiculously big it won't be very big at all when someone gets round to reviewing #536.\r\n\r\nOtherwise I think this should be ready for review.","created_at":"2015-12-09T19:36:53Z","updated_at":"2015-12-18T14:06:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a52060c079b477437b12127818e1fd23e7cd2631","assignee":null,"milestone":{"url":"https://api.github.com/repos/hyperspy/hyperspy/milestones/8","html_url":"https://github.com/hyperspy/hyperspy/milestones/0.9","labels_url":"https://api.github.com/repos/hyperspy/hyperspy/milestones/8/labels","id":856137,"number":8,"title":"0.9","description":"","creator":{"login":"francisco-dlp","id":989719,"avatar_url":"https://avatars.githubusercontent.com/u/989719?v=3","gravatar_id":"","url":"https://api.github.com/users/francisco-dlp","html_url":"https://github.com/francisco-dlp","followers_url":"https://api.github.com/users/francisco-dlp/followers","following_url":"https://api.github.com/users/francisco-dlp/following{/other_user}","gists_url":"https://api.github.com/users/francisco-dlp/gists{/gist_id}","starred_url":"https://api.github.com/users/francisco-dlp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/francisco-dlp/subscriptions","organizations_url":"https://api.github.com/users/francisco-dlp/orgs","repos_url":"https://api.github.com/users/francisco-dlp/repos","events_url":"https://api.github.com/users/francisco-dlp/events{/privacy}","received_events_url":"https://api.github.com/users/francisco-dlp/received_events","type":"User","site_admin":false},"open_issues":56,"closed_issues":28,"state":"open","created_at":"2014-11-06T15:54:23Z","updated_at":"2015-12-18T12:18:59Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786/commits","review_comments_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786/comments","review_comment_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/786/comments","statuses_url":"https://api.github.com/repos/hyperspy/hyperspy/statuses/c29ba200d35248c4b89a36dceb6d817587f76da0","head":{"label":"dnjohnstone:NEW_eds_model","ref":"NEW_eds_model","sha":"c29ba200d35248c4b89a36dceb6d817587f76da0","user":{"login":"dnjohnstone","id":10629656,"avatar_url":"https://avatars.githubusercontent.com/u/10629656?v=3","gravatar_id":"","url":"https://api.github.com/users/dnjohnstone","html_url":"https://github.com/dnjohnstone","followers_url":"https://api.github.com/users/dnjohnstone/followers","following_url":"https://api.github.com/users/dnjohnstone/following{/other_user}","gists_url":"https://api.github.com/users/dnjohnstone/gists{/gist_id}","starred_url":"https://api.github.com/users/dnjohnstone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dnjohnstone/subscriptions","organizations_url":"https://api.github.com/users/dnjohnstone/orgs","repos_url":"https://api.github.com/users/dnjohnstone/repos","events_url":"https://api.github.com/users/dnjohnstone/events{/privacy}","received_events_url":"https://api.github.com/users/dnjohnstone/received_events","type":"User","site_admin":false},"repo":{"id":29680132,"name":"hyperspy","full_name":"dnjohnstone/hyperspy","owner":{"login":"dnjohnstone","id":10629656,"avatar_url":"https://avatars.githubusercontent.com/u/10629656?v=3","gravatar_id":"","url":"https://api.github.com/users/dnjohnstone","html_url":"https://github.com/dnjohnstone","followers_url":"https://api.github.com/users/dnjohnstone/followers","following_url":"https://api.github.com/users/dnjohnstone/following{/other_user}","gists_url":"https://api.github.com/users/dnjohnstone/gists{/gist_id}","starred_url":"https://api.github.com/users/dnjohnstone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dnjohnstone/subscriptions","organizations_url":"https://api.github.com/users/dnjohnstone/orgs","repos_url":"https://api.github.com/users/dnjohnstone/repos","events_url":"https://api.github.com/users/dnjohnstone/events{/privacy}","received_events_url":"https://api.github.com/users/dnjohnstone/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dnjohnstone/hyperspy","description":"Multidimensional data analysis","fork":true,"url":"https://api.github.com/repos/dnjohnstone/hyperspy","forks_url":"https://api.github.com/repos/dnjohnstone/hyperspy/forks","keys_url":"https://api.github.com/repos/dnjohnstone/hyperspy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dnjohnstone/hyperspy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dnjohnstone/hyperspy/teams","hooks_url":"https://api.github.com/repos/dnjohnstone/hyperspy/hooks","issue_events_url":"https://api.github.com/repos/dnjohnstone/hyperspy/issues/events{/number}","events_url":"https://api.github.com/repos/dnjohnstone/hyperspy/events","assignees_url":"https://api.github.com/repos/dnjohnstone/hyperspy/assignees{/user}","branches_url":"https://api.github.com/repos/dnjohnstone/hyperspy/branches{/branch}","tags_url":"https://api.github.com/repos/dnjohnstone/hyperspy/tags","blobs_url":"https://api.github.com/repos/dnjohnstone/hyperspy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dnjohnstone/hyperspy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dnjohnstone/hyperspy/git/refs{/sha}","trees_url":"https://api.github.com/repos/dnjohnstone/hyperspy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dnjohnstone/hyperspy/statuses/{sha}","languages_url":"https://api.github.com/repos/dnjohnstone/hyperspy/languages","stargazers_url":"https://api.github.com/repos/dnjohnstone/hyperspy/stargazers","contributors_url":"https://api.github.com/repos/dnjohnstone/hyperspy/contributors","subscribers_url":"https://api.github.com/repos/dnjohnstone/hyperspy/subscribers","subscription_url":"https://api.github.com/repos/dnjohnstone/hyperspy/subscription","commits_url":"https://api.github.com/repos/dnjohnstone/hyperspy/commits{/sha}","git_commits_url":"https://api.github.com/repos/dnjohnstone/hyperspy/git/commits{/sha}","comments_url":"https://api.github.com/repos/dnjohnstone/hyperspy/comments{/number}","issue_comment_url":"https://api.github.com/repos/dnjohnstone/hyperspy/issues/comments{/number}","contents_url":"https://api.github.com/repos/dnjohnstone/hyperspy/contents/{+path}","compare_url":"https://api.github.com/repos/dnjohnstone/hyperspy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dnjohnstone/hyperspy/merges","archive_url":"https://api.github.com/repos/dnjohnstone/hyperspy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dnjohnstone/hyperspy/downloads","issues_url":"https://api.github.com/repos/dnjohnstone/hyperspy/issues{/number}","pulls_url":"https://api.github.com/repos/dnjohnstone/hyperspy/pulls{/number}","milestones_url":"https://api.github.com/repos/dnjohnstone/hyperspy/milestones{/number}","notifications_url":"https://api.github.com/repos/dnjohnstone/hyperspy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dnjohnstone/hyperspy/labels{/name}","releases_url":"https://api.github.com/repos/dnjohnstone/hyperspy/releases{/id}","created_at":"2015-01-22T13:34:00Z","updated_at":"2015-05-06T09:06:39Z","pushed_at":"2015-12-18T09:01:28Z","git_url":"git://github.com/dnjohnstone/hyperspy.git","ssh_url":"git@github.com:dnjohnstone/hyperspy.git","clone_url":"https://github.com/dnjohnstone/hyperspy.git","svn_url":"https://github.com/dnjohnstone/hyperspy","homepage":"http://www.hyperspy.org","size":71699,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"hyperspy:master","ref":"master","sha":"b511be9691489123280d193f7ee0307188cf07d7","user":{"login":"hyperspy","id":989756,"avatar_url":"https://avatars.githubusercontent.com/u/989756?v=3","gravatar_id":"","url":"https://api.github.com/users/hyperspy","html_url":"https://github.com/hyperspy","followers_url":"https://api.github.com/users/hyperspy/followers","following_url":"https://api.github.com/users/hyperspy/following{/other_user}","gists_url":"https://api.github.com/users/hyperspy/gists{/gist_id}","starred_url":"https://api.github.com/users/hyperspy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hyperspy/subscriptions","organizations_url":"https://api.github.com/users/hyperspy/orgs","repos_url":"https://api.github.com/users/hyperspy/repos","events_url":"https://api.github.com/users/hyperspy/events{/privacy}","received_events_url":"https://api.github.com/users/hyperspy/received_events","type":"Organization","site_admin":false},"repo":{"id":2233998,"name":"hyperspy","full_name":"hyperspy/hyperspy","owner":{"login":"hyperspy","id":989756,"avatar_url":"https://avatars.githubusercontent.com/u/989756?v=3","gravatar_id":"","url":"https://api.github.com/users/hyperspy","html_url":"https://github.com/hyperspy","followers_url":"https://api.github.com/users/hyperspy/followers","following_url":"https://api.github.com/users/hyperspy/following{/other_user}","gists_url":"https://api.github.com/users/hyperspy/gists{/gist_id}","starred_url":"https://api.github.com/users/hyperspy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hyperspy/subscriptions","organizations_url":"https://api.github.com/users/hyperspy/orgs","repos_url":"https://api.github.com/users/hyperspy/repos","events_url":"https://api.github.com/users/hyperspy/events{/privacy}","received_events_url":"https://api.github.com/users/hyperspy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hyperspy/hyperspy","description":"Multidimensional data analysis","fork":false,"url":"https://api.github.com/repos/hyperspy/hyperspy","forks_url":"https://api.github.com/repos/hyperspy/hyperspy/forks","keys_url":"https://api.github.com/repos/hyperspy/hyperspy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hyperspy/hyperspy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hyperspy/hyperspy/teams","hooks_url":"https://api.github.com/repos/hyperspy/hyperspy/hooks","issue_events_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/events{/number}","events_url":"https://api.github.com/repos/hyperspy/hyperspy/events","assignees_url":"https://api.github.com/repos/hyperspy/hyperspy/assignees{/user}","branches_url":"https://api.github.com/repos/hyperspy/hyperspy/branches{/branch}","tags_url":"https://api.github.com/repos/hyperspy/hyperspy/tags","blobs_url":"https://api.github.com/repos/hyperspy/hyperspy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hyperspy/hyperspy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hyperspy/hyperspy/git/refs{/sha}","trees_url":"https://api.github.com/repos/hyperspy/hyperspy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hyperspy/hyperspy/statuses/{sha}","languages_url":"https://api.github.com/repos/hyperspy/hyperspy/languages","stargazers_url":"https://api.github.com/repos/hyperspy/hyperspy/stargazers","contributors_url":"https://api.github.com/repos/hyperspy/hyperspy/contributors","subscribers_url":"https://api.github.com/repos/hyperspy/hyperspy/subscribers","subscription_url":"https://api.github.com/repos/hyperspy/hyperspy/subscription","commits_url":"https://api.github.com/repos/hyperspy/hyperspy/commits{/sha}","git_commits_url":"https://api.github.com/repos/hyperspy/hyperspy/git/commits{/sha}","comments_url":"https://api.github.com/repos/hyperspy/hyperspy/comments{/number}","issue_comment_url":"https://api.github.com/repos/hyperspy/hyperspy/issues/comments{/number}","contents_url":"https://api.github.com/repos/hyperspy/hyperspy/contents/{+path}","compare_url":"https://api.github.com/repos/hyperspy/hyperspy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hyperspy/hyperspy/merges","archive_url":"https://api.github.com/repos/hyperspy/hyperspy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hyperspy/hyperspy/downloads","issues_url":"https://api.github.com/repos/hyperspy/hyperspy/issues{/number}","pulls_url":"https://api.github.com/repos/hyperspy/hyperspy/pulls{/number}","milestones_url":"https://api.github.com/repos/hyperspy/hyperspy/milestones{/number}","notifications_url":"https://api.github.com/repos/hyperspy/hyperspy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hyperspy/hyperspy/labels{/name}","releases_url":"https://api.github.com/repos/hyperspy/hyperspy/releases{/id}","created_at":"2011-08-19T13:14:36Z","updated_at":"2015-12-15T14:25:54Z","pushed_at":"2015-12-18T11:51:01Z","git_url":"git://github.com/hyperspy/hyperspy.git","ssh_url":"git@github.com:hyperspy/hyperspy.git","clone_url":"https://github.com/hyperspy/hyperspy.git","svn_url":"https://github.com/hyperspy/hyperspy","homepage":"http://www.hyperspy.org","size":75790,"stargazers_count":51,"watchers_count":51,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":37,"mirror_url":null,"open_issues_count":138,"forks":37,"open_issues":138,"watchers":51,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786"},"html":{"href":"https://github.com/hyperspy/hyperspy/pull/786"},"issue":{"href":"https://api.github.com/repos/hyperspy/hyperspy/issues/786"},"comments":{"href":"https://api.github.com/repos/hyperspy/hyperspy/issues/786/comments"},"review_comments":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786/comments"},"review_comment":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hyperspy/hyperspy/pulls/786/commits"},"statuses":{"href":"https://api.github.com/repos/hyperspy/hyperspy/statuses/c29ba200d35248c4b89a36dceb6d817587f76da0"}}}}