Coverage for /Users/Newville/Codes/xraylarch/larch/xray/cromer_liberman.py: 0%
54 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1import os
2import ctypes
3import numpy as np
4from scipy.signal import convolve
6import larch
7from larch.larchlib import get_dll
9from larch.math import as_ndarray
10from xraydb import core_width, atomic_number
12CLLIB = None
14def f1f2(z, energies, width=None, edge=None):
15 """Return anomalous scattering factors f1, f2 from Cromer-Liberman
17 Look-up and return f1, f2 for an element and array of energies
18 from Cromer-Liberman (Cowan-Brennan implementation)
20 Parameters
21 ----------
22 z: atomic number of element
23 energies: array of x-ray energies (in eV)
24 width: width used to convolve values with lorentzian profile
25 edge: x-ray edge ('K', 'L3', etc) used to lookup energy
26 width for convolution.
28 Returns:
29 ---------
30 f1, f2: anomalous scattering factors
32 """
33 global CLLIB
34 if CLLIB is None:
35 CLLIB = get_dll('cldata')
37 en = as_ndarray(energies)
39 if not isinstance(z, int):
40 z = atomic_number(z)
41 if z is None:
42 return None
44 if z > 92:
45 print( 'Cromer-Liberman data not available for Z>92')
46 return
48 if edge is not None or width is not None:
49 natwid = core_width(element=z, edge=edge)
50 if width is None and natwid not in (None, []):
51 width = natwid
53 if width is not None: # will convolve!
54 e_extra = int(width*80.0)
55 estep = (en[1:] - en[:-1]).min()
56 emin = min(en) - e_extra
57 emax = max(en) + e_extra
59 npts = 1 + abs(emax-emin+estep*0.02)/abs(estep)
60 en = np.linspace(emin, emax, int(npts))
61 nk = int(e_extra / estep)
62 sig = width/2.0
63 lor = (1./(1 + ((np.arange(2*nk+1)-nk*1.0)/sig)**2))/(np.pi*sig)
64 scale = lor.sum()
66 # create ctypes pointers for the C function
67 npts = len(en)
68 p_z = ctypes.pointer(ctypes.c_int(int(z)))
69 p_npts = ctypes.pointer(ctypes.c_int(npts))
70 p_en = (npts*ctypes.c_double)()
71 p_f1 = (npts*ctypes.c_double)()
72 p_f2 = (npts*ctypes.c_double)()
74 for i in range(npts):
75 p_en[i] = en[i]
77 nout = CLLIB.f1f2(p_z, p_npts, p_en, p_f1, p_f2)
78 f1 = np.array([i for i in p_f1[:]])
79 f2 = np.array([i for i in p_f2[:]])
80 if width is not None: # do the convolution
81 f1 = np.interp(energies, en, convolve(f1, lor)[nk:-nk])/scale
82 f2 = np.interp(energies, en, convolve(f2, lor)[nk:-nk])/scale
83 return (f1, f2)
85if __name__ == '__main__':
86 en = np.linspace(8000, 9200, 51)
87 f1, f2 = f1f2(29, en)
88 print( en, f1, f2)