def set_lines(self, lines, only_one=True, only_lines=('a', )): """Erase all Xrays lines and set them. See add_lines for details. Parameters ---------- lines : list of strings A list of valid element X-ray lines to add e.g. Fe_Kb. Additionally, if `metadata.Sample.elements` is defined, add the lines of those elements that where not given in this list. only_one: bool If False, add all the lines of each element in `metadata.Sample.elements` that has not line defined in lines. If True (default), only add the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, only the given lines will be added. Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) >>> s.set_lines(['Cu_Ka']) >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] ['Al_Ka', 'C_Ka', 'Cu_Ka', 'Mn_La', 'Zr_La'] See also -------- add_lines, add_elements, set_elements """ only_lines = utils_eds._parse_only_lines(only_lines) if "Sample.xray_lines" in self.metadata: del self.metadata.Sample.xray_lines self.add_lines(lines=lines, only_one=only_one, only_lines=only_lines)
def _get_lines_from_elements(self, elements, only_one=False, only_lines=("a", )): """Returns the X-ray lines of the given elements in spectral range of the data. Parameters ---------- elements : list of strings A list containing the symbol of the chemical elements. only_one : bool If False, add all the lines of each element in the data spectral range. If True only add the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, only the given lines will be returned. Returns ------- list of X-ray lines alphabetically sorted """ only_lines = utils_eds._parse_only_lines(only_lines) beam_energy = self._get_beam_energy() lines = [] elements = [ el if isinstance(el, str) else el.decode() for el in elements ] for element in elements: # Possible line (existing and excited by electron) element_lines = [] for subshell in list(elements_db[element]['Atomic_properties'] ['Xray_lines'].keys()): if only_lines and subshell not in only_lines: continue element_lines.append(element + "_" + subshell) element_lines = self._get_xray_lines_in_spectral_range( element_lines)[0] if only_one and element_lines: # Choose the best line select_this = -1 element_lines.sort() for i, line in enumerate(element_lines): if (self._get_line_energy(line) < beam_energy / 2): select_this = i break element_lines = [ element_lines[select_this], ] if not element_lines: _logger.info(("There is no X-ray line for element %s " % element) + "in the data spectral range") else: lines.extend(element_lines) lines.sort() return lines
def _get_lines_from_elements(self, elements, only_one=False, only_lines=("a",)): """Returns the X-ray lines of the given elements in spectral range of the data. Parameters ---------- elements : list of strings A list containing the symbol of the chemical elements. only_one : bool If False, add all the lines of each element in the data spectral range. If True only add the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, only the given lines will be returned. Returns ------- list of X-ray lines alphabetically sorted """ only_lines = utils_eds._parse_only_lines(only_lines) beam_energy = self._get_beam_energy() lines = [] elements = [el if isinstance(el, str) else el.decode() for el in elements] for element in elements: # Possible line (existing and excited by electron) element_lines = [] for subshell in list(elements_db[element]['Atomic_properties' ]['Xray_lines'].keys()): if only_lines and subshell not in only_lines: continue element_lines.append(element + "_" + subshell) element_lines = self._get_xray_lines_in_spectral_range( element_lines)[0] if only_one and element_lines: # Choose the best line select_this = -1 element_lines.sort() for i, line in enumerate(element_lines): if (self._get_line_energy(line) < beam_energy / 2): select_this = i break element_lines = [element_lines[select_this], ] if not element_lines: _logger.info( ("There is no X-ray line for element %s " % element) + "in the data spectral range") else: lines.extend(element_lines) lines.sort() return lines
def _parse_xray_lines(self, xray_lines, only_one, only_lines): only_lines = utils_eds._parse_only_lines(only_lines) xray_lines = self._get_xray_lines(xray_lines, only_one=only_one, only_lines=only_lines) xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: warnings.warn("%s is not in the data energy range." % xray + "You can remove it with" + "s.metadata.Sample.xray_lines.remove('%s')" % xray) return xray_lines
def _parse_xray_lines(self, xray_lines, only_one, only_lines): only_lines = utils_eds._parse_only_lines(only_lines) xray_lines = self._get_xray_lines(xray_lines, only_one=only_one, only_lines=only_lines) xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: warnings.warn(f"{xray} is not in the data energy range. " "You can remove it with: " f"`s.metadata.Sample.xray_lines.remove('{xray}')`") return xray_lines
def _plot_xray_lines(self, xray_lines=False, only_lines=("a", "b"), only_one=False, background_windows=None, integration_windows=None, render_figure=True): if (xray_lines is not False or background_windows is not None or integration_windows is not None): if xray_lines is False: xray_lines = True only_lines = utils_eds._parse_only_lines(only_lines) if xray_lines is True or xray_lines == 'from_elements': if ('Sample.xray_lines' in self.metadata and xray_lines != 'from_elements'): xray_lines = self.metadata.Sample.xray_lines elif 'Sample.elements' in self.metadata: xray_lines = self._get_lines_from_elements( self.metadata.Sample.elements, only_one=only_one, only_lines=only_lines) else: _logger.warning( "No elements defined, set them with `add_elements`") # No X-rays lines, nothing to do then return xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: _logger.warning(f"{xray} is not in the data energy range.") xray_lines = np.unique(xray_lines) self.add_xray_lines_markers(xray_lines, render_figure=False) if background_windows is not None: self._add_background_windows_markers(background_windows, render_figure=False) if integration_windows is not None: if integration_windows == 'auto': integration_windows = 2.0 if hasattr(integration_windows, '__iter__') is False: integration_windows = self.estimate_integration_windows( windows_width=integration_windows, xray_lines=xray_lines) self._add_vertical_lines_groups(integration_windows, linestyle='--', render_figure=False) # Render figure only at the end if render_figure: self._render_figure(plot=['signal_plot'])
def set_lines(self, lines, only_one=True, only_lines=('a',)): """Erase all Xrays lines and set them. See add_lines for details. Parameters ---------- lines : list of strings A list of valid element X-ray lines to add e.g. Fe_Kb. Additionally, if `metadata.Sample.elements` is defined, add the lines of those elements that where not given in this list. only_one: bool If False, add all the lines of each element in `metadata.Sample.elements` that has not line defined in lines. If True (default), only add the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, only the given lines will be added. Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) >>> s.set_lines(['Cu_Ka']) >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] ['Al_Ka', 'C_Ka', 'Cu_Ka', 'Mn_La', 'Zr_La'] See also -------- add_lines, add_elements, set_elements """ only_lines = utils_eds._parse_only_lines(only_lines) if "Sample.xray_lines" in self.metadata: del self.metadata.Sample.xray_lines self.add_lines(lines=lines, only_one=only_one, only_lines=only_lines)
def _plot_xray_lines(self, xray_lines=False, only_lines=("a", "b"), only_one=False, background_windows=None, integration_windows=None): if xray_lines is not False or\ background_windows is not None or\ integration_windows is not None: if xray_lines is False: xray_lines = True only_lines = utils_eds._parse_only_lines(only_lines) if xray_lines is True or xray_lines == 'from_elements': if 'Sample.xray_lines' in self.metadata \ and xray_lines != 'from_elements': xray_lines = self.metadata.Sample.xray_lines elif 'Sample.elements' in self.metadata: xray_lines = self._get_lines_from_elements( self.metadata.Sample.elements, only_one=only_one, only_lines=only_lines) else: raise ValueError( "No elements defined, set them with `add_elements`") xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: _logger.warn("%s is not in the data energy range." % xray) xray_lines = np.unique(xray_lines) self.add_xray_lines_markers(xray_lines) if background_windows is not None: self._add_background_windows_markers(background_windows) if integration_windows is not None: if integration_windows == 'auto': integration_windows = 2.0 if hasattr(integration_windows, '__iter__') is False: integration_windows = self.estimate_integration_windows( windows_width=integration_windows, xray_lines=xray_lines) self._add_vertical_lines_groups(integration_windows, linestyle='--')
def _plot_xray_lines(self, xray_lines=False, only_lines=("a", "b"), only_one=False, background_windows=None, integration_windows=None): if xray_lines is not False or\ background_windows is not None or\ integration_windows is not None: if xray_lines is False: xray_lines = True only_lines = utils_eds._parse_only_lines(only_lines) if xray_lines is True or xray_lines == 'from_elements': if 'Sample.xray_lines' in self.metadata \ and xray_lines != 'from_elements': xray_lines = self.metadata.Sample.xray_lines elif 'Sample.elements' in self.metadata: xray_lines = self._get_lines_from_elements( self.metadata.Sample.elements, only_one=only_one, only_lines=only_lines) else: _logger.warning( "No elements defined, set them with `add_elements`") # No X-rays lines, nothing to do then return xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: _logger.warning("%s is not in the data energy range." % xray) xray_lines = np.unique(xray_lines) self.add_xray_lines_markers(xray_lines) if background_windows is not None: self._add_background_windows_markers(background_windows) if integration_windows is not None: if integration_windows == 'auto': integration_windows = 2.0 if hasattr(integration_windows, '__iter__') is False: integration_windows = self.estimate_integration_windows( windows_width=integration_windows, xray_lines=xray_lines) self._add_vertical_lines_groups(integration_windows, linestyle='--')
def get_lines_intensity(self, xray_lines=None, integration_windows=2., background_windows=None, plot_result=False, only_one=True, only_lines=("a",), **kwargs): """Return the intensity map of selected Xray lines. The intensities, the number of X-ray counts, are computed by suming the spectrum over the different X-ray lines. The sum window width is calculated from the energy resolution of the detector as defined in 'energy_resolution_MnKa' of the metadata. Backgrounds average in provided windows can be subtracted from the intensities. Parameters ---------- xray_lines: {None, "best", list of string} If None, if `metadata.Sample.elements.xray_lines` contains a list of lines use those. If `metadata.Sample.elements.xray_lines` is undefined or empty but `metadata.Sample.elements` is defined, use the same syntax as `add_line` to select a subset of lines for the operation. Alternatively, provide an iterable containing a list of valid X-ray lines symbols. integration_windows: Float or array If float, the width of the integration windows is the 'integration_windows_width' times the calculated FWHM of the line. Else provide an array for which each row corresponds to a X-ray line. Each row contains the left and right value of the window. background_windows: None or 2D array of float If None, no background subtraction. Else, the backgrounds average in the windows are subtracted from the return intensities. 'background_windows' provides the position of the windows in energy. Each line corresponds to a X-ray line. In a line, the two first values correspond to the limits of the left window and the two last values correspond to the limits of the right window. plot_result : bool If True, plot the calculated line intensities. If the current object is a single spectrum it prints the result instead. only_one : bool If False, use all the lines of each element in the data spectral range. If True use only the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, use only the given lines. kwargs The extra keyword arguments for plotting. See `utils.plot.plot_signals` Returns ------- intensities : list A list containing the intensities as Signal subclasses. Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.get_lines_intensity(['Mn_Ka'], plot_result=True) Mn_La at 0.63316 keV : Intensity = 96700.00 >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.plot(['Mn_Ka'], integration_windows=2.1) >>> s.get_lines_intensity(['Mn_Ka'], >>> integration_windows=2.1, plot_result=True) Mn_Ka at 5.8987 keV : Intensity = 53597.00 >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.set_elements(['Mn']) >>> s.set_lines(['Mn_Ka']) >>> bw = s.estimate_background_windows() >>> s.plot(background_windows=bw) >>> s.get_lines_intensity(background_windows=bw, plot_result=True) Mn_Ka at 5.8987 keV : Intensity = 46716.00 See also -------- set_elements, add_elements, estimate_background_windows, plot """ only_lines = utils_eds._parse_only_lines(only_lines) xray_lines = self._get_xray_lines(xray_lines, only_one=only_one, only_lines=only_lines) xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: warnings.warn("%s is not in the data energy range." % xray + "You can remove it with" + "s.metadata.Sample.xray_lines.remove('%s')" % xray) if hasattr(integration_windows, '__iter__') is False: integration_windows = self.estimate_integration_windows( windows_width=integration_windows, xray_lines=xray_lines) intensities = [] ax = self.axes_manager.signal_axes[0] # test 1D Spectrum (0D problem) # signal_to_index = self.axes_manager.navigation_dimension - 2 for i, (Xray_line, window) in enumerate( zip(xray_lines, integration_windows)): line_energy, line_FWHM = self._get_line_energy(Xray_line, FWHM_MnKa='auto') element, line = utils_eds._get_element_and_line(Xray_line) img = self.isig[window[0]:window[1]].integrate1D(-1) if background_windows is not None: bw = background_windows[i] # TODO: test to prevent slicing bug. To be reomved when fixed indexes = [float(ax.value2index(de)) for de in list(bw) + window] if indexes[0] == indexes[1]: bck1 = self.isig[bw[0]] else: bck1 = self.isig[bw[0]:bw[1]].integrate1D(-1) if indexes[2] == indexes[3]: bck2 = self.isig[bw[2]] else: bck2 = self.isig[bw[2]:bw[3]].integrate1D(-1) corr_factor = (indexes[5] - indexes[4]) / ( (indexes[1] - indexes[0]) + (indexes[3] - indexes[2])) img -= (bck1 + bck2) * corr_factor img.metadata.General.title = ( 'X-ray line intensity of %s: %s at %.2f %s' % (self.metadata.General.title, Xray_line, line_energy, self.axes_manager.signal_axes[0].units, )) if img.axes_manager.navigation_dimension >= 2: img = img.as_image([0, 1]) elif img.axes_manager.navigation_dimension == 1: img.axes_manager.set_signal_dimension(1) if plot_result and img.axes_manager.signal_dimension == 0: print("%s at %s %s : Intensity = %.2f" % (Xray_line, line_energy, ax.units, img.data)) img.metadata.set_item("Sample.elements", ([element])) img.metadata.set_item("Sample.xray_lines", ([Xray_line])) intensities.append(img) if plot_result and img.axes_manager.signal_dimension != 0: utils.plot.plot_signals(intensities, **kwargs) return intensities
def add_lines(self, lines=(), only_one=True, only_lines=("a",)): """Add X-rays lines to the internal list. Although most functions do not require an internal list of X-ray lines because they can be calculated from the internal list of elements, ocassionally it might be useful to customize the X-ray lines to be use by all functions by default using this method. The list of X-ray lines is stored in `metadata.Sample.xray_lines` Parameters ---------- lines : list of strings A list of valid element X-ray lines to add e.g. Fe_Kb. Additionally, if `metadata.Sample.elements` is defined, add the lines of those elements that where not given in this list. If the list is empty (default), and `metadata.Sample.elements` is defined, add the lines of all those elements. only_one: bool If False, add all the lines of each element in `metadata.Sample.elements` that has not line defined in lines. If True (default), only add the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, only the given lines will be added. Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.set_microscope_parameters(beam_energy=30) >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_Ka', 'Mn_Ka', 'Zr_La'] >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) >>> s.add_lines(['Cu_Ka']) >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] ['Al_Ka', 'C_Ka', 'Cu_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] See also -------- set_lines, add_elements, set_elements """ only_lines = utils_eds._parse_only_lines(only_lines) if "Sample.xray_lines" in self.metadata: xray_lines = set(self.metadata.Sample.xray_lines) else: xray_lines = set() # Define the elements which Xray lines has been customized # So that we don't attempt to add new lines automatically elements = set() for line in xray_lines: elements.add(line.split("_")[0]) for line in lines: try: element, subshell = line.split("_") except ValueError: raise ValueError( "Invalid line symbol. " "Please provide a valid line symbol e.g. Fe_Ka") if element in elements_db: elements.add(element) if subshell in elements_db[element]['Atomic_properties' ]['Xray_lines']: lines_len = len(xray_lines) xray_lines.add(line) if lines_len != len(xray_lines): _logger.info("%s line added," % line) else: _logger.info("%s line already in." % line) else: raise ValueError( "%s is not a valid line of %s." % (line, element)) else: raise ValueError( "%s is not a valid symbol of an element." % element) xray_not_here = self._get_xray_lines_in_spectral_range(xray_lines)[1] for xray in xray_not_here: warnings.warn("%s is not in the data energy range." % xray) if "Sample.elements" in self.metadata: extra_elements = (set(self.metadata.Sample.elements) - elements) if extra_elements: new_lines = self._get_lines_from_elements( extra_elements, only_one=only_one, only_lines=only_lines) if new_lines: self.add_lines(list(new_lines) + list(lines)) self.add_elements(elements) if not hasattr(self.metadata, 'Sample'): self.metadata.add_node('Sample') if "Sample.xray_lines" in self.metadata: xray_lines = xray_lines.union( self.metadata.Sample.xray_lines) self.metadata.Sample.xray_lines = sorted(list(xray_lines))
def get_lines_intensity(self, xray_lines=None, integration_windows=2., background_windows=None, plot_result=False, only_one=True, only_lines=("a", ), **kwargs): """Return the intensity map of selected Xray lines. The intensities, the number of X-ray counts, are computed by suming the spectrum over the different X-ray lines. The sum window width is calculated from the energy resolution of the detector as defined in 'energy_resolution_MnKa' of the metadata. Backgrounds average in provided windows can be subtracted from the intensities. Parameters ---------- xray_lines: {None, "best", list of string} If None, if `metadata.Sample.elements.xray_lines` contains a list of lines use those. If `metadata.Sample.elements.xray_lines` is undefined or empty but `metadata.Sample.elements` is defined, use the same syntax as `add_line` to select a subset of lines for the operation. Alternatively, provide an iterable containing a list of valid X-ray lines symbols. integration_windows: Float or array If float, the width of the integration windows is the 'integration_windows_width' times the calculated FWHM of the line. Else provide an array for which each row corresponds to a X-ray line. Each row contains the left and right value of the window. background_windows: None or 2D array of float If None, no background subtraction. Else, the backgrounds average in the windows are subtracted from the return intensities. 'background_windows' provides the position of the windows in energy. Each line corresponds to a X-ray line. In a line, the two first values correspond to the limits of the left window and the two last values correspond to the limits of the right window. plot_result : bool If True, plot the calculated line intensities. If the current object is a single spectrum it prints the result instead. only_one : bool If False, use all the lines of each element in the data spectral range. If True use only the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, use only the given lines. kwargs The extra keyword arguments for plotting. See `utils.plot.plot_signals` Returns ------- intensities : list A list containing the intensities as BaseSignal subclasses. Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.get_lines_intensity(['Mn_Ka'], plot_result=True) Mn_La at 0.63316 keV : Intensity = 96700.00 >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.plot(['Mn_Ka'], integration_windows=2.1) >>> s.get_lines_intensity(['Mn_Ka'], >>> integration_windows=2.1, plot_result=True) Mn_Ka at 5.8987 keV : Intensity = 53597.00 >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.set_elements(['Mn']) >>> s.set_lines(['Mn_Ka']) >>> bw = s.estimate_background_windows() >>> s.plot(background_windows=bw) >>> s.get_lines_intensity(background_windows=bw, plot_result=True) Mn_Ka at 5.8987 keV : Intensity = 46716.00 See also -------- set_elements, add_elements, estimate_background_windows, plot """ only_lines = utils_eds._parse_only_lines(only_lines) xray_lines = self._get_xray_lines(xray_lines, only_one=only_one, only_lines=only_lines) xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: warnings.warn("%s is not in the data energy range." % xray + "You can remove it with" + "s.metadata.Sample.xray_lines.remove('%s')" % xray) if hasattr(integration_windows, '__iter__') is False: integration_windows = self.estimate_integration_windows( windows_width=integration_windows, xray_lines=xray_lines) intensities = [] ax = self.axes_manager.signal_axes[0] # test Signal1D (0D problem) # signal_to_index = self.axes_manager.navigation_dimension - 2 for i, (Xray_line, window) in enumerate(zip(xray_lines, integration_windows)): line_energy, line_FWHM = self._get_line_energy(Xray_line, FWHM_MnKa='auto') element, line = utils_eds._get_element_and_line(Xray_line) img = self.isig[window[0]:window[1]].integrate1D(-1) if np.issubdtype(img.data.dtype, np.integer): # The operations below require a float dtype with the default # numpy casting rule ('same_kind') img.change_dtype("float") if background_windows is not None: bw = background_windows[i] # TODO: test to prevent slicing bug. To be reomved when fixed indexes = [ float(ax.value2index(de)) for de in list(bw) + window ] if indexes[0] == indexes[1]: bck1 = self.isig[bw[0]] else: bck1 = self.isig[bw[0]:bw[1]].integrate1D(-1) if indexes[2] == indexes[3]: bck2 = self.isig[bw[2]] else: bck2 = self.isig[bw[2]:bw[3]].integrate1D(-1) corr_factor = (indexes[5] - indexes[4]) / ( (indexes[1] - indexes[0]) + (indexes[3] - indexes[2])) img = img - (bck1 + bck2) * corr_factor img.metadata.General.title = ( 'X-ray line intensity of %s: %s at %.2f %s' % ( self.metadata.General.title, Xray_line, line_energy, self.axes_manager.signal_axes[0].units, )) img.axes_manager.set_signal_dimension(0) if plot_result and img.axes_manager.navigation_size == 1: print("%s at %s %s : Intensity = %.2f" % (Xray_line, line_energy, ax.units, img.data)) img.metadata.set_item("Sample.elements", ([element])) img.metadata.set_item("Sample.xray_lines", ([Xray_line])) intensities.append(img) if plot_result and img.axes_manager.navigation_size != 1: utils.plot.plot_signals(intensities, **kwargs) return intensities
def add_lines(self, lines=(), only_one=True, only_lines=("a", )): """Add X-rays lines to the internal list. Although most functions do not require an internal list of X-ray lines because they can be calculated from the internal list of elements, ocassionally it might be useful to customize the X-ray lines to be use by all functions by default using this method. The list of X-ray lines is stored in `metadata.Sample.xray_lines` Parameters ---------- lines : list of strings A list of valid element X-ray lines to add e.g. Fe_Kb. Additionally, if `metadata.Sample.elements` is defined, add the lines of those elements that where not given in this list. If the list is empty (default), and `metadata.Sample.elements` is defined, add the lines of all those elements. only_one: bool If False, add all the lines of each element in `metadata.Sample.elements` that has not line defined in lines. If True (default), only add the line at the highest energy above an overvoltage of 2 (< beam energy / 2). only_lines : {None, list of strings} If not None, only the given lines will be added. Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.set_microscope_parameters(beam_energy=30) >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_Ka', 'Mn_Ka', 'Zr_La'] >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.add_lines() >>> print(s.metadata.Sample.xray_lines) >>> s.add_lines(['Cu_Ka']) >>> print(s.metadata.Sample.xray_lines) ['Al_Ka', 'C_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] ['Al_Ka', 'C_Ka', 'Cu_Ka', 'Cu_La', 'Mn_La', 'Zr_La'] See also -------- set_lines, add_elements, set_elements """ only_lines = utils_eds._parse_only_lines(only_lines) if "Sample.xray_lines" in self.metadata: xray_lines = set(self.metadata.Sample.xray_lines) else: xray_lines = set() # Define the elements which Xray lines has been customized # So that we don't attempt to add new lines automatically elements = set() for line in xray_lines: elements.add(line.split("_")[0]) for line in lines: try: element, subshell = line.split("_") except ValueError: raise ValueError( "Invalid line symbol. " "Please provide a valid line symbol e.g. Fe_Ka") if element in elements_db: elements.add(element) if subshell in elements_db[element]['Atomic_properties'][ 'Xray_lines']: lines_len = len(xray_lines) xray_lines.add(line) if lines_len != len(xray_lines): _logger.info("%s line added," % line) else: _logger.info("%s line already in." % line) else: raise ValueError("%s is not a valid line of %s." % (line, element)) else: raise ValueError("%s is not a valid symbol of an element." % element) xray_not_here = self._get_xray_lines_in_spectral_range(xray_lines)[1] for xray in xray_not_here: warnings.warn("%s is not in the data energy range." % xray) if "Sample.elements" in self.metadata: extra_elements = (set(self.metadata.Sample.elements) - elements) if extra_elements: new_lines = self._get_lines_from_elements( extra_elements, only_one=only_one, only_lines=only_lines) if new_lines: self.add_lines(list(new_lines) + list(lines)) self.add_elements(elements) if not hasattr(self.metadata, 'Sample'): self.metadata.add_node('Sample') if "Sample.xray_lines" in self.metadata: xray_lines = xray_lines.union(self.metadata.Sample.xray_lines) self.metadata.Sample.xray_lines = sorted(list(xray_lines))
def plot(self, xray_lines=False, only_lines=("a", "b"), only_one=False, background_windows=None, integration_windows=None, **kwargs): """ Plot the EDS spectrum. The following markers can be added - The position of the X-ray lines and their names. - The background windows associated with each X-ray lines. A black line links the left and right window with the average value in each window. Parameters ---------- xray_lines: {False, True, 'from_elements', list of string} If not False, indicate the position and the name of the X-ray lines. If True, if `metadata.Sample.elements.xray_lines` contains a list of lines use those. If `metadata.Sample.elements.xray_lines` is undefined or empty or if xray_lines equals 'from_elements' and `metadata.Sample.elements` is defined, use the same syntax as `add_line` to select a subset of lines for the operation. Alternatively, provide an iterable containing a list of valid X-ray lines symbols. only_lines : None or list of strings If not None, use only the given lines (eg. ('a','Kb')). If None, use all lines. only_one : bool If False, use all the lines of each element in the data spectral range. If True use only the line at the highest energy above an overvoltage of 2 (< beam energy / 2). background_windows: None or 2D array of float If not None, add markers at the position of the windows in energy. Each line corresponds to a X-ray lines. In a line, the two first value corresponds to the limit of the left window and the two last values corresponds to the limit of the right window. integration_windows: None or 'auto' or float or 2D array of float If not None, add markers at the position of the integration windows. If 'auto' (or float), the width of the integration windows is 2.0 (or float) times the calculated FWHM of the line. see 'estimate_integration_windows'. Else provide an array for which each row corresponds to a X-ray line. Each row contains the left and right value of the window. kwargs The extra keyword arguments for plot() Examples -------- >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.plot() >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.plot(True) >>> s = hs.datasets.example_signals.EDS_TEM_Spectrum() >>> s.add_lines() >>> bw = s.estimate_background_windows() >>> s.plot(background_windows=bw) >>> s = hs.datasets.example_signals.EDS_SEM_Spectrum() >>> s.plot(['Mn_Ka'], integration_windows='auto') >>> s = hs.datasets.example_signals.EDS_TEM_Spectrum() >>> s.add_lines() >>> bw = s.estimate_background_windows() >>> s.plot(background_windows=bw, integration_windows=2.1) See also -------- set_elements, add_elements, estimate_integration_windows, get_lines_intensity, estimate_background_windows """ super(EDSSpectrum, self).plot(**kwargs) if xray_lines is not False or\ background_windows is not None or\ integration_windows is not None: if xray_lines is False: xray_lines = True only_lines = utils_eds._parse_only_lines(only_lines) if xray_lines is True or xray_lines == 'from_elements': if 'Sample.xray_lines' in self.metadata \ and xray_lines != 'from_elements': xray_lines = self.metadata.Sample.xray_lines elif 'Sample.elements' in self.metadata: xray_lines = self._get_lines_from_elements( self.metadata.Sample.elements, only_one=only_one, only_lines=only_lines) else: raise ValueError( "No elements defined, set them with `add_elements`") xray_lines, xray_not_here = self._get_xray_lines_in_spectral_range( xray_lines) for xray in xray_not_here: print("Warning: %s is not in the data energy range." % xray) xray_lines = np.unique(xray_lines) self._add_xray_lines_markers(xray_lines) if background_windows is not None: self._add_background_windows_markers(background_windows) if integration_windows is not None: if integration_windows == 'auto': integration_windows = 2.0 if hasattr(integration_windows, '__iter__') is False: integration_windows = self.estimate_integration_windows( windows_width=integration_windows, xray_lines=xray_lines) self._add_vertical_lines_groups(integration_windows, linestyle='--')