Source code for espm.models.absorption_edxs

r"""
Absorption functions
--------------------

The :mod:`espm.models.absorption_edxs` module implements the functions to calculate the contribution of absorption in edx spectra.

"""

from scipy.interpolate import interp1d
from espm.utils import number_to_symbol_dict, atomic_to_weight_dict, approx_density
from espm.conf import HSPY_MAC
import numpy as np
from pathlib import Path
from espm.conf import DB_PATH

[docs] @number_to_symbol_dict def absorption_coefficient (x,atomic_fraction = False,*,elements_dict = {"Si" : 1.0}) : r""" Calculate the mass-absorption coefficent of a given composition at a certain energy or over a certain energy range. Parameters ---------- x : :np.array 1D: Energy value or energy scale over which the mass-absorption coefficient is calculated. atomic_fraction : :boolean: If True, the atomic concentrations of elements_dict are converted in atomic weight fractions. elements_dict : :dict: Dictionnary with chemical elements as keys and atomic weight fractions (or atomic concentrations) as values. Returns ------- mass-absorption coefficient :np.array 1D: Calculated value of the mass-absorption coefficient on all the energy range. Notes ----- The mass-absorption coefficients are calculated using the database of hyperspy :cite:p:`francisco_de_la_pena_2022_7263263`. """ mu = np.zeros_like(x) if atomic_fraction : elements_dict = atomic_to_weight_dict(elements_dict = elements_dict) sum_elts = sum(elements_dict.values()) for key in elements_dict.keys() : x_db = HSPY_MAC[key]["energies (keV)"] y_db = HSPY_MAC[key]["mass_absorption_coefficient (cm2/g)"] interp_func = interp1d(x_db,y_db,kind="cubic") mu += elements_dict[key]*interp_func(x)/sum_elts if len(elements_dict.keys()) == 0 : mu = 1 / np.power(x,3) return mu
[docs] def absorption_correction (x,thickness = 100e-7,toa = 90,density = None,atomic_fraction = False,*,elements_dict = {"Si" : 1.0},**kwargs) : r""" Calculate the contribution of the absorption on the EDX spectrum for a thin slab of material with a given composition at a certain energy or over a certain energy range. The absorption correction is calculated using the following formula : .. math:: \frac{1 - \exp(-\chi)}{\chi} where :math:`\chi = \mu \rho t / \sin(\theta)`, :math:`\mu` is the mass-absorption coefficient, :math:`\rho` is the density of the material, :math:`t` is the thickness of the sample and :math:`\theta` is the average take-off angle of the x-rays travelling from the sample to the x-ray detectors. Parameters ---------- x : :np.array 1D: Energy value or energy scale over which the mass-absorption coefficient is calculated. thickness : :float: Thickness of the material slab in meter. If the thickness is set to 0.0, the function will return 1.0. toa : :float: Take-off angle in degrees of the x-rays travelling from the sample to the x-ray detectors. density : :float: Density of the material in g/m3 (to be checked). If None, an approximation of the density based on the atomic numbers of the elements of the materials is calculated. atomic_fraction : :boolean: If True, the atomic concentrations of elements_dict are converted in atomic weight fractions. elements_dict : :dict: Dictionnary with chemical elements as keys and atomic weight fractions (or atomic concentrations) as values. Returns ------- absorption correction :np.array 1D: Calculated value of the absorption correction on all the energy range. """ mu = absorption_coefficient(x,atomic_fraction,elements_dict = elements_dict) rad_toa = np.deg2rad(toa) if density is None : density = approx_density(atomic_fraction,elements_dict = elements_dict) if thickness == 0 : return 1.0 else : chi = mu*density*thickness/np.sin(rad_toa) return (1 - np.exp(-chi))/chi
[docs] def absorption_mass_thickness(x,mass_thickness, toa = 90, atomic_fraction = True, *, elements_dict = {"Si" : 1.0}) : r""" Calculate the contribution of the absorption of a mass-thickness map. Parameters ---------- x : :np.array 1D: Energy value or energy scale over which the mass-absorption coefficient is calculated. mass_thickness: :np.array 2D: Mass-thickness map. See the notes for details. toa : :float: Take-off angle in degrees of the x-rays travelling from the sample to the x-ray detectors. atomic_fraction : :boolean: If True, the atomic concentrations of elements_dict are converted in atomic weight fractions. elements_dict : :dict: Dictionnary with chemical elements as keys and atomic weight fractions (or atomic concentrations) as values. Returns ------- absorption correction :np.array 3D: Calculated value of the absorption correction on all the energy range for all pixels of the mass-thickness map. Notes ----- The mass-thickness map can be extracted from an EDX spectrum image by calculating the concentration of an element for both K and L lines. Since there should be only one concentration for both lines, it is possible to determine the contribution of the absorption for each pixel and thus obtain the mass-thickness map. """ mu = absorption_coefficient(x,atomic_fraction,elements_dict = elements_dict) rad_toa = np.deg2rad(toa) if type(mu) == float : chi = mu*mass_thickness/np.sin(rad_toa) else : chi = mu[:,np.newaxis]@((mass_thickness.reshape(-1)[:,np.newaxis]).T)/np.sin(rad_toa) return (1-np.exp(-chi))/chi
[docs] def det_efficiency_from_curve (x,filename,kind = "cubic") : r""" Interpolate a detection efficiency curve that is stored in ~/espm/tables. Parameters ---------- x : :np.array 1D: Energy value or energy scale over which the mass-absorption coefficient is calculated. filename: :string: Name of the file of the detection efficiency. kind : :string: Polynomial order of the interpolation. Returns ------- Detection efficiency :np.array 1D: Interpolated detection efficiency. """ array = np.loadtxt(DB_PATH / Path(filename)) x_curve,y_curve = array[:,0], array[:,1] interp_func = interp1d(x_curve,y_curve,kind = kind) return interp_func(x)
[docs] def det_efficiency_layer (x, thickness = 100e-7, density = None, atomic_fraction = False, *, elements_dict = {"Si" : 1.0}) : r""" Calculate the proportion of absorbed X-rays for one virtual layer of the detector. Parameters ---------- x : :np.array 1D: Energy value or energy scale over which the mass-absorption coefficient is calculated. thickness : :float: Thickness of the material slab in meter. If the thickness is set to 0.0, the function will return 1.0. density : :float: Density of the material in g/m3 (to be checked). If None, an approximation of the density based on the atomic numbers of the elements of the materials is calculated. atomic_fraction : :boolean: If True, the atomic concentrations of elements_dict are converted in atomic weight fractions. elements_dict : :dict: Dictionnary with chemical elements as keys and atomic weight fractions (or atomic concentrations) as values. Returns ------- absorption :np.array 1D: Calculated absorption of one layer of the detector. Notes ----- We assume that the X-rays arrive perpendicular to the detector. """ mu = absorption_coefficient(x,atomic_fraction,elements_dict=elements_dict) if density is None : density = approx_density(atomic_fraction,elements_dict = elements_dict) return mu*thickness*density
[docs] def det_efficiency (x,det_dict) : r""" Simulate the detection efficiency of an EDX detector. The absorption wihtin one layer of the detector is calculated according to the following equation, corresponding to the Beer-Lambert law: .. math:: d_k = \exp\left( - \mu(\varepsilon_k)\rho t \right) where :math:`d_k` is the detection efficiency at the energy :math:`\varepsilon_k`, :math:`\mu(\varepsilon_k)` is the mass-absorption coefficient at the energy :math:`\varepsilon_k`, :math:`\rho` is the density of the material, and :math:`t` is the thickness of the material. In a detection layer of the detector, an absobed photon counts as a detected photon so that the detection efficiency is equal to 1 minus the absorption. The detection efficiency is calculated as the product of the absorption of each layer of the detector. Parameters ---------- x : :np.array 1D: Energy value or energy scale over which the mass-absorption coefficient is calculated. det_dict : :dict: See Examples for an example of the structure of the dict. One of the layer of the input dictionnary should be named "detection" to model a active layer of the detector. Returns ------- efficiency :np.array 1D: Calculated detection efficiency based on the input layers model. Examples -------- >>> from espm.models.absorption_edxs import det_efficiency >>> import matplotlib.pyplot as plt >>> import numpy as np >>> dict = {"detection" : {"thickness" : 10e-3, "density" : 2.3, "atomic_fraction" : False, "elements_dict" : {"Si" : 1.0}}, >>> "dead_layer" : {"thickness" : 50e-7, "density" : 2.7, "atomic_fraction" : False, "elements_dict" : {"Al" : 1.0}}} >>> x = np.linspace(0.5,20,num = 1000) >>> efficiency = det_efficiency(x,dict) >>> plt.plot(x,efficiency) """ efficiency = np.ones_like(x) for layer in det_dict : if layer == "detection" : efficiency *= 1 - np.exp(-det_efficiency_layer(x,**det_dict[layer])) else : efficiency *= np.exp(-det_efficiency_layer(x,**det_dict[layer])) return efficiency