Source code for xrayutilities.materials.atom

# This file is part of xrayutilities.
#
# xrayutilities is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# Copyright (c) 2015-2025 Dominik Kriegner <dominik.kriegner@gmail.com>

"""module handling physical properties of elements.

The Atom class manages the database access for atomic scattering factors and
the atomic mass.
"""
import hashlib
import re
from importlib.resources import files

import numpy

from .. import config, utilities
from . import database

dbfile = files("xrayutilities.materials") / "data" / config.DBNAME
_db = database.DataBase(dbfile)
_db.Open()


[docs] def get_key(*args): """Generate a hash key for several possible types of arguments """ tup = [] for a in args: if isinstance(a, numpy.ndarray): tup.append(hashlib.md5(a).digest()) elif isinstance(a, list): tup.append(hash(tuple(a))) else: tup.append(hash(a)) return hash(tuple(tup))
[docs] class Atom: max_cache_length = 1000
[docs] def __init__(self, name, num): self.name = name self.ostate = re.sub('[A-Za-z]', '', name) for r, o in zip(('dot', 'p', 'm'), ('.', '+', '-')): self.ostate = self.ostate.replace(o, r) self.basename = re.sub('[^A-Za-z]', '', name) self.num = num self.__weight = None self.__color = None self.__radius = numpy.nan self._dbcache = {prop: [] for prop in ('f0', 'f1', 'f2', 'f')}
def __key__(self): """Key function to return the elements number""" return self.num def __lt__(self, other_el): """Make elements sortable by their key""" return self.__key__() < other_el.__key__() @property def weight(self): if not self.__weight: _db.SetMaterial(self.basename) self.__weight = _db.weight return self.__weight @property def color(self): if self.__color is None: _db.SetMaterial(self.basename) self.__color = _db.color return self.__color @property def radius(self): if self.__radius is numpy.nan: _db.SetMaterial(self.basename) self.__radius = _db.radius return self.__radius
[docs] def get_cache(self, prop, key): """Check cached value to speed up repeated database requests. Returns ------- bool True then result contains the cached otherwise False and result is None result : database value """ history = self._dbcache[prop] for idx, (k, result) in enumerate(history): if k == key: history.insert(0, history.pop(idx)) # move to front return True, result return False, None
[docs] def set_cache(self, prop, key, result): """Set result to be cached to speed up future calls """ history = self._dbcache[prop] if len(history) == self.max_cache_length: history.pop(-1) history.insert(0, (key, result))
[docs] def f0(self, q): key = get_key(q) f, res = self.get_cache('f0', key) if f: return res _db.SetMaterial(self.basename) res = _db.GetF0(q, self.ostate) self.set_cache('f0', key, res) return res
[docs] def f1(self, en='config'): key = get_key(en) f, res = self.get_cache('f1', key) if f: return res if isinstance(en, str) and en == 'config': en = utilities.energy(config.ENERGY) _db.SetMaterial(self.basename) res = _db.GetF1(utilities.energy(en)) self.set_cache('f1', key, res) return res
[docs] def f2(self, en='config'): key = get_key(en) f, res = self.get_cache('f2', key) if f: return res if isinstance(en, str) and en == 'config': en = utilities.energy(config.ENERGY) _db.SetMaterial(self.basename) res = _db.GetF2(utilities.energy(en)) self.set_cache('f2', key, res) return res
[docs] def f(self, q, en='config'): """Function to calculate the atomic structure factor F Parameters ---------- q : float, array-like momentum transfer en : float or str, optional energy for which F should be calculated, if omitted the value from the xrayutilities configuration is used Returns ------- float or array-like value(s) of the atomic structure factor """ key = get_key(q, en) f, res = self.get_cache('f', key) if f: return res res = self.f0(q) + self.f1(en) + 1.j * self.f2(en) self.set_cache('f', key, res) return res
def __str__(self): ostr = self.name ostr += f" ({self.num:2d})" return ostr def __repr__(self): return self.__str__()