Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions doc/user_guide/eels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,49 @@ edge functionalities:
* :py:meth:`~.models.EELSModel.fix_fine_structure`
* :py:meth:`~.models.EELSModel.free_fine_structure`

.. _eels.absolute_quantification:

Absolute quantification
^^^^^^^^^^^^^^^^^^^^^^^

The fitted :py:attr:`~.components.EELSCLEdge.intensity` parameter is the
scaling factor applied to the theoretical cross-section (in
barns/eV/atom). When the model is convolved with a **raw** low-loss
spectrum (i.e. in counts, not normalised), the fitted intensity is
directly proportional to the areal density :math:`N` of the element:

.. math::

\text{intensity} = N \times 10^{-10}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain where does this 1e-10 come from?


where :math:`N` is in atoms/nm². Therefore, to obtain the areal density:

.. code-block:: python

>>> N_B = m.components.B_K.intensity.value * 1e10
>>> N_N = m.components.N_K.intensity.value * 1e10

.. warning::

If the core-loss and low-loss spectra were acquired with different
dwell times (or total acquisition times), the fitted intensity must
be corrected by the ratio of the dwell times before converting to
areal density. The dwell time can often be found in the signal
metadata (e.g.
:py:attr:`~hyperspy.api.signals.BaseSignal.metadata`\ ``.Acquisition_instrument.TEM.Detector.EELS.dwell_time``).
The corrected conversion is:

.. math::

N = \text{intensity} \times \frac{t_{\text{core}}}{t_{\text{low}}} \times 10^{10}

where :math:`t_{\text{core}}` and :math:`t_{\text{low}}` are the
dwell times of the core-loss and low-loss spectra, respectively.

If the low-loss spectrum has been normalised (e.g. to sum to unity)
before convolution, the zero-loss intensity factor is removed and
the simple relationship above no longer holds.

.. _eels.fine_structure:

Fine structure analysis using a spline function
Expand Down
10 changes: 10 additions & 0 deletions examples/model_fitting/EELS_curve_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@
m.enable_fine_structure()
m.multifit(kind="smart")

# %%
# Print the fitted intensities and convert them to areal density
# (atoms/nm²). This assumes the low-loss spectrum was passed *raw*
# (counts) and that both spectra share the same dwell time.

m.quantify()
for edge in m.edges:
N = edge.intensity.value * 1e10
print(f"{edge.name}: {N:.3e} atoms/nm²")

# %%
# Plot the model fit result
m.plot()
8 changes: 7 additions & 1 deletion exspy/_docstrings/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@
If an EELSSpectrum is provided, it will be assumed that it is
a low-loss EELS spectrum, and it will be used to simulate the
effect of multiple scattering by convolving it with the EELS
spectrum.
spectrum. For absolute quantification (areal density in
atoms/nm²), the low-loss spectrum should be the *raw* (i.e.
unnormalised) spectrum in counts. If the core-loss and low-loss
spectra were acquired with different dwell times, the fitted
``intensity`` must be corrected by the ratio of dwell times
before conversion to areal density. See
:attr:`~exspy.components.EELSCLEdge.intensity` for details.
auto_background : bool
If True, and if spectrum is an EELS instance adds automatically
a powerlaw to the model and estimate the parameters by the
Expand Down
17 changes: 17 additions & 0 deletions exspy/_misc/eels/base_gos.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,23 @@ def as_dictionary(self, fullcopy=True):
return dic

def integrateq(self, onset_energy, angle, E0):
"""Integrate the GOS over q to obtain the energy-differential cross-section.

Parameters
----------
onset_energy : float
The edge onset energy in eV.
angle : float
The effective collection semi-angle in rad.
E0 : float
The electron beam energy in keV.

Returns
-------
scipy.interpolate.BSpline
A spline representing the energy-differential cross-section
in barns/eV/atom as a function of energy loss in eV.
"""
a0 = scipy.constants.value("Bohr radius")
R = scipy.constants.value("Rydberg constant times hc in eV")

Expand Down
17 changes: 17 additions & 0 deletions exspy/_misc/eels/hydrogenic_gos.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,23 @@ def __init__(self, element_subshell):
_logger.info(info_str)

def integrateq(self, onset_energy, angle, E0):
"""Integrate the GOS over q to obtain the energy-differential cross-section.

Parameters
----------
onset_energy : float
The edge onset energy in eV.
angle : float
The effective collection semi-angle in rad.
E0 : float
The electron beam energy in keV.

Returns
-------
scipy.interpolate.BSpline
A spline representing the energy-differential cross-section
in barns/eV/atom as a function of energy loss in eV.
"""
energy_shift = onset_energy - self.onset_energy
self.energy_shift = energy_shift
gamma = 1 + E0 / 511.06
Expand Down
32 changes: 28 additions & 4 deletions exspy/components/_eels_cl_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,34 @@ class EELSCLEdge(Component):
onset_energy : Parameter
The edge onset position
intensity : Parameter
The factor by which the cross section is multiplied, what in
favourable cases is proportional to the number of atoms of
the element. It is a component.Parameter instance.
It is fixed by default.
The factor by which the cross section is multiplied. The
internal cross-section is given in barns per eV per atom,
so ``intensity`` is proportional to the areal density of
atoms. When the model is convolved with a *raw* low-loss
spectrum (i.e. counts), the fitted ``intensity`` equals
:math:`N \times 10^{-10}`, where :math:`N` is the areal
density in atoms/nm². Consequently:
Comment on lines +108 to +110

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image


.. code-block:: python

N_atoms_per_nm2 = edge.intensity.value * 1e10

If the core-loss and low-loss spectra were acquired with
different dwell times (or total acquisition times), the
ratio of the dwell times must be accounted for:

.. code-block:: python

N_atoms_per_nm2 = edge.intensity.value * (t_core / t_low) * 1e10

where ``t_core`` and ``t_low`` are the dwell times of the
core-loss and low-loss spectra, respectively. Note that
if the low-loss spectrum is normalised (e.g. summed to
unity) before convolution, the zero-loss intensity factor
is removed and the simple relationship above no longer
holds.

It is a component.Parameter instance. It is fixed by default.
effective_angle : Parameter
The effective collection semi-angle. It is automatically
calculated by ``set_microscope_parameters``. It is a
Expand Down
16 changes: 15 additions & 1 deletion exspy/models/eelsmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,21 @@ def _fit_edge(self, edgenumber, start_energy=None, **kwargs):

def quantify(self):
"""Prints the value of the intensity of all the independent
active EELS core loss edges defined in the model
active EELS core loss edges defined in the model.

The printed ``intensity`` is the fitted scaling factor of the
cross-section (in barns/eV/atom). When the model has been
convolved with a *raw* low-loss spectrum, this value is
proportional to the areal density :math:`N` (atoms/nm²) via
:math:`\\text{intensity} = N \\times 10^{-10}`. See the
:attr:`~.components.EELSCLEdge.intensity` attribute for the
full conversion formula, including the dwell-time correction
Comment thread
ericpre marked this conversation as resolved.
required when the core-loss and low-loss acquisition times
differ.

See Also
--------
:attr:`exspy.components.EELSCLEdge.intensity`

"""
elements = {}
Expand Down
Loading