def _peakparams(self, paramgroup=None, **kws): """evaluate peak parameter values """ # sigma, amplitude, center out = [] for parname in ('amplitude', 'sigma', 'center'): val = getattr(self, parname) if parname in kws: if kws[parname] is not None: val = kws[parname] if isinstance(val, six.string_types): thispar = Parameter(expr=val, _larch=self._larch) setattr(self, parname, thispar) val = getattr(self, parname) out.append(param_value(val)) return out
def autobk(energy, mu=None, 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, nclamp=4, clamp_lo=1, clamp_hi=1, calc_uncertainties=False, _larch=None, **kws): """Use Autobk algorithm to remove XAFS background Parameters: ----------- energy: 1-d array of x-ray energies, in eV, or group mu: 1-d array of mu(E) group: output group (and input group for e0 and edge_step). rbkg: distance (in Ang) for chi(R) above which the signal is ignored. Default = 1. e0: edge energy, in eV. If None, it will be determined. edge_step: edge step. If None, it will be determined. pre_edge_kws: keyword arguments to pass to pre_edge() nknots: number of knots in spline. If None, it will be determined. kmin: minimum k value [0] kmax: maximum k value [full data range]. kweight: k weight for FFT. [1] dk: FFT window window parameter. [0] win: FFT window function name. ['hanning'] nfft: array size to use for FFT [2048] kstep: k step size to use for FFT [0.05] k_std: optional k array for standard chi(k). chi_std: optional chi array for standard chi(k). nclamp: number of energy end-points for clamp [2] clamp_lo: weight of low-energy clamp [1] clamp_hi: weight of high-energy clamp [1] calc_uncertaintites: Flag to calculate uncertainties in mu_0(E) and chi(k) [False] Output arrays are written to the provided group. Follows the 'First Argument Group' convention. """ msg = _larch.writer.write if 'kw' in kws: kweight = kws.pop('kw') if len(kws) > 0: msg('Unrecognized a:rguments for autobk():\n') msg(' %s\n' % (', '.join(kws.keys()))) return energy, mu, group = parse_group_args(energy, members=('energy', 'mu'), defaults=(mu,), group=group, fcn_name='autobk') 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() group = set_xafsGroup(group, _larch=_larch) if edge_step is None and isgroup(group, 'edge_step'): edge_step = group.edge_step if e0 is None and isgroup(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) pre_edge(energy, mu, group=group, _larch=_larch, **pre_kws) if e0 is None: e0 = group.e0 if edge_step is None: edge_step = group.edge_step if e0 is None or edge_step is None: msg('autobk() could not determine e0 or edge_step!: trying running pre_edge first\n') return # get array indices for rkbg and e0: irbkg, ie0 ie0 = index_of(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 enpe = energy[ie0:] - e0 kraw = np.sign(enpe)*np.sqrt(ETOK*abs(enpe)) if kmax is None: kmax = max(kraw) else: kmax = max(0, min(max(kraw), kmax)) kout = kstep * np.arange(int(1.01+kmax/kstep), dtype='float64') iemax = min(len(energy), 2+index_of(energy, e0+kmax*kmax/ETOK)) - 1 # 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) # pre-load FT window 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 nspl = max(4, min(128, 2*int(rbkg*(kmax-kmin)/np.pi) + 1)) spl_y, spl_k, spl_e = np.zeros(nspl), np.zeros(nspl), np.zeros(nspl) for i in range(nspl): q = kmin + i*(kmax-kmin)/(nspl - 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 params = Group() for i in range(len(coefs)): 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[:iemax-ie0+1], mu[ie0:iemax+1], knots, coefs, order, kout) # do fit fit = Minimizer(__resid, params, _larch=_larch, toler=1.e-4, fcn_kws = dict(ncoefs=len(coefs), chi_std=chi_std, knots=knots, order=order, kraw=kraw[:iemax-ie0+1], mu=mu[ie0:iemax+1], irbkg=irbkg, kout=kout, ftwin=ftwin, kweight=kweight, nfft=nfft, nclamp=nclamp, clamp_lo=clamp_lo, clamp_hi=clamp_hi)) fit.leastsq() # write final results coefs = [getattr(params, FMT_COEF % i) for i in range(len(coefs))] bkg, chi = spline_eval(kraw[:iemax-ie0+1], mu[ie0:iemax+1], knots, coefs, order, kout) obkg = np.copy(mu) obkg[ie0:ie0+len(bkg)] = bkg # outputs to group group = set_xafsGroup(group, _larch=_larch) group.bkg = obkg group.chie = (mu-obkg)/edge_step group.k = kout group.chi = chi/edge_step # now fill in 'autobk_details' group params.init_bkg = np.copy(mu) params.init_bkg[ie0:ie0+len(bkg)] = initbkg params.init_chi = initchi/edge_step params.knots_e = spl_e params.knots_y = np.array([coefs[i] for i in range(nspl)]) params.init_knots_y = spl_y params.nfev = params.fit_details.nfev params.kmin = kmin params.kmax = kmax group.autobk_details = params # uncertainties in mu0 and chi: fairly slow!! if HAS_UNCERTAIN and calc_uncertainties: vbest, vstd = [], [] for n in fit.var_names: par = getattr(params, n) vbest.append(par.value) vstd.append(par.stderr) uvars = uncertainties.correlated_values(vbest, params.covar) # uncertainty in bkg (aka mu0) # note that much of this is working around # limitations in the uncertainty package that make it # 1. take an argument list (not array) # 2. work on returned scalars (but not arrays) # 3. not handle kw args and *args well (so use # of global "index" is important here) nkx = iemax-ie0 + 1 def my_dsplev(*args): coefs = np.array(args) return splev(kraw[:nkx], [knots, coefs, order])[index] fdbkg = uncertainties.wrap(my_dsplev) dmu0 = [fdbkg(*uvars).std_dev() for index in range(len(bkg))] group.delta_bkg = np.zeros(len(mu)) group.delta_bkg[ie0:ie0+len(bkg)] = np.array(dmu0) # uncertainty in chi (see notes above) def my_dchi(*args): coefs = np.array(args) b,chi = spline_eval(kraw[:nkx], mu[ie0:iemax+1], knots, coefs, order, kout) return chi[index] fdchi = uncertainties.wrap(my_dchi) dchi = [fdchi(*uvars).std_dev() for index in range(len(kout))] group.delta_chi = np.array(dchi)/edge_step
def do_fit(self, which): if which == 'testrun': folder = self.testrun else: folder = self.baseline data = read_xdi(join(self.path, 'UO2.chik'), _larch=self._larch) gds = Group(amp=Parameter(1, vary=True, _larch=self._larch), enot=Parameter(0.01, vary=True, _larch=self._larch), sso=Parameter(0.003, vary=True, _larch=self._larch), ssu=Parameter(0.003, vary=True, _larch=self._larch), sso2=Parameter(0.003, vary=True, _larch=self._larch), dro=Parameter(0.0001, vary=True, _larch=self._larch), dru=Parameter(0.0001, vary=True, _larch=self._larch), dro2=Parameter(0.0001, vary=True, _larch=self._larch), nu=Parameter(12, vary=True, _larch=self._larch), no2=Parameter(expr='2*nu', _larch=self._larch), _larch=self._larch) paths = list() paths.append( feffpath( realpath(join(folder, "feff0001.dat")), # 1st shell O SS s02='amp', e0='enot', sigma2='sso', deltar='dro', _larch=self._larch)) # paths.append(feffpath(realpath(join(folder, "feff0002.dat")), # triangle in first shell # s02 = 'amp', # e0 = 'enot', # sigma2 = 'sso*1.5', # deltar = 'dro*(1+sqrt(2))/2', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0003.dat")), # 2nd shell U SS degen=1, s02='amp*nu', e0='enot', sigma2='ssu', deltar='dru', _larch=self._larch)) # paths.append(feffpath(realpath(join(folder, "feff0004.dat")), # 1st shell, longer triangle # s02 = 'amp', # e0 = 'enot', # sigma2 = '2*sso', # deltar = '2*dro', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0006.dat")), # 3rd shell O SS degen=1, s02='amp*no2', #s02 = 'amp*nu*2', e0='enot', sigma2='sso2', deltar='dro2', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0007.dat") ), # 1st shell, non-forward linear through absorber s02='amp', e0='enot', sigma2='sso2', deltar='dro2', _larch=self._larch)) paths.append( feffpath( realpath(join( folder, "feff0008.dat")), # 1st shell forward through absorber s02='amp', e0='enot', sigma2='2*sso', deltar='2*dro', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0009.dat")), # rattle in 1st shell s02='amp', e0='enot', sigma2='2*sso', deltar='2*dro', _larch=self._larch)) trans = feffit_transform(kmin=3, kmax=11, kw=(2, 1, 3), dk=1, window='hanning', rmin=1.25, rmax=4.3, _larch=self._larch) dset = feffit_dataset(data=data, pathlist=paths, transform=trans, _larch=self._larch) fit = feffit(gds, dset, _larch=self._larch) if self.doplot: offset = max(dset.data.chir_mag) _newplot(dset.data.r, dset.data.chir_mag + offset, xmax=8, win=2, xlabel=r'$R \rm\,(\AA)$', label='data', ylabel=r'$|\chi(R)| \rm\,(\AA^{-3})$', title='Fit to ' + self.folder, show_legend=True, _larch=self._larch) _plot(dset.model.r, dset.model.chir_mag + offset, label='fit', win=2, _larch=self._larch) _plot(dset.data.r, dset.data.chir_re, label='data', win=2, _larch=self._larch) _plot(dset.model.r, dset.model.chir_re, label='fit', win=2, _larch=self._larch) #end if if self.verbose: print feffit_report(fit, _larch=self._larch) #end if return fit
def do_fit(self, which, firstshell=False, fittest='baseline'): firstshell = False # no 1st shell fit for this material if which == 'testrun': folder = self.testrun elif which == 'baseline': folder = self.baseline else: folder = realpath(join(self.folder, fittest, which)) #endif data = read_xdi(join(self.path, 'uranyl.chik'), _larch=self._larch) gds = Group( amp=Parameter(1, vary=True, _larch=self._larch), enot=Parameter(1e-7, vary=True, _larch=self._larch), #enot = Parameter(1e-7, vary=True, _larch=self._larch, min=0, max=13), enoteq=Parameter(expr='enot', _larch=self._larch), deloax=Parameter(1e-7, vary=True, _larch=self._larch), deloeq=Parameter(1e-7, vary=True, _larch=self._larch), sigoax=Parameter(0.003, vary=True, _larch=self._larch), sigoeq=Parameter(0.003, vary=True, _larch=self._larch), nax=Parameter(2.0, vary=False, _larch=self._larch), neq=Parameter(6, vary=False, _larch=self._larch), #, min=0, max=12), _larch=self._larch) paths = list() paths.append( feffpath( realpath(join(folder, "feff0001.dat")), # axial oxygen degen=1, s02='amp*nax', e0='enot', sigma2='sigoax', deltar='deloax', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0003.dat")), # equatorial oxygen degen=1, s02='amp*neq', e0='enoteq', sigma2='sigoeq', deltar='deloeq', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0008.dat")), # axial oxygen, rattle degen=1, s02='amp*nax', e0='enot', sigma2='sigoax*4', deltar='deloax*2', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0009.dat")), # axial oxygen, non-forward degen=1, s02='amp*nax', e0='enot', sigma2='sigoax', deltar='deloax*2', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0010.dat")), # axial oxygen, forward degen=1, s02='amp*nax', e0='enot', sigma2='sigoax', deltar='deloax*2', _larch=self._larch)) trans = feffit_transform(kmin=3, kmax=11, kw=(2, 1, 3), dk=1, window='hanning', rmin=1.0, rmax=3.2, _larch=self._larch) dset = feffit_dataset(data=data, pathlist=paths, transform=trans, _larch=self._larch) fit = feffit(gds, dset, _larch=self._larch) if self.doplot: offset = max(dset.data.chir_mag) _newplot(dset.data.r, dset.data.chir_mag + offset, xmax=8, win=2, xlabel=r'$R \rm\,(\AA)$', label='data', ylabel=r'$|\chi(R)| \rm\,(\AA^{-3})$', title='Fit to ' + self.folder, show_legend=True, _larch=self._larch) _plot(dset.model.r, dset.model.chir_mag + offset, label='fit', win=2, _larch=self._larch) _plot(dset.data.r, dset.data.chir_re, label='data', win=2, _larch=self._larch) _plot(dset.model.r, dset.model.chir_re, label='fit', win=2, _larch=self._larch) #end if if self.verbose: print feffit_report(fit, _larch=self._larch) #end if shells = '' if firstshell: shells = '_1st' write_ascii(join(self.folder, fittest, "fit_" + which + ".k"), dset.data.k, dset.data.chi, dset.model.chi, labels="r data_mag fit_mag data_re fit_re", _larch=self._larch) write_ascii(join(self.folder, fittest, "fit_" + which + ".r"), dset.data.r, dset.data.chir_mag, dset.model.chir_mag, dset.data.chir_re, dset.model.chir_re, labels="r data_mag fit_mag data_re fit_re", _larch=self._larch) renderer = pystache.Renderer() with open(join(self.folder, fittest, 'fit_' + which + '.gp'), 'w') as inp: inp.write( renderer.render_path( 'plot.mustache', # gnuplot mustache file { 'material': 'uranyl', 'model': which, 'fittest': fittest, 'shells': shells, 'kmin': 3, 'kmax': 11, 'rmin': 1.0, 'rmax': 3.2, 'offset': 1, })) return fit
def do_fit(self, which): if which == 'testrun': folder = self.testrun else: folder = self.baseline data = read_xdi(join(self.path, 'NiO.chik'), _larch=self._larch) if hasattr(data, 'wavenumber'): data.k = data.wavenumber gds = Group( amp=Parameter(1, vary=True, _larch=self._larch), enot=Parameter(0.01, vary=True, _larch=self._larch), alpha=Parameter(0.0001, vary=True, _larch=self._larch), sso=Parameter(0.003, vary=True, _larch=self._larch), ssni=Parameter(0.003, vary=True, _larch=self._larch), sso2=Parameter(0.003, vary=True, _larch=self._larch), #sso3 = Parameter(0.003, vary=True, _larch=self._larch), ssni2=Parameter(0.003, vary=True, _larch=self._larch), #ssni3 = Parameter(0.003, vary=True, _larch=self._larch), #ssni4 = Parameter(0.003, vary=True, _larch=self._larch), _larch=self._larch) paths = list() paths.append( feffpath( realpath(join(folder, "feff0001.dat")), # 1st shell O SS s02='amp', e0='enot', sigma2='sso', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0002.dat")), # 2nd shell Ni SS s02='amp', e0='enot', sigma2='ssni', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0003.dat")), # O-O triangle s02='amp', e0='enot', sigma2='1.5*sso', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0004.dat")), # O-Ni triangle s02='amp', e0='enot', sigma2='sso+ssni', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0005.dat")), # 3rd shell O SS s02='amp', e0='enot', sigma2='sso2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0006.dat")), # 4th shell Ni SS s02='amp', e0='enot', sigma2='ssni2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0007.dat")), # O-O non-forward linear s02='amp', e0='enot', sigma2='sso*2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0008.dat")), # O-Ni forward scattering s02='amp', e0='enot', sigma2='ssni2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0009.dat")), # O-O forward through absorber s02='amp', e0='enot', sigma2='sso*2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0010.dat")), # O-Ni-O double forward s02='amp', e0='enot', sigma2='ssni2', deltar='alpha*reff', _larch=self._larch)) trans = feffit_transform(kmin=3, kmax=15.938, kw=(2, 1, 3), dk=1, window='hanning', rmin=1.5, rmax=4.2, _larch=self._larch) dset = feffit_dataset(data=data, pathlist=paths, transform=trans, _larch=self._larch) fit = feffit(gds, dset, _larch=self._larch) if self.doplot: offset = 0.6 * max(dset.data.chir_mag) _newplot(dset.data.r, dset.data.chir_mag + offset, xmax=8, win=2, xlabel=r'$R \rm\,(\AA)$', label='data', ylabel=r'$|\chi(R)| \rm\,(\AA^{-3})$', title='Fit to ' + self.folder, show_legend=True, _larch=self._larch) _plot(dset.model.r, dset.model.chir_mag + offset, label='fit', win=2, _larch=self._larch) _plot(dset.data.r, dset.data.chir_re, label='data', win=2, _larch=self._larch) _plot(dset.model.r, dset.model.chir_re, label='fit', win=2, _larch=self._larch) #end if if self.verbose: print feffit_report(fit, _larch=self._larch) #end if return fit
def do_fit(self, which, firstshell=False, fittest='baseline'): if which == 'testrun': folder = self.testrun elif which == 'baseline': folder = self.baseline else: folder = realpath(join(self.folder, fittest, which)) #endif data = read_xdi(join(self.path, 'FeS2.chik'), _larch=self._larch) gds = Group(amp=Parameter(1, vary=True, _larch=self._larch), enot=Parameter(1e-7, vary=True, _larch=self._larch), ss=Parameter(0.003, vary=True, _larch=self._larch), _larch=self._larch) if firstshell: gds.delr = Parameter(1e-7, vary=True, _larch=self._larch) dr1param = 'delr' else: gds.alpha = Parameter(1e-7, vary=True, _larch=self._larch) gds.ss2 = Parameter(0.003, vary=True, _larch=self._larch) gds.ss3 = Parameter(expr='ss2', _larch=self._larch) gds.ssfe = Parameter(0.003, vary=True, _larch=self._larch) dr1param = 'alpha*reff' paths = list() paths.append( feffpath( realpath(join(folder, "feff0001.dat")), # 1st shell S SS s02='amp', e0='enot', sigma2='ss', deltar=dr1param, _larch=self._larch)) if not firstshell: paths.append( feffpath( realpath(join(folder, "feff0002.dat")), # 2nd shell S SS s02='amp', e0='enot', sigma2='ss2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0003.dat")), # 3rd shell S SS s02='amp', e0='enot', sigma2='ss3', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0004.dat")), # 4th shell Fe SS s02='amp', e0='enot', sigma2='ssfe', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0005.dat")), # S-S triangle s02='amp', e0='enot', sigma2='ss*1.5', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0006.dat")), # S-Fe triangle s02='amp', e0='enot', sigma2='ss/2+ssfe', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0012.dat")), # S-S non-forward linear s02='amp', e0='enot', sigma2='ss*2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0013.dat")), # S-S forward scattering s02='amp', e0='enot', sigma2='ss*2', deltar='alpha*reff', _larch=self._larch)) paths.append( feffpath( realpath(join(folder, "feff0014.dat")), # S-S rattle s02='amp', e0='enot', sigma2='ss*4', deltar='alpha*reff', _larch=self._larch)) rx = 4.2 if firstshell: rx = 2.3 trans = feffit_transform(kmin=3, kmax=12.956, kw=(2, 1, 3), dk=1, window='hanning', rmin=1.2, rmax=rx, _larch=self._larch) dset = feffit_dataset(data=data, pathlist=paths, transform=trans, _larch=self._larch) fit = feffit(gds, dset, _larch=self._larch) if self.doplot: offset = 0.6 * max(dset.data.chir_mag) _newplot(dset.data.r, dset.data.chir_mag + offset, xmax=8, xlabel=r'$R \rm\,(\AA)$', label='data', ylabel=r'$|\chi(R)| \rm\,(\AA^{-3})$', title='Fit to ' + self.folder, show_legend=True, _larch=self._larch) _plot(dset.model.r, dset.model.chir_mag + offset, label='fit', _larch=self._larch) _plot(dset.data.r, dset.data.chir_re, label='data', _larch=self._larch) _plot(dset.model.r, dset.model.chir_re, label='fit', _larch=self._larch) #end if if self.verbose: print feffit_report(fit, _larch=self._larch) #end if shells = '' if firstshell: shells = '_1st' write_ascii(join(self.folder, fittest, "fit_" + which + shells + ".k"), dset.data.k, dset.data.chi, dset.model.chi, labels="r data_mag fit_mag data_re fit_re", _larch=self._larch) write_ascii(join(self.folder, fittest, "fit_" + which + shells + ".r"), dset.data.r, dset.data.chir_mag, dset.model.chir_mag, dset.data.chir_re, dset.model.chir_re, labels="r data_mag fit_mag data_re fit_re", _larch=self._larch) renderer = pystache.Renderer() with open(join(self.folder, fittest, 'fit_' + which + shells + '.gp'), 'w') as inp: inp.write( renderer.render_path( 'plot.mustache', # gnuplot mustache file { 'material': 'FeS2', 'model': which, 'fittest': fittest, 'shells': shells, 'kmin': 3, 'kmax': 12.956, 'rmin': 1.2, 'rmax': rx, 'offset': 1, })) return fit
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
def autobk(energy, mu=None, 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, nclamp=4, clamp_lo=1, clamp_hi=1, calc_uncertainties=True, err_sigma=1, _larch=None, **kws): """Use Autobk algorithm to remove XAFS background Parameters: ----------- energy: 1-d array of x-ray energies, in eV, or group mu: 1-d array of mu(E) group: output group (and input group for e0 and edge_step). rbkg: distance (in Ang) for chi(R) above which the signal is ignored. Default = 1. e0: edge energy, in eV. If None, it will be determined. edge_step: edge step. If None, it will be determined. pre_edge_kws: keyword arguments to pass to pre_edge() nknots: number of knots in spline. If None, it will be determined. kmin: minimum k value [0] kmax: maximum k value [full data range]. kweight: k weight for FFT. [1] dk: FFT window window parameter. [0] win: FFT window function name. ['hanning'] nfft: array size to use for FFT [2048] kstep: k step size to use for FFT [0.05] k_std: optional k array for standard chi(k). chi_std: optional chi array for standard chi(k). nclamp: number of energy end-points for clamp [2] clamp_lo: weight of low-energy clamp [1] clamp_hi: weight of high-energy clamp [1] calc_uncertaintites: Flag to calculate uncertainties in mu_0(E) and chi(k) [True] err_sigma: sigma level for uncertainties in mu_0(E) and chi(k) [1] Output arrays are written to the provided group. Follows the 'First Argument Group' convention. """ msg = _larch.writer.write if 'kw' in kws: kweight = kws.pop('kw') if len(kws) > 0: msg('Unrecognized a:rguments for autobk():\n') msg(' %s\n' % (', '.join(kws.keys()))) return energy, mu, group = parse_group_args(energy, members=('energy', 'mu'), defaults=(mu,), group=group, fcn_name='autobk') if len(energy.shape) > 1: energy = energy.squeeze() if len(mu.shape) > 1: mu = mu.squeeze() 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() group = set_xafsGroup(group, _larch=_larch) if edge_step is None and isgroup(group, 'edge_step'): edge_step = group.edge_step if e0 is None and isgroup(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) pre_edge(energy, mu, group=group, _larch=_larch, **pre_kws) if e0 is None: e0 = group.e0 if edge_step is None: edge_step = group.edge_step if e0 is None or edge_step is None: msg('autobk() could not determine e0 or edge_step!: trying running pre_edge first\n') return # get array indices for rkbg and e0: irbkg, ie0 ie0 = index_of(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 enpe = energy[ie0:] - e0 kraw = np.sign(enpe)*np.sqrt(ETOK*abs(enpe)) if kmax is None: kmax = max(kraw) else: kmax = max(0, min(max(kraw), kmax)) kout = kstep * np.arange(int(1.01+kmax/kstep), dtype='float64') iemax = min(len(energy), 2+index_of(energy, e0+kmax*kmax/ETOK)) - 1 # 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) # pre-load FT window 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 nspl = max(4, min(128, 2*int(rbkg*(kmax-kmin)/np.pi) + 1)) spl_y, spl_k, spl_e = np.zeros(nspl), np.zeros(nspl), np.zeros(nspl) for i in range(nspl): q = kmin + i*(kmax-kmin)/(nspl - 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 params = Group() for i in range(len(coefs)): 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[:iemax-ie0+1], mu[ie0:iemax+1], knots, coefs, order, kout) # do fit fit = Minimizer(__resid, params, _larch=_larch, toler=1.e-4, fcn_kws = dict(ncoefs=len(coefs), chi_std=chi_std, knots=knots, order=order, kraw=kraw[:iemax-ie0+1], mu=mu[ie0:iemax+1], irbkg=irbkg, kout=kout, ftwin=ftwin, kweight=kweight, nfft=nfft, nclamp=nclamp, clamp_lo=clamp_lo, clamp_hi=clamp_hi)) fit.leastsq() # write final results coefs = [getattr(params, FMT_COEF % i) for i in range(len(coefs))] bkg, chi = spline_eval(kraw[:iemax-ie0+1], mu[ie0:iemax+1], knots, coefs, order, kout) obkg = np.copy(mu) obkg[ie0:ie0+len(bkg)] = bkg # outputs to group group = set_xafsGroup(group, _larch=_larch) group.bkg = obkg group.chie = (mu-obkg)/edge_step group.k = kout group.chi = chi/edge_step # now fill in 'autobk_details' group params.init_bkg = np.copy(mu) params.init_bkg[ie0:ie0+len(bkg)] = initbkg params.init_chi = initchi/edge_step params.knots_e = spl_e params.knots_y = np.array([coefs[i] for i in range(nspl)]) params.init_knots_y = spl_y params.nfev = params.fit_details.nfev params.kmin = kmin params.kmax = kmax group.autobk_details = params # uncertainties in mu0 and chi: can be fairly slow. if calc_uncertainties: nchi = len(chi) nmue = iemax-ie0 + 1 redchi = params.chi_reduced covar = params.covar / redchi jac_chi = np.zeros(nchi*nspl).reshape((nspl, nchi)) jac_bkg = np.zeros(nmue*nspl).reshape((nspl, nmue)) cvals, cerrs = [], [] for i in range(len(coefs)): par = getattr(params, FMT_COEF % i) cvals.append(getattr(par, 'value', 0.0)) cdel = getattr(par, 'stderr', 0.0) if cdel is None: cdel = 0.0 cerrs.append(cdel/2.0) cvals = np.array(cvals) cerrs = np.array(cerrs) # find derivatives by hand! _k = kraw[:nmue] _m = mu[ie0:iemax+1] for i in range(nspl): cval0 = cvals[i] cvals[i] = cval0 + cerrs[i] bkg1, chi1 = spline_eval(_k, _m, knots, cvals, order, kout) cvals[i] = cval0 - cerrs[i] bkg2, chi2 = spline_eval(_k, _m, knots, cvals, order, kout) cvals[i] = cval0 jac_chi[i] = (chi1 - chi2) / (2*cerrs[i]) jac_bkg[i] = (bkg1 - bkg2) / (2*cerrs[i]) dfchi = np.zeros(nchi) dfbkg = np.zeros(nmue) for i in range(nspl): for j in range(nspl): dfchi += jac_chi[i]*jac_chi[j]*covar[i,j] dfbkg += jac_bkg[i]*jac_bkg[j]*covar[i,j] prob = 0.5*(1.0 + erf(err_sigma/np.sqrt(2.0))) dchi = t.ppf(prob, nchi-nspl) * np.sqrt(dfchi*redchi) dbkg = t.ppf(prob, nmue-nspl) * np.sqrt(dfbkg*redchi) group.delta_chi = dchi group.delta_bkg = 0.0*mu group.delta_bkg[ie0:ie0+len(dbkg)] = dbkg