Exemple #1
0
def xas_convolve(energy, norm=None, group=None, form='lorentzian',
                   esigma=1.0, eshift=0.0, _larch=None):
    """
    convolve a normalized mu(E) spectra with a Lorentzian or Gaussian peak
    shape, degrading separation of XANES features.

    This is provided as a complement to xas_deconvolve, and to deliberately
    broaden spectra to compare with spectra measured at lower resolution.

    Arguments
    ----------
    energy:   array of x-ray energies (in eV) or XAFS data group
    norm:     array of normalized mu(E)
    group:    output group
    form:     form of deconvolution function. One of
              'lorentzian' or  'gaussian' ['lorentzian']
    esigma    energy sigma (in eV) to pass to gaussian() or lorentzian() [1.0]
    eshift    energy shift (in eV) to apply to result [0]

    Returns
    -------
    None
       The array 'conv' will be written to the output group.

    Notes
    -----
       Follows the First Argument Group convention, using group members named
       'energy' and 'norm'
    """

    energy, mu, group = parse_group_args(energy, members=('energy', 'norm'),
                                         defaults=(norm,), group=group,
                                         fcn_name='xas_convolve')
    eshift = eshift + 0.5 * esigma

    en  = remove_dups(energy)
    en  = en - en[0]
    estep = max(0.001, 0.001*int(min(en[1:]-en[:-1])*1000.0))

    npad = 1 + int(max(estep*2.01, 50*esigma)/estep)

    npts = npad  + int(max(en) / estep)

    x = np.arange(npts)*estep
    y = interp(en, mu, x, kind='cubic')

    kernel = lorentzian
    if form.lower().startswith('g'):
        kernel = gaussian

    k = kernel(x, center=0, sigma=esigma)
    ret = np.convolve(y, k, mode='full')

    out = interp(x-eshift, ret[:len(x)], en, kind='cubic')

    group = set_xafsGroup(group, _larch=_larch)
    group.conv = out / k.sum()
Exemple #2
0
def merge_groups(grouplist,
                 master=None,
                 xarray='energy',
                 yarray='mu',
                 kind='cubic',
                 trim=True,
                 calc_yerr=True,
                 _larch=None):
    """merge arrays from a list of groups.

    Arguments
    ---------
     grouplist   list of groups to merge
     master      group to use for common x arrary [None -> 1st group]
     xarray      name of x-array for merge ['energy']
     yarray      name of y-array for merge ['mu']
     kind        interpolation kind ['cubic']
     trim        whether to trim to the shortest energy range [True]
     calc_yerr   whether to use the variance in the input as yerr [True]

    Returns
    --------
     group with x-array and y-array containing merged data.

    """
    if master is None:
        master = grouplist[0]

    xout = getattr(master, xarray)
    xmins = [min(xout)]
    xmaxs = [max(xout)]
    yvals = []

    for g in grouplist:
        x = getattr(g, xarray)
        y = getattr(g, yarray)
        yvals.append(interp(x, y, xout, kind=kind))
        xmins.append(min(x))
        xmaxs.append(max(x))

    yvals = np.array(yvals)
    yave = yvals.mean(axis=0)
    ystd = yvals.std(axis=0)

    if trim:
        xmin = min(xmins)
        xmax = min(xmaxs)
        ixmin = index_of(xout, xmin)
        ixmax = index_of(xout, xmax)
        xout = xout[ixmin:ixmax]
        yave = yave[ixmin:ixmax]
        ystd = ystd[ixmin:ixmax]

    grp = Group()
    setattr(grp, xarray, xout)
    setattr(grp, yarray, yave)
    setattr(grp, yarray + '_std', ystd)
    return grp
Exemple #3
0
def groups2csv(grouplist, filename,
               x='energy', y='norm', _larch=None):
    """save data from a list of groups to a CSV file

    Arguments
    ---------
    grouplist  list of groups to save arrays from
    filname    name of output file
    x          name of group member to use for `x`
    y          name of group member to use for `y`

    """
    def get_label(grp):
        'get label for group'
        for attr in ('filename', 'label', 'name', 'file', '__name__'):
            o = getattr(grp, attr, None)
            if o is not None:
                return o
        return repr(o)

    ngroups = len(grouplist)
    x0 = getattr(grouplist[0], x)
    npts = len(x0)
    columns = [x0, getattr(grouplist[0], y)]
    labels = [x, get_label(grouplist[0]) ]

    for g in grouplist[1:]:

        labels.append(get_label(g))
        _x = getattr(g, x)
        _y = getattr(g, y)

        if ((len(_x) != npts) or (abs(_x -x0)).sum() > 1.0):
            columns.append(interp(_x, _y, x0))
        else:
            columns.append(_y)

    buff = ["# %s" % ', '.join(labels)]
    for i in range(npts):
        buff.append(', '.join(["%.6f" % s[i] for s in columns]))

    buff.append('')
    with open(filename, 'w') as fh:
        fh.write("\n".join(buff))

    print("Wrote %i groups to %s" % (len(columns)-1, filename))
Exemple #4
0
def groups2csv(grouplist, filename,
               x='energy', y='norm', _larch=None):
    """save data from a list of groups to a CSV file

    Arguments
    ---------
    grouplist  list of groups to save arrays from
    filname    name of output file
    x          name of group member to use for `x`
    y          name of group member to use for `y`

    """
    def get_label(grp):
        'get label for group'
        for attr in ('filename', 'label', 'name', 'file', '__name__'):
            o = getattr(grp, attr, None)
            if o is not None:
                return o
        return repr(o)

    ngroups = len(grouplist)
    x0 = getattr(grouplist[0], x)
    npts = len(x0)
    columns = [x0, getattr(grouplist[0], y)]
    labels = [x, get_label(grouplist[0]) ]

    for g in grouplist[1:]:

        labels.append(get_label(g))
        _x = getattr(g, x)
        _y = getattr(g, y)

        if ((len(_x) != npts) or (abs(_x -x0)).sum() > 1.0):
            columns.append(interp(_x, _y, x0))
        else:
            columns.append(_y)

    buff = ["# %s" % ', '.join(labels)]
    for i in range(npts):
        buff.append(', '.join(["%.6f" % s[i] for s in columns]))

    buff.append('')
    with open(filename, 'w') as fh:
        fh.write("\n".join(buff))

    print("Wrote %i groups to %s" % (len(columns)-1, filename))
Exemple #5
0
    def kk(self,
           energy=None,
           mu=None,
           z=None,
           edge='K',
           how='scalar',
           mback_kws=None):
        """
        Convert mu(E) data into f'(E) and f"(E).  f"(E) is made by
        matching mu(E) to the tabulated values of the imaginary part
        of the scattering factor (Cromer-Liberman), f'(E) is then
        obtained by performing a differential Kramers-Kronig transform
        on the matched f"(E).

          Attributes
            energy:     energy array
            mu:         array with mu(E) data
            z:          Z number of absorber
            edge:       absorption edge, usually 'K' or 'L3'
            mback_kws:  arguments for the mback algorithm

          Returns
            self.f1, self.f2:  CL values over on the input energy grid
            self.fp, self.fpp: matched and KK transformed data on the input energy grid

        References:
          * Cromer-Liberman: http://dx.doi.org/10.1063/1.1674266
          * KK computation: Ohta and Ishida, Applied Spectroscopy 42:6 (1988) 952-957
          * diffKK implementation: http://dx.doi.org/10.1103/PhysRevB.58.11215
          * MBACK (Weng, Waldo, Penner-Hahn): http://dx.doi.org/10.1086/303711
          * Lee and Xiang: http://dx.doi.org/10.1088/0004-637X/702/2/970

        """

        if type(energy).__name__ == 'ndarray': self.energy = energy
        if type(mu).__name__ == 'ndarray': self.mu = mu
        if z != None: self.z = z
        if edge != None: self.edge = edge
        if mback_kws != None: self.mback_kws = mback_kws

        if self.z == None:
            Exception("Z for absorber not provided for diffKK")
        if self.edge == None:
            Exception("absorption edge not provided for diffKK")

        mb_kws = dict(order=3,
                      z=self.z,
                      edge=self.edge,
                      e0=None,
                      leexiang=False,
                      tables='chantler',
                      fit_erfc=False,
                      return_f1=True)
        if self.mback_kws is not None:
            mb_kws.update(self.mback_kws)

        start = time.clock()

        mback(self.energy, self.mu, group=self, **mb_kws)

        ## interpolate matched data onto an even grid with an even number of elements (about 1 eV)
        npts = int(self.energy[-1] - self.energy[0]) + (
            int(self.energy[-1] - self.energy[0]) % 2)
        self.grid = np.linspace(self.energy[0], self.energy[-1], npts)
        fpp = interp(self.energy,
                     self.f2 - self.fpp,
                     self.grid,
                     fill_value=0.0)

        ## do difference KK
        if repr(how).startswith('sca'):
            fp = kkmclr_sca(self.grid, fpp)
        else:
            fp = kkmclr(self.grid, fpp)

        ## interpolate back to original grid and add diffKK result to f1 to make fp array
        self.fp = self.f1 + interp(self.grid, fp, self.energy, fill_value=0.0)

        ## clean up group
        #for att in ('normalization_function', 'weight', 'grid'):
        #    if hasattr(self, att): delattr(self, att)
        finish = time.clock()
        self.time_elapsed = float(finish - start)
Exemple #6
0
def xas_deconvolve(energy,
                   norm=None,
                   group=None,
                   form='lorentzian',
                   esigma=1.0,
                   eshift=0.0,
                   smooth=True,
                   sgwindow=None,
                   sgorder=3,
                   _larch=None):
    """XAS spectral deconvolution

    de-convolve a normalized mu(E) spectra with a peak shape, enhancing the
    intensity and separation of peaks of a XANES spectrum.

    The results can be unstable, and noisy, and should be used
    with caution!

    Arguments
    ----------
    energy:   array of x-ray energies (in eV) or XAFS data group
    norm:     array of normalized mu(E)
    group:    output group
    form:     functional form of deconvolution function. One of
              'gaussian' or 'lorentzian' [default]
    esigma    energy sigma to pass to gaussian() or lorentzian()
              [in eV, default=1.0]
    eshift    energy shift to apply to result. [in eV, default=0]
    smooth    whether to smooth result with savitzky_golay method [True]
    sgwindow  window size for savitzky_golay [found from data step and esigma]
    sgorder   order for savitzky_golay [3]

    Returns
    -------
    None
       The array 'deconv' will be written to the output group.

    Notes
    -----
       Support See First Argument Group convention, requiring group
       members 'energy' and 'norm'

       Smoothing with savitzky_golay() requires a window and order.  By
       default, window = int(esigma / estep) where estep is step size for
       the gridded data, approximately the finest energy step in the data.
    """
    energy, mu, group = parse_group_args(energy,
                                         members=('energy', 'norm'),
                                         defaults=(norm, ),
                                         group=group,
                                         fcn_name='xas_deconvolve')
    eshift = eshift + 0.5 * esigma

    en = remove_dups(energy)
    estep1 = int(0.1 * en[0]) * 2.e-5
    en = en - en[0]
    estep = max(estep1, 0.01 * int(min(en[1:] - en[:-1]) * 100.0))
    npts = 1 + int(max(en) / estep)
    if npts > 25000:
        npts = 25001
        estep = max(en) / 25000.0

    x = np.arange(npts) * estep
    y = interp(en, mu, x, kind='cubic')

    kernel = lorentzian
    if form.lower().startswith('g'):
        kernel = gaussian

    yext = np.concatenate((y, np.arange(len(y)) * y[-1]))

    ret, err = deconvolve(yext, kernel(x, center=0, sigma=esigma))
    nret = min(len(x), len(ret))

    ret = ret[:nret] * yext[nret - 1] / ret[nret - 1]
    if smooth:
        if sgwindow is None:
            sgwindow = int(1.0 * esigma / estep)

        sqwindow = int(sgwindow)
        if sgwindow < (sgorder + 1):
            sgwindow = sgorder + 2
        if sgwindow % 2 == 0:
            sgwindow += 1
        ret = savitzky_golay(ret, sgwindow, sgorder)

    out = interp(x + eshift, ret, en, kind='cubic')
    group = set_xafsGroup(group, _larch=_larch)
    group.deconv = out
Exemple #7
0
    def kk(self, energy=None, mu=None, z=None, edge='K', how='scalar', mback_kws=None):
        """
        Convert mu(E) data into f'(E) and f"(E).  f"(E) is made by
        matching mu(E) to the tabulated values of the imaginary part
        of the scattering factor (Cromer-Liberman), f'(E) is then
        obtained by performing a differential Kramers-Kronig transform
        on the matched f"(E).

          Attributes
            energy:     energy array
            mu:         array with mu(E) data
            z:          Z number of absorber
            edge:       absorption edge, usually 'K' or 'L3'
            mback_kws:  arguments for the mback algorithm

          Returns
            self.f1, self.f2:  CL values over on the input energy grid
            self.fp, self.fpp: matched and KK transformed data on the input energy grid

        References:
          * Cromer-Liberman: http://dx.doi.org/10.1063/1.1674266
          * KK computation: Ohta and Ishida, Applied Spectroscopy 42:6 (1988) 952-957
          * diffKK implementation: http://dx.doi.org/10.1103/PhysRevB.58.11215
          * MBACK (Weng, Waldo, Penner-Hahn): http://dx.doi.org/10.1086/303711
          * Lee and Xiang: http://dx.doi.org/10.1088/0004-637X/702/2/970

        """

        if type(energy).__name__ == 'ndarray': self.energy = energy
        if type(mu).__name__     == 'ndarray': self.mu     = mu
        if z    != None: self.z    = z
        if edge != None: self.edge = edge
        if mback_kws != None: self.mback_kws = mback_kws

        if self.z == None:
            Exception("Z for absorber not provided for diffKK")
        if self.edge == None:
            Exception("absorption edge not provided for diffKK")

        mb_kws = dict(order=3, z=self.z, edge=self.edge, e0=None, emin=None, emax=None,
                      whiteline=False, leexiang=False, tables='chantler',
                      fit_erfc=False, return_f1=True)
        if self.mback_kws is not None:
            mb_kws.update(self.mback_kws)

        start = time.clock()

        mback(self.energy, self.mu, group=self, _larch=self._larch, **mb_kws)

        ## interpolate matched data onto an even grid with an even number of elements (about 1 eV)
        npts = int(self.energy[-1] - self.energy[0]) + (int(self.energy[-1] - self.energy[0])%2)
        self.grid = np.linspace(self.energy[0], self.energy[-1], npts)
        fpp = interp(self.energy, self.f2-self.fpp, self.grid, fill_value=0.0)

        ## do difference KK
        if repr(how).startswith('sca'):
            fp = kkmclr_sca(self.grid, fpp)
        else:
            fp = kkmclr(self.grid, fpp)

        ## interpolate back to original grid and add diffKK result to f1 to make fp array
        self.fp = self.f1 + interp(self.grid, fp, self.energy, fill_value=0.0)

        ## clean up group
        #for att in ('normalization_function', 'weight', 'grid'):
        #    if hasattr(self, att): delattr(self, att)
        finish = time.clock()
        self.time_elapsed = float(finish-start)