def find_e0(energy, mu, group=None, _larch=None): """calculate E0 given mu(energy) This finds the point with maximum derivative with some checks to avoid spurious glitches. Arguments ---------- energy: array of x-ray energies, in eV mu: array of mu(E) group: output group Returns ------- e0: edge energy, in eV. If a group is supplied, group.e0 will also be set to this value. """ if _larch is None: raise Warning("cannot find e0 -- larch broken?") energy = remove_dups(energy) dmu = np.diff(mu) / np.diff(energy) # find points of high derivative high_deriv_pts = np.where(dmu > max(dmu) * 0.05)[0] idmu_max, dmu_max = 0, 0 for i in high_deriv_pts: if dmu[i] > dmu_max and (i + 1 in high_deriv_pts) and (i - 1 in high_deriv_pts): idmu_max, dmu_max = i, dmu[i] if _larch.symtable.isgroup(group): group.e0 = energy[idmu_max + 1] return energy[idmu_max + 1]
def pre_edge( energy, mu, group=None, e0=None, step=None, nnorm=3, nvict=0, pre1=None, pre2=-50, norm1=100, norm2=None, _larch=None, **kws ): """pre edge subtraction, normalization for XAFS This performs a number of steps: 1. determine E0 (if not supplied) from max of deriv(mu) 2. fit a line of polymonial to the region below the edge 3. fit a polymonial to the region above the edge 4. extrapolae the two curves to E0 to determine the edge jump Arguments ---------- energy: array of x-ray energies, in eV mu: array of mu(E) group: output group e0: edge energy, in eV. If None, it will be determined here. step: edge jump. If None, it will be determined here. pre1: low E range (relative to E0) for pre-edge fit pre2: high E range (relative to E0) for pre-edge fit nvict: energy exponent to use for pre-edg fit. See Note norm1: low E range (relative to E0) for post-edge fit norm2: high E range (relative to E0) for post-edge fit nnorm: number of terms in polynomial (that is, 1+degree) for post-edge, normalization curve. Default=3 (quadratic) Returns ------- if group is None, the return value is (edge_step, e0) if gorup is not None, the return value is None, and the following data is put into the supplied group: e0 energy origin edge_step edge step norm normalized mu(E) pre_edge determined pre-edge curve post_edge determined post-edge, normalization curve Notes ----- nvict gives an exponent to the energy term for the pre-edge fit. That is, a line (m * energy + b) is fit to mu(energy)*energy**nvict over the pr-edge regin, energy=[e0+pre1, e0+pre2]. """ if _larch is None: raise Warning("cannot remove pre_edge -- larch broken?") if e0 is None or e0 < energy[0] or e0 > energy[-1]: e0 = find_e0(energy, mu, group=group, _larch=_larch) energy = remove_dups(energy) p1 = min(np.where(energy >= e0 - 10.0)[0]) p2 = max(np.where(energy <= e0 + 10.0)[0]) ie0 = np.where(energy - e0 == min(abs(energy[p1:p2] - e0)))[0][0] if pre1 is None: pre1 = min(energy) - e0 if norm2 is None: norm2 = max(energy) - e0 p1 = min(np.where(energy >= pre1 + e0)[0]) p2 = max(np.where(energy <= pre2 + e0)[0]) if p2 - p1 < 2: p2 = min(len(energy), p1 + 2) omu = mu * energy ** nvict coefs = polyfit(energy[p1:p2], omu[p1:p2], 1) pre_edge = (coefs[0] * energy + coefs[1]) * energy ** (-nvict) # normalization p1 = min(np.where(energy >= norm1 + e0)[0]) p2 = max(np.where(energy <= norm2 + e0)[0]) if p2 - p1 < 2: p2 = min(len(energy), p1 + 2) coefs = polyfit(energy[p1:p2], omu[p1:p2], nnorm) post_edge = 0 for n, c in enumerate(reversed(list(coefs))): post_edge += c * energy ** (n - nvict) edge_step = post_edge[ie0] - pre_edge[ie0] norm = (mu - pre_edge) / edge_step if _larch.symtable.isgroup(group): group.e0 = e0 group.norm = norm group.edge_step = edge_step group.pre_edge = pre_edge group.post_edge = post_edge return edge_step, e0
def autobk(energy, mu, group=None, rbkg=1, nknots=None, e0=None, edge_step=None, kmin=0, kmax=None, kweight=1, dk=0, win='hanning', k_std=None, chi_std=None, nfft=2048, kstep=0.05, pre_edge_kws=None, debug=False, _larch=None, **kws): """Use Autobk algorithm to remove XAFS background Options are: rbkg -- distance out to which the chi(R) is minimized """ if _larch is None: raise Warning("cannot calculate autobk spline -- larch broken?") if 'kw' in kws: kweight = kws['kw'] energy = remove_dups(energy) # if e0 or edge_step are not specified, get them, either from the # passed-in group or from running pre_edge() if edge_step is None: if _larch.symtable.isgroup(group) and hasattr(group, 'edge_step'): edge_step = group.edge_step if e0 is None: if _larch.symtable.isgroup(group) and hasattr(group, 'e0'): e0 = group.e0 if e0 is None or edge_step is None: # need to run pre_edge: pre_kws = dict(nnorm=3, nvict=0, pre1=None, pre2=-50., norm1=100., norm2=None) if pre_edge_kws is not None: pre_kws.update(pre_edge_kws) edge_step, e0 = pre_edge(energy, mu, group=group, _larch=_larch, **pre_kws) # get array indices for rkbg and e0: irbkg, ie0 ie0 = index_nearest(energy, e0) rgrid = np.pi/(kstep*nfft) if rbkg < 2*rgrid: rbkg = 2*rgrid irbkg = int(1.01 + rbkg/rgrid) # save ungridded k (kraw) and grided k (kout) # and ftwin (*k-weighting) for FT in residual kraw = np.sqrt(ETOK*(energy[ie0:] - e0)) if kmax is None: kmax = max(kraw) kout = kstep * np.arange(int(1.01+kmax/kstep), dtype='float64') # interpolate provided chi(k) onto the kout grid if chi_std is not None and k_std is not None: chi_std = np.interp(kout, k_std, chi_std) ftwin = kout**kweight * ftwindow(kout, xmin=kmin, xmax=kmax, window=win, dx=dk) # calc k-value and initial guess for y-values of spline params nspline = max(4, min(60, 2*int(rbkg*(kmax-kmin)/np.pi) + 1)) spl_y = np.zeros(nspline) spl_k = np.zeros(nspline) spl_e = np.zeros(nspline) for i in range(nspline): q = kmin + i*(kmax-kmin)/(nspline - 1) ik = index_nearest(kraw, q) i1 = min(len(kraw)-1, ik + 5) i2 = max(0, ik - 5) spl_k[i] = kraw[ik] spl_e[i] = energy[ik+ie0] spl_y[i] = (2*mu[ik+ie0] + mu[i1+ie0] + mu[i2+ie0] ) / 4.0 # get spline represention: knots, coefs, order=3 # coefs will be varied in fit. knots, coefs, order = splrep(spl_k, spl_y) # set fit parameters from initial coefficients ncoefs = len(coefs) params = Group() for i in range(ncoefs): name = FMT_COEF % i p = Parameter(coefs[i], name=name, vary=i<len(spl_y)) p._getval() setattr(params, name, p) initbkg, initchi = spline_eval(kraw, mu[ie0:], knots, coefs, order, kout) fitkws = dict(ncoefs=len(coefs), chi_std=chi_std, knots=knots, order=order, kraw=kraw, mu=mu[ie0:], irbkg=irbkg, kout=kout, ftwin=ftwin, nfft=nfft) # do fit fit = Minimizer(__resid, params, fcn_kws=fitkws, _larch=_larch, toler=1.e-4) fit.leastsq() # write final results coefs = [getattr(params, FMT_COEF % i) for i in range(ncoefs)] bkg, chi = spline_eval(kraw, mu[ie0:], knots, coefs, order, kout) obkg = np.zeros(len(mu)) obkg[:ie0] = mu[:ie0] obkg[ie0:] = bkg if _larch.symtable.isgroup(group): group.bkg = obkg group.chie = (mu-obkg)/edge_step group.k = kout group.chi = chi/edge_step if debug: group.spline_params = params ix_bkg = np.zeros(len(mu)) ix_bkg[:ie0] = mu[:ie0] ix_bkg[ie0:] = initbkg group.init_bkg = ix_bkg group.init_chi = initchi/edge_step group.spline_e = spl_e group.spline_y = np.array([coefs[i] for i in range(nspline)]) group.spline_yinit = spl_y