Ejemplo n.º 1
0
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]
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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