Introduction to pyEELSMODEL
In this notebook, a small overview is given on the different functionalities of pyEELSMODEL. The other notebooks are more in-depth on how to use the model-based approach for elemental quantification.
[1]:
%matplotlib qt
#important for interactive plotting tools
[2]:
import pyEELSMODEL.api as em
import os
import numpy as np
import matplotlib.pyplot as plt
Loading and Saving Data
Spectrum loading
Many function contain docstrings, one can access the documentation via following command
[3]:
em.Spectrum.load?
Signature: em.Spectrum.load(filename, flip_sign=False, **kwargs)
Docstring:
Loads different types of data. The possible datatypes are: .hdf5,
.hspy, .dm3/.dm4 and .msa.
Parameters
----------
filename : string
String of the filename where the extention should be '.hdf5'
flip_sign: boolean
Indicates whether the offset value should be negative when loading
a .dm file. (default: False)
Returns
-------
s: Spectrum
The spectrum which is contained in the filename
File: c:\users\djannis\pycharmprojects\project\pyeelsmodel\pyeelsmodel\core\spectrum.py
Type: method
[4]:
filename = os.path.join('data', 'hl.msa')
s = em.Spectrum.load(filename)
this will change the size and contents of the current spectrum
[5]:
s.plot()
A spectrum can also be created by first creating a SpectrumShape which needs to know the dispersion, offset and size. Next, the Spectrum needs to have the Spectrumshape and raw data
[6]:
dispersion = 1
offset = 100
size = 100
data = np.random.random(size)
specshape = em.Spectrumshape(dispersion, offset, size)
s = em.Spectrum(specshape, data)
[7]:
s.plot()
When the energy axis is given and the raw data is available, the following code can be used to create a spectrum
[8]:
energy_axis = dispersion*np.arange(size)+offset
data = np.random.random(size)
s = em.Spectrum.from_numpy(data, energy_axis)
[9]:
s.plot()
Spectrum saving
The spectrum can be saved as .hdf5.
[10]:
savename = r'.\data\hl_test.hdf5'
s.save_hdf5(savename)
[10]:
False
[11]:
p = em.Spectrum.load(savename)
[12]:
p.plot()
MultiSpectrum loading
[13]:
filename = os.path.join('data', 'multi.hdf5')
s = em.MultiSpectrum.load(filename)
[14]:
#shows the current selected spectrum
print('current spectrum id is: ' + str(s.currentspectrumid))
s.plot()
current spectrum id is: (0, 0)
[15]:
s.setcurrentspectrum((2,1))
[16]:
print('current spectrum id is: ' + str(s.currentspectrumid))
s.plot()
current spectrum id is: (2, 1)
[17]:
#plots the average spectrum
s.mean().plot()
A multispectrum can also be created by having a MultiSpectrumshape object together with the raw data
[18]:
dispersion = 1
offset = 100
size = 100
xsize = 4
ysize = 2
data = np.random.random((xsize, ysize, size))
m_specshape = em.MultiSpectrumshape(dispersion, offset, size, xsize, ysize)
s = em.MultiSpectrum(m_specshape, data)
Or from a numpy array and energy axis
[19]:
energy_axis = dispersion*np.arange(size)+offset
data = np.random.random((xsize, ysize, size))
s = em.MultiSpectrum.from_numpy(data, energy_axis)
MultiSpectrum saving
[20]:
s.save_hdf5?
Signature: s.save_hdf5(filename, metadata=None, overwrite=False)
Docstring:
Saves the spectrum as a hdf5 file. The structure of the file can easily
be investigated via a hdfview software.
Parameters
----------
filename : string
filename of the saved file.
metadata: dictionary
A dictionary containing E0, alpha, beta, elements and edges can be
added to the hdf5 file. If None is given, nothing will be saved.
overwrite: boolean
Indicates if the file will be overwritten if it already exists.
(default: False)
Returns
-------
If the file saving workes, a True value is returned.
File: c:\users\djannis\pycharmprojects\project\pyeelsmodel\pyeelsmodel\core\multispectrum.py
Type: method
[21]:
savename = os.path.join('data', 'multi_test.hdf5')
s.save_hdf5(savename)
not overwriting
[21]:
False
STEM-EELS Simulation
In this part, a simulated STEM-EELS map is created which will be used to showcase the different functionalities of pyEELSMODEL. It contains a low-loss and core-loss. The core-loss has carbon K edge, nitrogen K edge, oxygen K edge and iron L edge and has a scan size of 128x128. In the CoreLossExampleExtended notebook, the procedure of simulating this multispectrum is shown.
[22]:
from pyEELSMODEL.misc.data_simulator import simulate_data
[23]:
hl, ll = simulate_data()
16384it [00:13, 1216.67it/s]
16384it [00:37, 439.14it/s]
16384it [00:00, 38290.51it/s]
Multispectrum is simulated
Aligning multispectra
The core-loss gets aligned using the low-loss. Multiple methods are available for find the appropriate shifts and correct for it.
FastAlignZeroLoss: Uses the energy at which the maximum intensity is measured. The found shifts are applied by rolling each spectra to make it align. This method is fast and does not modify the inputted data via interpolation. Hence it cannot find subpixel positions. This method works best when the zero-loss peak is sharp and has a high intensity which is valid in most cases. In our experience, this method works really well for elemental quantification.
AlignZeroLoss: Fits a model to the zero-loss peak where the model is a Gaussian or Lorentzian function which needs to be specified by the user. This method is a lot slower and can be unstable due to its non-linear fitting procedure but has the potential to correct for subpixel shifts and works for a noisy and not sharp zero-loss peak.
AlignCrossCorrelation: Finds the shift which gives the best similarity between two spectra by cross correlation the two spectra. Subpixel accuracy can be obtained via interpolating the experimental data and finding the shift. This method is generally faster than the AlignZeroLoss but could fail if the low loss spectra are not very similar to each other. This method can also be used to align core-loss signal when no low-loss is available.
[24]:
fast_align = em.FastAlignZeroLoss(ll, other_spectra=[hl], cropping=True)
align = em.AlignZeroLoss(ll, other_spectra=[hl], cropping=True)
cros_align = em.AlignCrossCorrelation(ll, other_spectra=[hl], cropping=True, is_zlp=True)
[25]:
print('Start using FastAlignZeroLoss object')
fast_align.perform_alignment()
print('Stop using FastAlignZeroLoss object')
print('Start using AlignZeroLoss object')
align.perform_alignment()
print('Stop using AlignZeroLoss object')
print('Start using AlignCrossCorrelation object')
cros_align.perform_alignment()
print('Stop using AlignCrossCorrelation object')
Start using FastAlignZeroLoss object
Stop using FastAlignZeroLoss object
Start using AlignZeroLoss object
Estimates the parameters for the fitting procedure
16384it [00:43, 378.53it/s]
16384it [00:03, 4392.15it/s]
Stop using AlignZeroLoss object
Start using AlignCrossCorrelation object
16384it [00:08, 2012.52it/s]
16384it [00:03, 4525.90it/s]
Stop using AlignCrossCorrelation object
The applied shift can be visualized
[26]:
fig = fast_align.show_shift()
The average zero-loss peak can be shown to see if it indeed improved the sharpness. If this is not the case then something went wrong during the alignment procedure
[27]:
fig = fast_align.show_alignment_result()
[28]:
#redefining the new alignemet multispectra
hl_al = fast_align.aligned_others[0]
ll_al = fast_align.aligned
Operations on Spectrum and MultiSpectrum
Some simple operations which can be performed on spectra and multispectra are shown
The raw data and energy axis can be accessed by following command
[ ]:
raw_data = hl_al[0,0].data
print('raw data')
print(raw_data)
energy_axis = hl_al.energy_axis
print('energy axis')
print(energy_axis)
Get the aveage spectrum of the multispectrum
[ ]:
spec = hl_al.mean()
Get the index of the 315 eV energy position.
[ ]:
index = hl_al.get_energy_index(315)
print('The index of the 315 eV energy loss is: '+str(index))
Gaussian smooting of the spectrum, the boundaries will not resemble reality since the convolution is performed with FFT which assumes symmetric boundary conditions.
[ ]:
smth = hl_al[10,10,:].gaussiansmooth(2)
fig, ax = plt.subplots()
ax.plot(hl_al.energy_axis, hl_al[10,10].data, label='Raw')
ax.plot(smth.energy_axis, smth.data, label='Smoothed')
ax.legend()
The multispectrum object can be sliced to select a part of the multispectrum. Slicing is not implemented for the energy direction.
[ ]:
sub = hl_al[4:10, 3:8, :]
print('x size of the multispectrum is: '+str(sub.xsize))
print('y size of the multispectrum is: '+str(sub.ysize))
sub.mean().plot()
To get a subpart of the energy axis, following function can be used
[ ]:
sub = hl_al.get_interval((500.,900.))
print('Start of the energy axis is: '+str(sub.offset))
sub.mean().plot()
Integrating the multispectrum over a certain energy range
[ ]:
integral = hl_al.integrate((600,800))
fig, ax = plt.subplots()
ax.imshow(integral)
Rebinning on the multispectrum can be performed. The rebinning factors should be positive integers since a kernel of rebinning size is used to perform the rebinning
[ ]:
rb = (4,2,2)
hl_rb = hl_al.rebin(rb)
[ ]:
print('new xsize is: ' +str(hl_rb.xsize))
print('new ysize is: ' +str(hl_rb.ysize))
print('new Esize is: ' +str(hl_rb.size))
[ ]:
hl_rb.mean().plot()
Visualization methods
Some classes are defined which provide some live user input to navigate through the data.
The em.MultiSpectrumVisualizer object gives the ability to navigate through a MultiSpectrum.
The arrows on the keyboard are used to change the rectangle position.
‘+’ key increases the area on which to visualize the spectrum and takes the average.
‘-’ key decreases the area
Mouse can be used to drag the rectangle over multispectrum by left mouse clicking inside the rectangle holding it and dragging
[29]:
#shows one multispectrum
em.MultiSpectrumVisualizer([hl_al], labels=['Experimental data'])
[29]:
<pyEELSMODEL.operators.multispectrumvisualizer.MultiSpectrumVisualizer at 0x1f7b8572b00>
[ ]:
#multiple multispectra with the same xsize, ysize can be shown.
em.MultiSpectrumVisualizer([ll_al, hl_al], labels=['low loss', 'core loss'], logscale=True)
A line spectrum can be made from the multispectrum by averaging along one axis
[ ]:
hl_line = hl_al.mean(1)
[ ]:
em.MultiSpectrumVisualizer([hl_line], labels=['line spectrum'])
The em.AreaSelection object gives the ability to draw shapes on the map and extract the average spectrum from that area. The right mouse click is used to select the points of the area
[ ]:
area = em.AreaSelection(hl_al, max_points=5, other_spectra=[ll_al])
The .determine_input_area() shows the image on which to select a region.
[ ]:
area.determine_input_area()
[ ]:
#shows the drawn area
area.show_area()
[ ]:
spec_mean = area.get_mean_from_area()
[ ]:
#the average spectrum of the added multispectra
ll_mean = area.other_avg_spec[0]
[ ]:
spec_mean.plot()
[ ]:
ll_mean.plot()
Background Removal
In this part a small example is shown on how to remove the background from a multispectrum. The BackgroundRemoval class has a workflow implemented on how to get the appropriate results. Multiple methods of background subtraction are implemented but in this case we focus on the power-law only.
[ ]:
em.BackgroundRemoval?
[ ]:
back = em.BackgroundRemoval(hl_al, (600,700)) #before the iron L edge
[ ]:
rem = back.calculate_multi() #rem is the background removed spectrum
[ ]:
fig = back.show_fit_result(use_mean=True)
[ ]:
em.MultiSpectrumVisualizer([hl_al, rem, back.multi_model_signal], labels=['Raw spectrum', 'Background subtracted', 'Background'])
Model-based Quantification
The last part shows how to get the elemental quantification on the multispectrum. It uses the ElementalQuantification class which has a workflow defined which only needs information on the estimated elements, acceleration voltage, convergence angle and collection angle. See other notebooks for more in-depth examples on how to perform model-based quantification.
[ ]:
elements = ['C', 'N', 'O', 'Fe']
edges = ['K', 'K', 'K', 'L']
E0 = 300e3
alpha = 1e-9
beta = 20e-3
settings = (E0, alpha, beta)
[ ]:
quant = em.ElementalQuantification(hl, elements, edges, settings, ll=ll)
quant.n_bgterms = 4
quant.linear_fitter_method = 'ols'
quant.do_procedure()
[ ]:
#calculate different models with different components added to it.
multimodels = quant.get_multimodels()
[ ]:
#compare the fitted model to experimental data
em.MultiSpectrumVisualizer([quant.spectrum, multimodels[-1]])
[ ]:
quant.show_elements_maps()