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

1import os 

2import ctypes 

3import numpy as np 

4from scipy.signal import convolve 

5 

6import larch 

7from larch.larchlib import get_dll 

8 

9from larch.math import as_ndarray 

10from xraydb import core_width, atomic_number 

11 

12CLLIB = None 

13 

14def f1f2(z, energies, width=None, edge=None): 

15 """Return anomalous scattering factors f1, f2 from Cromer-Liberman 

16 

17 Look-up and return f1, f2 for an element and array of energies 

18 from Cromer-Liberman (Cowan-Brennan implementation) 

19 

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. 

27 

28 Returns: 

29 --------- 

30 f1, f2: anomalous scattering factors 

31 

32 """ 

33 global CLLIB 

34 if CLLIB is None: 

35 CLLIB = get_dll('cldata') 

36 

37 en = as_ndarray(energies) 

38 

39 if not isinstance(z, int): 

40 z = atomic_number(z) 

41 if z is None: 

42 return None 

43 

44 if z > 92: 

45 print( 'Cromer-Liberman data not available for Z>92') 

46 return 

47 

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 

52 

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 

58 

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() 

65 

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)() 

73 

74 for i in range(npts): 

75 p_en[i] = en[i] 

76 

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) 

84 

85if __name__ == '__main__': 

86 en = np.linspace(8000, 9200, 51) 

87 f1, f2 = f1f2(29, en) 

88 print( en, f1, f2)