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()
def predict_pileup(self, scale=None): """ predict pileup for a spectrum, save to 'pileup' attribute """ counts = self.counts.astype(int)/1.e6 pileup = np.convolve(counts, counts, 'full') en = self.energy ex = en[0] + np.arange(len(pileup))*(en[1] - en[0]) if scale is None: npts = len(en) nhalf = int(npts/2) + 1 nmost = int(7*npts/8.0) - 1 scale = self.counts[nhalf:].sum()/ pileup[nhalf:nmost].sum() self.pileup = interp(ex, scale*pileup, self.energy, kind='cubic') self.pileup_scale = scale
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. """ if _larch is None: raise Warning("cannot deconvolve -- larch broken?") 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) en = en - en[0] estep = max(0.001, 0.001 * int(min(en[1:] - en[:-1]) * 1000.0)) npts = 1 + int(max(en) / estep) x = np.arange(npts) * estep y = interp(en, mu, x, kind='cubic', _larch=_larch) 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', _larch=_larch) group = set_xafsGroup(group, _larch=_larch) group.deconv = out
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' """ if _larch is None: raise Warning("cannot xas_convolve -- larch broken?") 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', _larch=_larch) 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', _larch=_larch) group = set_xafsGroup(group, _larch=_larch) group.conv = out / k.sum()
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)
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) en = en - en[0] estep = max(0.001, 0.001*int(min(en[1:]-en[:-1])*1000.0)) npts = 1 + 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 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
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)