Source code for roentgen.absorption.material

"""
"""

from __future__ import absolute_import

import numpy as np
import os
import astropy.units as u
from scipy import interpolate
import roentgen

__all__ = ['Material', 'MassAttenuationCoefficient', 'Compound', 'is_an_element', 'get_atomic_number']

_package_directory = roentgen._package_directory
_data_directory = roentgen._data_directory


[docs]class Material(object): """An object which provides the properties of a material in x-rays Parameters ---------- material_str : str A string representation of the material which includes an element symbol (e.g. Si), an element name (e.g. Silicon), or the name of a compound (e.g. cdte, mylar). Cap-sensitive. thickness : `astropy.units.Quantity` The thickness of the material in the optical path. density : `astropy.units.Quantity` The density of the material. If None use default values. Examples -------- >>> from roentgen.absorption.material import Material >>> import astropy.units as u >>> detector = Material('cdte', 500 * u.um) >>> thermal_blankets = Material('mylar', 0.5 * u.mm) """ @u.quantity_input def __init__(self, material_str, thickness: u.m, density=None): self.name = material_str self.thickness = thickness self.mass_attenuation_coefficient = MassAttenuationCoefficient(material_str) self.name = self.mass_attenuation_coefficient.name if density is None: if is_an_element(material_str): self.density = roentgen.elemental_densities[get_atomic_number(material_str)-1]['density'] else: # not using loc because table indexing is not yet stable # self.density = roentgen.compounds.loc[material_str]['density'] index = list(roentgen.compounds['symbol']).index(material_str) self.density = roentgen.compounds[index]['density'] else: self.density = density def __repr__(self): """Returns a human-readable representation.""" txt = '<Material ' + str(self.name) + ' (' + str(self.name) + ') ' txt += str(self.thickness) + ' ' + str(self.density) + '>' return txt def __add__(self, other): if isinstance(other, Material): return Compound([self, other]) elif isinstance(other, Compound): return Compound([self] + other.materials) else: raise ValueError("Can't add <Material> and " + str(other))
[docs] def transmission(self, energy): """Provide the transmission fraction (0 to 1). Parameters ---------- energy : `astropy.units.Quantity` An array of energies in keV """ coefficients = self.mass_attenuation_coefficient.func(energy) transmission = np.exp(- coefficients * self.density * self.thickness) return transmission
[docs] def absorption(self, energy): """Provides the absorption fraction (0 to 1). Parameters ---------- energy : `astropy.units.Quantity` An array of energies in keV. """ return 1 - self.transmission(energy)
[docs]class Compound(object): """An object which provides the x-ray properties of a compound (i.e. many materials). Parameters ---------- materials : list A list of Material objects Examples -------- >>> from roentgen.absorption.material import Material, Compound >>> import astropy.units as u >>> detector = Compound([Material('Pt', 5 * u.um), Material('cdte', 500 * u.um)]) """ def __init__(self, materials): self.materials = materials def __add__(self, other): if isinstance(other, Material): return Compound(self.materials + [other]) elif isinstance(other, Compound): return Compound(self.materials + other.materials) else: raise ValueError("Can't add <Compound> and " + str(other)) def __repr__(self): """Returns a human-readable representation.""" txt = '<Compound ' for material in self.materials: txt += str(material) return txt + '>'
[docs] def transmission(self, energy): """Provide the transmission fraction (0 to 1). Parameters ---------- energy : `astropy.units.Quantity` An array of energies in keV """ transmission = np.ones(len(energy), dtype=np.float) for material in self.materials: coefficients = material.mass_attenuation_coefficient.func(energy) transmission *= np.exp(- coefficients * material.density * material.thickness) return transmission
[docs] def absorption(self, energy): """Provides the absorption fraction (0 to 1). Parameters ---------- energy : `astropy.units.Quantity` An array of energies in keV. """ return 1 - self.transmission(energy)
[docs]class MassAttenuationCoefficient(object): """ The mass attenuation coefficient Parameters ---------- material : str A string representing a material (e.g. cdte, be, mylar, si) """ def __init__(self, material): if is_an_element(material): atomic_number = get_atomic_number(material) datafile_path = os.path.join(_data_directory, 'elements', 'z' + str(atomic_number).zfill(2) + '.csv') symbol = roentgen.elements[atomic_number-1]['symbol'] name = roentgen.elements[atomic_number-1]['name'] else: datafile_path = os.path.join(_data_directory, 'compounds_mixtures', material.lower().replace(' ', '_') + '.csv') index = list(roentgen.compounds['symbol']).index(material) symbol = roentgen.compounds[index]['symbol'] name = roentgen.compounds[index]['name'] data = np.loadtxt(datafile_path, delimiter=',') # find the material in our list self.symbol = symbol self.name = name self.energy = u.Quantity(data[:, 0] * 1000, 'keV') self.data = u.Quantity(data[:, 1], 'cm^2/g') data_energy_kev = np.log10(self.energy.value) data_attenuation_coeff = np.log10(self.data.value) self._f = interpolate.interp1d(data_energy_kev, data_attenuation_coeff, bounds_error=False, fill_value=0.0) self.func = lambda x: u.Quantity(10 ** self._f(np.log10(x.to('keV').value)), 'cm^2/g')
[docs] def get_filename(material_str): if len(material_str) <= 2: # likely a symbol of an element if material_str in list(elements['symbol']): return
[docs]def is_an_element(element_str): """Returns True is the string represents an element""" result = False if (len(element_str) <= 2) and (element_str in list(roentgen.elements['symbol'])): result = True else: if element_str in list(roentgen.elements['name']): result = True return result
[docs]def get_atomic_number(element_str): """Return the atomic number of the element""" # check to see if element_str is symbol if is_an_element(element_str): if len(element_str) <= 2: atomic_number = list(roentgen.elements['symbol']).index(element_str) + 1 else: atomic_number = list(roentgen.elements['name']).index(element_str) + 1 else: atomic_number = None return atomic_number