def predict_escape(self, det='Si', scale=1.0): """ predict detector escape for a spectrum, save to 'escape' attribute X-rays penetrate a depth 1/mu(material, energy) and the detector fluorescence escapes from that depth as exp(-mu(material, KaEnergy)*thickness) with a fluorecence yield of the material """ fluor_en = xray_line(det, 'Ka').energy / 1000.0 edge = xray_edge(det, 'K') # here we use the 1/e depth for the emission # and the 1/e depth of the incident beam: # the detector fluorescence can escape on either side mu_emit = material_mu(det, fluor_en * 1000) mu_input = material_mu(det, self.energy * 1000) escape = 0.5 * scale * edge.fyield * np.exp(-mu_emit / (2 * mu_input)) escape[np.where(self.energy * 1000 < edge.energy)] = 0.0 escape *= interp(self.energy - fluor_en, self.counts * 1.0, self.energy, kind='cubic') self.escape = escape
def test_material_mu_components2(): mu = material_mu('TiO2', 10000, density=4.23) assert_allclose(mu, 290.7, rtol=0.001) mu = material_mu('TiO2', 10000) assert_allclose(mu, 290.7, rtol=0.001) mu = material_mu('TiO2', 10000, density=4.5) assert_allclose(mu, 309.26, rtol=0.001) comps = material_mu_components('TiO2', 10000, density=4.23) known_comps = { 'mass': 79.88, 'density': 4.23, 'elements': ['Ti', 'O'], 'Ti': (1, 47.88, 110.676), 'O': (2.0, 15.9994, 5.953) } assert 'Ti' in comps['elements'] assert 'O' in comps['elements'] for attr in ('mass', 'density'): assert_allclose(comps[attr], known_comps[attr], rtol=0.01) for attr in ('Ti', 'O'): assert_allclose(comps[attr][0], known_comps[attr][0], rtol=0.01) assert_allclose(comps[attr][1], known_comps[attr][1], rtol=0.01) assert_allclose(comps[attr][2], known_comps[attr][2], rtol=0.01) with pytest.raises(Warning): c = material_mu_components('TiO2', 10000)
def genSampleComponent(row): sCompKeys = [ # for setting datatypes of the logbook columns "componentId", "componentSurroundedBy", "componentName", "composition", "density", "volFrac", "massFrac", "associatedHazards", "MSDSAvailable", "eLogId", ] sComp = dict.fromkeys(sCompKeys) sComp.update(row.loc[sCompKeys].to_dict()) # try adding an X-ray absorption coefficient try: logging.debug(f'composition: {sComp["composition"]}, density: {sComp["density"]}, mu: { xraydb.material_mu(sComp["composition"], 8.04e3, density = sComp["density"], kind = "total" )}') # kind can be adjusted to 'photo' to calculate just the photo-absorption... calculate not in 1/cm but in 1/m.. SI please. sComp.update({"absCoeffCuTotal": xraydb.material_mu(sComp['composition'], 8.04e3, density = sComp['density'], kind = 'total' ) * 100}) sComp.update({"absCoeffCuPhoto": xraydb.material_mu(sComp['composition'], 8.04e3, density = sComp['density'], kind = 'photo' ) * 100}) sComp.update({"absCoeffMoTotal": xraydb.material_mu(sComp['composition'], 17.4e3, density = sComp['density'], kind = 'total' ) * 100}) sComp.update({"absCoeffMoPhoto": xraydb.material_mu(sComp['composition'], 17.4e3, density = sComp['density'], kind = 'photo' ) * 100}) except: logging.warning('X-ray absorption coefficient could not be calculated') return sComp
def calc_mu(self, energy): "calculate mu for energy in keV" # note material_mu works in eV! self.mu_total = material_mu(self.material, 1000 * energy, density=self.density, kind='total') self.mu_photo = material_mu(self.material, 1000 * energy, density=self.density, kind='photo')
def attendata(formula, rho, t, e1, e2, estep): en_array = np.arange(float(e1), float(e2)+float(estep), float(estep)) rho = float(rho) mu_array = xraydb.material_mu(formula, en_array, density=rho) t = float(t) trans = np.exp(-0.1*t*mu_array) atten = 1 - trans header = (' X-ray attenuation data from xrayweb %s ' % time.ctime(), ' Material.formula : %s ' % formula, ' Material.density : %.3f gr/cm^3 ' % rho, ' Material.thickness : %.3f mm ' % t, ' Column.1: energy (eV)', ' Column.2: atten_length (mm)' , ' Column.3: trans_fraction', ' Column.4: atten_fraction') arr_names = ('energy ', 'atten_length ', 'trans_fract ', 'atten_fract ') txt = make_asciifile(header, arr_names, (en_array, 10/mu_array, trans, atten)) fname = 'xrayweb_atten_%s_%s.txt' % (formula, time.strftime('%Y%h%d_%H%M%S')) return Response(txt, mimetype='text/plain', headers={"Content-Disposition": "attachment;filename=%s" % fname})
def test_material_mu1(): en = np.linspace(8500, 9500, 21) known_mu = np.array([236.2, 232.4, 228.7, 225.1, 221.5, 218.1, 214.7, 211.4, 208.1, 204.9, 1403.6, 1385.6, 1367.9, 1350.6, 1333.5, 1316.7, 1300.3, 1284.0, 1268.1, 1252.4, 1237.0]) mu = material_mu('CuO', en, density=6.3) assert_allclose(mu, known_mu, rtol=0.005)
def test_material_mu4(): en = np.linspace(5000, 10000, 21) known_mu = np.array([42.592, 36.801, 32.006, 28.005, 24.641, 21.794, 19.367, 17.288, 15.496, 13.943, 12.592, 11.411, 10.373, 9.458, 8.649, 7.931, 7.291, 6.719, 6.206, 5.745, 5.330]) with pytest.raises(Warning): out = material_mu('H2SO4', en)
def test_material_mu3(): en = np.linspace(5000, 10000, 21) known_mu = np.array([42.592, 36.801, 32.006, 28.005, 24.641, 21.794, 19.367, 17.288, 15.496, 13.943, 12.592, 11.411, 10.373, 9.458, 8.649, 7.931, 7.291, 6.719, 6.206, 5.745, 5.330]) mu = material_mu('H2O', en) assert_allclose(mu, known_mu, rtol=0.05)
def test_material_mu2(): en = np.linspace(5000, 10000, 21) known_mu = np.array([ 0.04934, 0.04267, 0.03715, 0.03254, 0.02866, 0.02538, 0.02257, 0.02016, 0.01809, 0.01629, 0.01472, 0.01334, 0.01214, 0.01108, 0.01014, 0.00930, 0.00856, 0.00789, 0.00729, 0.00675, 0.00626 ]) mu = material_mu('air', en) assert_allclose(mu, known_mu, rtol=0.05) air_formula, air_density = get_material('air') air_comps = chemparse(air_formula) assert air_comps['Ar'] < 0.013 assert air_comps['Ar'] > 0.007 mu = material_mu('air', en, density=2.0 * air_density) assert_allclose(mu, 2.0 * known_mu, rtol=0.05)
def calc_escape_scale(self, energy, thickness=None): """ calculate energy dependence of escape effect X-rays penetrate a depth 1/mu(material, energy) and the detector fluorescence escapes from that depth as exp(-mu(material, KaEnergy)*thickness) with a fluorecence yield of the material """ det = self.detector # note material_mu, xray_edge, xray_line work in eV! escape_energy_ev = xray_line(det.material, 'Ka').energy mu_emit = material_mu(det.material, escape_energy_ev) self.escape_energy = 0.001 * escape_energy_ev mu_input = material_mu(det.material, 1000 * energy) edge = xray_edge(det.material, 'K') self.escape_scale = edge.fyield * np.exp(-mu_emit / (2 * mu_input)) self.escape_scale[np.where(energy < 0.001 * edge.energy)] = 0.0
def fluo_corr(norm, formula, elem, edge, line, anginp, angout): """ Correct over-absorption (self-absorption) for fluorescene XAFS using the based on the `larch` implementation of the FLUO alogrithm of D. Haskel. See FLUO manual_ for details. .. _manual: https://www3.aps.anl.gov/haskel/fluo.html Parameters ---------- norm : iter Normalized fluorescence formula : str Chemical formula of the compound. For example: 'EuO'. elem : str Element of interest. For example: 'Eu'. edge : str Absorption edge of interest. For example: 'L3'. line : str Fluorescence line measured. For example: 'La'. anginp : float Input angle with respect to the sample surface. See FLUO manual. anginp : float Output angle with respect to the sample surface. See FLUO manual. Returns -------- norm_corr : numpy.array Corrected normalized fluorescence. """ # Angular correction. Avoid divide by zero. ang_corr = (np.sin(np.deg2rad(anginp)) / np.sin(max(1.e-7, np.deg2rad(angout)))) # Find edge energies and fluorescence line energy e_edge = xray_edge(elem, edge).energy e_fluor = xray_line(elem, line).energy # Calculate mu(E) for fluorescence energy, above, below edge # alpha ends up independent of density! muvals = material_mu(formula, np.array([e_fluor, e_edge - 10.0, e_edge + 10.0]), density=1) # Do the correction alpha = (muvals[0] * ang_corr + muvals[1]) / (muvals[2] - muvals[1]) norm_corr = np.array(norm) * alpha / (alpha + 1 - np.array(norm)) return norm_corr
def get_att_len(energy, material=None, density=None): """ Get the attenuation length (in meter) of a material. The X-ray beam intensity I(x) at depth x in a material is a function of the attenuation coefficient mu, and can be calculated by the Beer-Lambert law. The Absorption Length (or Attenuation Length) is defined as the distance into a material where the x-ray beam intensity has decreased to a value of 1/e (~ 40%) of the incident beam intensity (Io). (1/e) = e^(-mu * x) ln(1/e) = ln(e^(-mu * x)) 1 = mu * x x = 1/mu Parameters ---------- energy : number Beam Energy given in KeV material : str, optional Atomic symbol for element, defaults to 'Be' density : float, optional Material density in g/cm^3 Returns ------- att_len : float Attenuation length (in meters) Raises ------ ValueError If an invalid symbol is provided for material. Examples -------- >>> get_att_len(energy=8, material='Be') 0.004810113254120656 """ material = material or MATERIAL density = density or xdb.atomic_density(material) try: # xdb.material_my returns absorption length in 1/cm and takes energy # or array of energies in eV. att_len = 1.0 / (xdb.material_mu(material, energy * 1.0e3, density=density)) * 1.0e-2 except Exception as ex: logger.error('Get Attenuation Length error: %s', ex) raise ex return att_len
def test_material_mu_components1(): mu = material_mu('quartz', 10000) assert_allclose(mu, 50.368, rtol=0.001) comps = material_mu_components('quartz', 10000) known_comps = {'mass': 60.08, 'density': 2.65, 'elements': ['Si', 'O'], 'Si': (1, 28.1, 33.879), 'O': (2.0, 16.0, 5.953)} assert 'Si'in comps['elements'] assert 'O'in comps['elements'] for attr in ('mass', 'density'): assert_allclose(comps[attr], known_comps[attr], rtol=0.01) for attr in ('Si', 'O'): assert_allclose(comps[attr][0], known_comps[attr][0], rtol=0.01) assert_allclose(comps[attr][1], known_comps[attr][1], rtol=0.01) assert_allclose(comps[attr][2], known_comps[attr][2], rtol=0.01)
def attendata(formula, rho, t, e1, e2, de, fname): en_array = energy_array(e1, e2, de) rho = float(rho) mu_array = xraydb.material_mu(formula, en_array, density=rho) t = float(t) trans = np.exp(-0.1 * t * mu_array) atten = 1 - trans header = (' X-ray attenuation data from xrayweb %s ' % time.ctime(), ' Material.formula : %s ' % formula, ' Material.density : %.3f gr/cm^3 ' % rho, ' Material.thickness : %.3f mm ' % t, ' Column.1: energy (eV)', ' Column.2: attenuation_length (mm)', ' Column.3: transmitted_fraction', ' Column.4: attenuated_fraction') arr_names = ('energy ', 'atten_length ', 'trans_fract ', 'atten_fract ') txt = make_asciifile(header, arr_names, (en_array, 10 / mu_array, trans, atten)) return Response(txt, mimetype='text/plain')
#!/usr/bin/env python # XrayDB example script python/examples/mu_water.py # # calculate the fraction of X-rays transmitted through 1 mm of water # import numpy as np import matplotlib.pyplot as plt from xraydb import material_mu energy = np.linspace(1000, 41000, 201) mu = material_mu('H2O', energy) # mu is returned in 1/cm trans = np.exp(-0.1 * mu) plt.plot(energy, trans, label='transmitted') plt.plot(energy, 1 - trans, label='attenuated') plt.title('X-ray absorption by 1 mm of water') plt.xlabel('Energy (eV)') plt.ylabel('Transmitted / Attenuated fraction') plt.legend() plt.show()
def fluo_corr(energy, mu, formula, elem, group=None, edge='K', anginp=45, angout=45, _larch=None, **pre_kws): """correct over-absorption (self-absorption) for fluorescene XAFS using the FLUO alogrithm of D. Haskel. Arguments --------- energy array of energies mu uncorrected fluorescence mu formula string for sample stoichiometry elem atomic symbol or Z of absorbing element group output group [default None] edge name of edge ('K', 'L3', ...) [default 'K'] anginp input angle in degrees [default 45] angout output angle in degrees [default 45] Additional keywords will be passed to pre_edge(), which will be used to ensure consistent normalization. Returns -------- None, writes `mu_corr` and `norm_corr` (normalized `mu_corr`) to output group. Notes ----- Support First Argument Group convention, requiring group members 'energy' and 'mu' """ energy, mu, group = parse_group_args(energy, members=('energy', 'mu'), defaults=(mu, ), group=group, fcn_name='fluo_corr') # gather pre-edge options pre_opts = { 'e0': None, 'nnorm': 1, 'nvict': 0, 'pre1': None, 'pre2': -30, 'norm1': 100, 'norm2': None } if hasattr(group, 'pre_edge_details'): uopts = getattr(group.pre_edge_details, 'call_args', {}) for attr in pre_opts: if attr in uopts: pre_opts[attr] = uopts[attr] pre_opts.update(pre_kws) pre_opts['step'] = None pre_opts['nvict'] = 0 # generate normalized mu for correction preinp = preedge(energy, mu, **pre_opts) ang_corr = (np.sin(max(1.e-7, np.deg2rad(anginp))) / np.sin(max(1.e-7, np.deg2rad(angout)))) # find edge energies and fluorescence line energy e_edge = xray_edge(elem, edge).energy e_fluor = xray_line(elem, edge).energy # calculate mu(E) for fluorescence energy, above, below edge muvals = material_mu(formula, np.array([e_fluor, e_edge - 10.0, e_edge + 10.0]), density=1) alpha = (muvals[0] * ang_corr + muvals[1]) / (muvals[2] - muvals[1]) mu_corr = mu * alpha / (alpha + 1 - preinp['norm']) preout = preedge(energy, mu_corr, **pre_opts) if group is not None: if _larch is not None: group = set_xafsGroup(group, _larch=_larch) group.mu_corr = mu_corr group.norm_corr = preout['norm']
def atten(material=None, density=None, t='1.0', e1='1000', e2='51000', de='50'): message = [] mu_plot = atten_plot = {} num = errors = 0 mode = 'Linear' datalink = None do_plot = True if request.method == 'POST': formula = request.form.get('formula') matname = request.form.get('matname') mode = request.form.get('mode') density = float(request.form.get('density')) e1 = float(request.form.get('e1')) e2 = float(request.form.get('e2')) de = float(request.form.get('de')) t = float(request.form.get('thickness')) #input validation if not xraydb.validate_formula(formula): message.append("cannot interpret chemical formula") try: density = max(0, float(density)) except: message.append('Density must be a positive number.') else: e1 = float(e1) e2 = float(e2) de = float(de) t = float(t) if material in materials_: mats = material formula = materials_['material'].formula density = materials_['material'].density elif material is not None and density is not None: formula = material density = float(density) mats = None else: do_plot = False mats = 'silicon' formula = materials_['silicon'].formula density = materials_['silicon'].density request.form = { 'mats': mats, 'formula': formula, 'density': density, 'e1': e1, 'e2': e2, 'de': de, 'thickness': t, 'mode': 'Linear' } if do_plot and formula is not None: # make plot en_array = energy_array(e1, e2, de) num = en_array.size mu_array = xraydb.material_mu(formula, en_array, density=float(density)) trans = np.exp(-0.1 * t * mu_array) atten = 1 - trans use_log = mode.lower() == 'log' mu_plot = make_plot(en_array, 10 / mu_array, material, formula, ylog_scale=use_log, ytitle='1/e length (mm)') atten_plot = make_plot(en_array, trans, material, "%.3f mm %s" % (t, formula), ylog_scale=use_log, y2=atten, ytitle='transmitted/attenuated fraction', y1label='transmitted', y2label='attenuated') return render_template('attenuation.html', message=message, errors=len(message), datalink=datalink, mu_plot=mu_plot, de=int(de), atten_plot=atten_plot, matlist=matlist, materials_dict=materials_dict) # , input=input)
def formula(material=None): message = [''] abslen = absq = energies = [] mu_plot = output = {} num = errors = 0 isLog = True if request.method == 'POST': formula = request.form.get('formula') density = request.form.get('density') energy1 = request.form.get('energy1') energy2 = request.form.get('energy2') step = request.form.get('step') mode = request.form.get('mode') data = request.form.get('data') #input validation output = validate_input(formula, density, step, energy1, energy2, mode, material, page='formula') message = output['message'] else: request.form = { 'formula': materials_dict['silicon'].formula, 'density': materials_dict['silicon'].density, 'energy1': '1000', 'energy2': '50000', 'step': '100' } if message[0] == 'Input is valid': #unpack floats df = output['df'] ef = output['ef'] ef2 = output['ef2'] sf = output['sf'] isLog = output['isLog'] #make plot while ef2 < ( ef + (sf * 2) ): #this ensures that there are always at least 3 energy values for a meaningful plot ef2 += sf ef2 += sf #this includes energy2 in the plot, normally np.arrage excludes the upper bound en_array = np.arange(ef, ef2, sf) num = en_array.size mu_array = xraydb.material_mu(formula, en_array, density=df) if data == 'Abslen': len_array = np.empty((0, )) for i in mu_array: len_array = np.append(len_array, [10000 / i], axis=0) mu_plot = make_plot(en_array, len_array, material, formula, ytitle='10000 / mu', ylog_scale=isLog) else: mu_plot = make_plot(en_array, mu_array, material, formula, ylog_scale=isLog) energies = [nformat(x) for x in en_array] absq = [nformat(x) for x in mu_array] abslen = [10000 / float(nformat(x)) for x in mu_array] message = [] else: errors = len(message) return render_template('formulas.html', message=message, errors=errors, abslen=abslen, mu_plot=mu_plot, absq=absq, energies=energies, num=num, matlist=matlist, materials_dict=materials_dict_j, input=input)
def atten(material=None): message = [] energies = [] mu_plot = atten_plot = {} num = errors = 0 mode = 'Linear' datalink = None if request.method == 'POST': formula = request.form.get('formula') matname = request.form.get('matname') density = request.form.get('density') energy1 = request.form.get('energy1') energy2 = request.form.get('energy2') estep = request.form.get('step') mode = request.form.get('mode') thickness = request.form.get('thickness') #input validation if not xraydb.validate_formula(formula): message.append("cannot interpret chemical formula") try: density = max(0, float(density)) except: message.append('Density must be a positive number.') if len(message) == 0: use_log = mode.lower() == 'log' # make plot en_array = np.arange(float(energy1), float(energy2)+float(estep), float(estep)) num = en_array.size mu_array = xraydb.material_mu(formula, en_array, density=float(density)) t = float(thickness) trans = np.exp(-0.1*t*mu_array) atten = 1 - trans mu_plot = make_plot(en_array, 10/mu_array, material, formula, ylog_scale=use_log, ytitle='1/e length (mm)') atten_plot = make_plot(en_array, trans, material, "%.3f mm %s" % (t, formula), ylog_scale=use_log, y2=atten, ytitle='transmitted/attenuated fraction', y1label='transmitted', y2label='attenuated') else: request.form = {'mats': 'silicon', 'formula': materials_['silicon'].formula, 'density': materials_['silicon'].density, 'energy1': 1000, 'energy2': 51000, 'step': "50", 'thickness': 1.00, 'mode': 'Linear'} return render_template('attenuation.html', message=message, errors=len(message), datalink=datalink, mu_plot=mu_plot, atten_plot=atten_plot, matlist=matlist, materials_dict=materials_dict, input=input)
#!/usr/bin/env python # XrayDB example script python/examples/mu_water.py # # calculate the fraction of X-rays transmitted through 1 mm of calcite # import numpy as np import matplotlib.pyplot as plt from xraydb import material_mu energy = np.linspace(1000, 41000, 201) mu = material_mu('CaCO3', energy, density=2.71) # mu is returned in 1/cm trans = np.exp(-0.1 * mu) plt.plot(energy, trans, label='transmitted') plt.plot(energy, 1-trans, label='attenuated') plt.title('X-ray absorption by 1 mm of calcite') plt.xlabel('Energy (eV)') plt.ylabel('Transmitted / Attenuated fraction') plt.legend() plt.show()