Source code for orsopy.slddb.element_table.element

"""
Defining the Element class that is used to hold all needed data for one element/isotope.
"""

import os

from numpy import array, load

from .masses import ATOMIC_WEIGHTS, ELEMENT_CHARGES, ELEMENT_FULLNAMES
from .nabs_geant4 import DATA_DIR as NABS_DATA_DIR
from .nabs_geant4 import NEUTRON_ABSORPTIONS
from .nlengths_pt import NEUTRON_SCATTERING_LENGTHS
from .xray_nist import XRAY_SCATTERING_FACTORS

BASE_PATH = os.path.abspath(os.path.dirname(__file__))

ELEMENT_NAMES = dict([(value, key) for key, value in ELEMENT_CHARGES.items()])

ELEMENT_FULLNAMES["D"] = "deuterium"
ELEMENT_FULLNAMES["Hx"] = "exchangeable hydrogen"

for data in [ATOMIC_WEIGHTS, NEUTRON_SCATTERING_LENGTHS]:
    data["D"] = data[(1, 2)]
    data["Hx"] = data[(1, 1)]
for data in [ELEMENT_CHARGES, XRAY_SCATTERING_FACTORS]:
    data["Hx"] = data["H"]


[docs] class Element: N = None def __init__(self, symbol=None, Z=None): # get element from database N = None self.symbol = symbol if Z is None and symbol is None: raise ValueError("Provide either Symbol or Z") elif Z is None: if "[" in symbol: self.symbol, N = symbol.rstrip("]").split("[", 1) N = int(N) key = (ELEMENT_CHARGES[self.symbol], N) else: key = symbol self.Z = ELEMENT_CHARGES[self.symbol] else: self.Z = Z self.symbol = ELEMENT_NAMES[Z] key = self.symbol self.N = N self.name = ELEMENT_FULLNAMES[self.symbol] self.mass = ATOMIC_WEIGHTS[key] try: self.b = NEUTRON_SCATTERING_LENGTHS[key] except KeyError: raise ValueError(f"No neutorn scattering data for {key}") if key in NEUTRON_ABSORPTIONS: self._ndata = load(os.path.join(BASE_PATH, NABS_DATA_DIR, NEUTRON_ABSORPTIONS[key]))["arr_0"] else: self._ndata = None try: self._xdata = array(XRAY_SCATTERING_FACTORS[self.symbol]) except KeyError: self._xdata = None
[docs] def f_of_E(self, Ei): if self._xdata is None: return float("nan") E, fp, fpp = self._xdata fltr = E >= Ei if not fltr.any(): return 0.0 - 0j else: # linear interpolation between two nearest points E1 = E[fltr][0] try: E2 = E[fltr][1] except IndexError: return fp[fltr][0] - 1j * fpp[fltr][0] else: f1 = fp[fltr][0] - 1j * fpp[fltr][0] f2 = fp[fltr][1] - 1j * fpp[fltr][1] return ((E2 - Ei) * f1 + (Ei - E1) * f2) / (E2 - E1)
[docs] def b_of_L(self, Li): if self._ndata is None: return self.b L, b_abs = self._ndata if Li > L[-1]: return self.b.real - 1j * b_abs[-1] if Li < L[0]: return self.b.real - 1j * b_abs[0] fltr = L >= Li # linear interpolation between two nearest points L1 = L[fltr][0] try: L2 = L[fltr][1] except IndexError: return self.b.real - 1j * b_abs[fltr][0] else: b_abs1 = b_abs[fltr][0] b_abs2 = b_abs[fltr][1] return self.b.real - 1j * ((L2 - Li) * b_abs1 + (Li - L1) * b_abs2) / (L2 - L1)
@property def E(self): return self._xdata[0] @property def f(self): return self._xdata[1] - 1j * self._xdata[2] @property def fp(self): return self._xdata[1] @property def fpp(self): return self._xdata[2] @property def has_ndata(self): return self._ndata is not None @property def Lamda(self): # return neutron wavelength values for energy dependent absorption if self.has_ndata: return self._ndata[0] else: return array([0.05, 50.0]) @property def b_abs(self): return self._ndata[1] @property def b_lambda(self): if self.has_ndata: return self.b.real - 1j * self._ndata[1] else: return array([self.b, self.b]) def __str__(self): if self.N is None: return self.symbol else: return "%s[%s]" % (self.symbol, self.N) def __repr__(self): if self.N is None: symb = self.symbol else: symb = "%s[%s]" % (self.symbol, self.N) return 'Element(symbol="%s")' % symb def __eq__(self, other): if isinstance(other, Element): return self.N == other.N and self.Z == other.Z and self.symbol == other.symbol else: return object.__eq__(self, other) def __hash__(self): return hash((self.N, self.Z, self.symbol))