Coverage for /Users/Newville/Codes/xraylarch/larch/xafs/rebin_xafs.py: 11%
73 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 numpy as np
3from larch import Group, Make_CallArgs, parse_group_args
4from larch.math import index_of, interp1d, remove_dups
5from .xafsutils import ktoe, etok, TINY_ENERGY
7@Make_CallArgs(["energy", "mu"])
8def sort_xafs(energy, mu=None, group=None, fix_repeats=True, overwrite=True):
9 """sort energy, mu pair of XAFS data so that energy is monotonically increasing
11 Arguments
12 ---------
13 energy input energy array
14 mu input mu array
15 group output group
16 fix_repeats bool, whether to fix repeated energies
17 overwrite bool, whether to overwrite arrays [True]
19 Returns
20 -------
21 None
23 if overwrite is False, a group named 'sorted' will be created
24 in the output group, with sorted energy and mu arrays
26 (if the output group is None, _sys.xafsGroup will be written to)
28 """
29 energy, mu, group = parse_group_args(energy, members=('energy', 'mu'),
30 defaults=(mu,), group=group,
31 fcn_name='sort_xafs')
33 indices = np.argsort(energy)
34 new_energy = energy[indices]
35 new_mu = mu[indices]
37 if fix_repeats:
38 new_energy = remove_dups(new_energy, tiny=TINY_ENERGY)
40 if not overwrite:
41 group.sorted = Group(energy=new_energy, mu=new_mu)
42 else:
43 group.energy = new_energy
44 group.mu = new_mu
45 return
48@Make_CallArgs(["energy", "mu"])
49def rebin_xafs(energy, mu=None, group=None, e0=None, pre1=None, pre2=-30,
50 pre_step=2, xanes_step=None, exafs1=15, exafs2=None,
51 exafs_kstep=0.05, method='centroid'):
52 """rebin XAFS energy and mu to a 'standard 3 region XAFS scan'
54 Arguments
55 ---------
56 energy input energy array
57 mu input mu array
58 group output group
59 e0 energy reference -- all energy values are relative to this
60 pre1 start of pre-edge region [1st energy point]
61 pre2 end of pre-edge region, start of XANES region [-30]
62 pre_step energy step for pre-edge region [2]
63 xanes_step energy step for XANES region [see note]
64 exafs1 end of XANES region, start of EXAFS region [15]
65 exafs2 end of EXAFS region [last energy point]
66 exafs_kstep k-step for EXAFS region [0.05]
67 method one of 'boxcar', 'centroid' ['centroid']
69 Returns
70 -------
71 None
73 A group named 'rebinned' will be created in the output group, with the
74 following attributes:
75 energy new energy array
76 mu mu for energy array
77 e0 e0 copied from current group
79 (if the output group is None, _sys.xafsGroup will be written to)
81 Notes
82 ------
83 1 If the first argument is a Group, it must contain 'energy' and 'mu'.
84 See First Argrument Group in Documentation
86 2 If xanes_step is None, it will be found from the data as E0/25000,
87 truncated down to the nearest 0.05: xanes_step = 0.05*max(1, int(e0/1250.0))
90 3 The EXAFS region will be spaced in k-space
92 4 The rebinned data is found by determining which segments of the
93 input energy correspond to each bin in the new energy array. That
94 is, each input energy is assigned to exactly one bin in the new
95 array. For each new energy bin, the new value is selected from the
96 data in the segment as either
97 a) linear interpolation if there are fewer than 3 points in the segment.
98 b) mean value ('boxcar')
99 c) centroid ('centroid')
101 """
102 energy, mu, group = parse_group_args(energy, members=('energy', 'mu'),
103 defaults=(mu,), group=group,
104 fcn_name='rebin_xafs')
106 if e0 is None:
107 e0 = getattr(group, 'e0', None)
109 if e0 is None:
110 raise ValueError("need e0")
112 if pre1 is None:
113 pre1 = pre_step*int((min(energy) - e0)/pre_step)
115 if exafs2 is None:
116 exafs2 = max(energy) - e0
118 # determine xanes step size:
119 # find mean of energy difference within 10 eV of E0
120 nx1 = index_of(energy, e0-10)
121 nx2 = index_of(energy, e0+10)
122 de_mean = np.diff(energy[nx1:nx1]).mean()
123 if xanes_step is None:
124 xanes_step = 0.05 * max(1, int(e0 / 1250.0)) # E0/25000, round down to 0.05
126 # create new energy array from the 3 segments (pre, xanes, exafs)
127 en = []
128 for start, stop, step, isk in ((pre1, pre2, pre_step, False),
129 (pre2, exafs1, xanes_step, False),
130 (exafs1, exafs2, exafs_kstep, True)):
131 if isk:
132 start = etok(start)
133 stop = etok(stop)
135 npts = 1 + int(0.1 + abs(stop - start) / step)
136 reg = np.linspace(start, stop, npts)
137 if isk:
138 reg = ktoe(reg)
139 en.extend(e0 + reg[:-1])
141 # find the segment boundaries of the old energy array
142 bounds = [index_of(energy, e) for e in en]
143 mu_out = []
144 err_out = []
146 j0 = 0
147 for i in range(len(en)):
148 if i == len(en) - 1:
149 j1 = len(energy) - 1
150 else:
151 j1 = int((bounds[i] + bounds[i+1] + 1)/2.0)
152 if i == 0 and j0 == 0:
153 j0 = index_of(energy, en[0]-5)
154 # if not enough points in segment, do interpolation
155 if (j1 - j0) < 3:
156 jx = j1 + 1
157 if (jx - j0) < 3:
158 jx += 1
160 val = interp1d(energy[j0:jx], mu[j0:jx], en[i])
161 err = mu[j0:jx].std()
162 if np.isnan(val):
163 j0 = max(0, j0-1)
164 jx = min(len(energy), jx+1)
165 val = interp1d(energy[j0:jx], mu[j0:jx], en[i])
166 err = mu[j0:jx].std()
167 else:
168 if method.startswith('box'):
169 val = mu[j0:j1].mean()
170 else:
171 val = (mu[j0:j1]*energy[j0:j1]).mean()/energy[j0:j1].mean()
172 mu_out.append(val)
173 err_out.append(mu[j0:j1].std())
174 j0 = j1
176 newname = group.__name__ + '_rebinned'
177 group.rebinned = Group(energy=np.array(en), mu=np.array(mu_out),
178 delta_mu=np.array(err_out), e0=e0,
179 __name__=newname)
180 return