def from_lm_params(cls, params: lm.Parameters) -> NRGParams: d = {} for k1, k2 in zip( ['gamma', 'theta', 'center', 'amp', 'lin', 'const', 'lin_occ'], ['g', 'theta', 'mid', 'amp', 'lin', 'const', 'occ_lin']): par = params.get(k2, None) if par is not None: v = par.value elif k1 == 'gamma': v = 0 # This will cause issues if not set somewhere else, but no better choice here. else: v = 0 if k1 != 'amp' else 1 # Most things should default to zero except for amp d[k1] = v return cls(**d)
class FeffPathGroup(Group): def __init__(self, filename=None, label=None, s02=None, degen=None, e0=None, ei=None, deltar=None, sigma2=None, third=None, fourth=None, _larch=None, **kws): kwargs = dict(name='FeffPath: %s' % filename) kwargs.update(kws) Group.__init__(self, **kwargs) self.filename = filename self.params = None self.label = label self.spline_coefs = None def_degen = 1 self._feffdat = None if filename is not None: self._feffdat = FeffDatFile(filename=filename) self.geom = self._feffdat.geom def_degen = self._feffdat.degen if self.label is None: self.label = self.__geom2label() self.degen = def_degen if degen is None else degen self.s02 = 1.0 if s02 is None else s02 self.e0 = 0.0 if e0 is None else e0 self.ei = 0.0 if ei is None else ei self.deltar = 0.0 if deltar is None else deltar self.sigma2 = 0.0 if sigma2 is None else sigma2 self.third = 0.0 if third is None else third self.fourth = 0.0 if fourth is None else fourth self.k = None self.chi = None if self._feffdat is not None: self.create_spline_coefs() def __geom2label(self): """generate label by hashing path geometry""" rep = [] if self.geom is not None: for atom in self.geom: rep.extend(atom) if self._feffdat is not None: rep.append(self._feffdat.degen) rep.append(self._feffdat.reff) for attr in ('s02', 'e0', 'ei', 'deltar', 'sigma2', 'third', 'fourth'): rep.append(getattr(self, attr, '_')) s = "|".join([str(i) for i in rep]) return "p%s" % (b32hash(s)[:8].lower()) def __copy__(self): return FeffPathGroup(filename=self.filename, s02=self.s02, degen=self.degen, e0=self.e0, ei=self.ei, deltar=self.deltar, sigma2=self.sigma2, third=self.third, fourth=self.fourth) def __deepcopy__(self, memo): return FeffPathGroup(filename=self.filename, s02=self.s02, degen=self.degen, e0=self.e0, ei=self.ei, deltar=self.deltar, sigma2=self.sigma2, third=self.third, fourth=self.fourth) @property def reff(self): return self._feffdat.reff @reff.setter def reff(self, val): pass @property def nleg(self): return self._feffdat.nleg @nleg.setter def nleg(self, val): pass @property def rmass(self): return self._feffdat.rmass @rmass.setter def rmass(self, val): pass def __repr__(self): if self.filename is not None: return '<FeffPath Group %s>' % self.filename return '<FeffPath Group (empty)>' def create_path_params(self, params=None): """ create Path Parameters within the current lmfit.Parameters namespace """ if params is not None: self.params = params if self.params is None: self.params = Parameters() if self.params._asteval.symtable.get('sigma2_debye', None) is None: add_sigma2funcs(self.params) if self.label is None: self.label = self.__geom2label() self.store_feffdat() for pname in PATH_PARS: val = getattr(self, pname) attr = 'value' if isinstance(val, str): attr = 'expr' kws = {'vary': False, attr: val} parname = fix_varname(PATHPAR_FMT % (pname, self.label)) self.params.add(parname, **kws) def create_spline_coefs(self): """pre-calculate spline coefficients for feff data""" self.spline_coefs = {} fdat = self._feffdat self.spline_coefs['pha'] = UnivariateSpline(fdat.k, fdat.pha, s=0) self.spline_coefs['amp'] = UnivariateSpline(fdat.k, fdat.amp, s=0) self.spline_coefs['rep'] = UnivariateSpline(fdat.k, fdat.rep, s=0) self.spline_coefs['lam'] = UnivariateSpline(fdat.k, fdat.lam, s=0) def store_feffdat(self): """stores data about this Feff path in the Parameters symbol table for use as `reff` and in sigma2 calcs """ symtab = self.params._asteval.symtable symtab['feffpath'] = self._feffdat symtab['reff'] = self._feffdat.reff def __path_params(self, **kws): """evaluate path parameter value. Returns (degen, s02, e0, ei, deltar, sigma2, third, fourth) """ # put 'reff' and '_feffdat' into the symboltable so that # they can be used in constraint expressions self.store_feffdat() if self.params is None: self.create_path_params() out = [] for pname in PATH_PARS: val = kws.get(pname, None) parname = fix_varname(PATHPAR_FMT % (pname, self.label)) if val is None: val = self.params[parname]._getval() out.append(val) return out def path_paramvals(self, **kws): (deg, s02, e0, ei, delr, ss2, c3, c4) = self.__path_params() return dict(degen=deg, s02=s02, e0=e0, ei=ei, deltar=delr, sigma2=ss2, third=c3, fourth=c4) def report(self): "return text report of parameters" tmpvals = self.__path_params() pathpars = {} for pname in ('degen', 's02', 'e0', 'deltar', 'sigma2', 'third', 'fourth', 'ei'): parname = fix_varname(PATHPAR_FMT % (pname, self.label)) if parname in self.params: pathpars[pname] = (self.params[parname].value, self.params[parname].stderr) geomlabel = ' atom x y z ipot' geomformat = ' %4s % .4f, % .4f, % .4f %i' out = [' Path %s, Feff.dat file = %s' % (self.label, self.filename)] out.append(geomlabel) for atsym, iz, ipot, amass, x, y, z in self.geom: s = geomformat % (atsym, x, y, z, ipot) if ipot == 0: s = "%s (absorber)" % s out.append(s) stderrs = {} out.append(' {:7s}= {:s}'.format('reff', gformat(self._feffdat.reff))) for pname in ('degen', 's02', 'e0', 'r', 'deltar', 'sigma2', 'third', 'fourth', 'ei'): val = strval = getattr(self, pname, 0) parname = fix_varname(PATHPAR_FMT % (pname, self.label)) std = None if pname == 'r': parname = fix_varname(PATHPAR_FMT % ('deltar', self.label)) par = self.params.get(parname, None) val = par.value + self._feffdat.reff strval = 'reff + ' + getattr(self, 'deltar', 0) std = par.stderr else: if pname in pathpars: val, std = pathpars[pname] else: par = self.params.get(parname, None) if par is not None: val = par.value std = par.stderr if std is None or std <= 0: svalue = gformat(val) else: svalue = "{:s} +/-{:s}".format(gformat(val), gformat(std)) if pname == 's02': pname = 'n*s02' svalue = " {:7s}= {:s}".format(pname, svalue) if isinstance(strval, str): svalue = "{:s} '{:s}'".format(svalue, strval) if val == 0 and pname in ('third', 'fourth', 'ei'): continue out.append(svalue) return '\n'.join(out) def _calc_chi(self, k=None, kmax=None, kstep=None, degen=None, s02=None, e0=None, ei=None, deltar=None, sigma2=None, third=None, fourth=None, debug=False, interp='cubic', **kws): """calculate chi(k) with the provided parameters""" fdat = self._feffdat if fdat.reff < 0.05: print('reff is too small to calculate chi(k)') return # make sure we have a k array if k is None: if kmax is None: kmax = 30.0 kmax = min(max(fdat.k), kmax) if kstep is None: kstep = 0.05 k = kstep * np.arange(int(1.01 + kmax / kstep), dtype='float64') reff = fdat.reff # get values for all the path parameters (degen, s02, e0, ei, deltar, sigma2, third, fourth) = \ self.__path_params(degen=degen, s02=s02, e0=e0, ei=ei, deltar=deltar, sigma2=sigma2, third=third, fourth=fourth) # create e0-shifted energy and k, careful to look for |e0| ~= 0. en = k * k - e0 * ETOK if min(abs(en)) < SMALL: try: en[np.where(abs(en) < 2 * SMALL)] = SMALL except ValueError: pass # q is the e0-shifted wavenumber q = np.sign(en) * np.sqrt(abs(en)) # lookup Feff.dat values (pha, amp, rep, lam) if interp.startswith('lin'): pha = np.interp(q, fdat.k, fdat.pha) amp = np.interp(q, fdat.k, fdat.amp) rep = np.interp(q, fdat.k, fdat.rep) lam = np.interp(q, fdat.k, fdat.lam) else: pha = self.spline_coefs['pha'](q) amp = self.spline_coefs['amp'](q) rep = self.spline_coefs['rep'](q) lam = self.spline_coefs['lam'](q) if debug: self.debug_k = q self.debug_pha = pha self.debug_amp = amp self.debug_rep = rep self.debug_lam = lam # p = complex wavenumber, and its square: pp = (rep + 1j / lam)**2 + 1j * ei * ETOK p = np.sqrt(pp) # the xafs equation: cchi = np.exp(-2 * reff * p.imag - 2 * pp * (sigma2 - pp * fourth / 3) + 1j * (2 * q * reff + pha + 2 * p * (deltar - 2 * sigma2 / reff - 2 * pp * third / 3))) cchi = degen * s02 * amp * cchi / (q * (reff + deltar)**2) cchi[0] = 2 * cchi[1] - cchi[2] # outputs: self.k = k self.p = p self.chi = cchi.imag self.chi_imag = -cchi.real
class FeffPathGroup(Group): def __init__(self, filename=None, _larch=None, label=None, s02=None, degen=None, e0=None, ei=None, deltar=None, sigma2=None, third=None, fourth=None, **kws): kwargs = dict(name='FeffPath: %s' % filename) kwargs.update(kws) Group.__init__(self, **kwargs) self._larch = _larch self.filename = filename self.params = None self.label = label self.spline_coefs = None def_degen = 1 self._feffdat = None if filename is not None: self._feffdat = FeffDatFile(filename=filename, _larch=_larch) self.geom = self._feffdat.geom def_degen = self._feffdat.degen if self.label is None: self.label = self.__geom2label() self.degen = def_degen if degen is None else degen self.s02 = 1.0 if s02 is None else s02 self.e0 = 0.0 if e0 is None else e0 self.ei = 0.0 if ei is None else ei self.deltar = 0.0 if deltar is None else deltar self.sigma2 = 0.0 if sigma2 is None else sigma2 self.third = 0.0 if third is None else third self.fourth = 0.0 if fourth is None else fourth self.k = None self.chi = None if self._feffdat is not None: self.create_spline_coefs() def __geom2label(self): """generate label by hashing path geometry""" rep = [] if self.geom is not None: for atom in self.geom: rep.extend(atom) if self._feffdat is not None: rep.append(self._feffdat.degen) rep.append(self._feffdat.reff) for attr in ('s02', 'e0', 'ei', 'deltar', 'sigma2', 'third', 'fourth'): rep.append(getattr(self, attr, '_')) s = "|".join([str(i) for i in rep]) return "p%s" % (b32hash(s)[:8].lower()) def __copy__(self): return FeffPathGroup(filename=self.filename, _larch=self._larch, s02=self.s02, degen=self.degen, e0=self.e0, ei=self.ei, deltar=self.deltar, sigma2=self.sigma2, third=self.third, fourth=self.fourth) def __deepcopy__(self, memo): return FeffPathGroup(filename=self.filename, _larch=self._larch, s02=self.s02, degen=self.degen, e0=self.e0, ei=self.ei, deltar=self.deltar, sigma2=self.sigma2, third=self.third, fourth=self.fourth) @property def reff(self): return self._feffdat.reff @reff.setter def reff(self, val): pass @property def nleg(self): return self._feffdat.nleg @nleg.setter def nleg(self, val): pass @property def rmass(self): return self._feffdat.rmass @rmass.setter def rmass(self, val): pass def __repr__(self): if self.filename is not None: return '<FeffPath Group %s>' % self.filename return '<FeffPath Group (empty)>' def create_path_params(self): """ create Path Parameters within the current fiteval """ self.params = Parameters(asteval=self._larch.symtable._sys.fiteval) if self.label is None: self.label = self.__geom2label() self.store_feffdat() for pname in PATH_PARS: val = getattr(self, pname) attr = 'value' if isinstance(val, six.string_types): attr = 'expr' kws = {'vary': False, attr: val} parname = fix_varname(PATHPAR_FMT % (pname, self.label)) self.params.add(parname, **kws) def create_spline_coefs(self): """pre-calculate spline coefficients for feff data""" self.spline_coefs = {} fdat = self._feffdat self.spline_coefs['pha'] = UnivariateSpline(fdat.k, fdat.pha, s=0) self.spline_coefs['amp'] = UnivariateSpline(fdat.k, fdat.amp, s=0) self.spline_coefs['rep'] = UnivariateSpline(fdat.k, fdat.rep, s=0) self.spline_coefs['lam'] = UnivariateSpline(fdat.k, fdat.lam, s=0) def store_feffdat(self): """stores data about this Feff path in the fiteval symbol table for use as `reff` and in sigma2 calcs """ fiteval = self._larch.symtable._sys.fiteval fdat = self._feffdat fiteval.symtable['feffpath'] = fdat fiteval.symtable['reff'] = fdat.reff return fiteval def __path_params(self, **kws): """evaluate path parameter value. Returns (degen, s02, e0, ei, deltar, sigma2, third, fourth) """ # put 'reff' and '_feffdat' into the symboltable so that # they can be used in constraint expressions, and get # fiteval evaluator self.store_feffdat() if self.params is None: self.create_path_params() out = [] for pname in PATH_PARS: val = kws.get(pname, None) parname = fix_varname(PATHPAR_FMT % (pname, self.label)) if val is None: val = self.params[parname]._getval() out.append(val) return out def path_paramvals(self, **kws): (deg, s02, e0, ei, delr, ss2, c3, c4) = self.__path_params() return dict(degen=deg, s02=s02, e0=e0, ei=ei, deltar=delr, sigma2=ss2, third=c3, fourth=c4) def report(self): "return text report of parameters" (deg, s02, e0, ei, delr, ss2, c3, c4) = self.__path_params() geomlabel = ' atom x y z ipot' geomformat = ' %4s % .4f, % .4f, % .4f %i' out = [' Path %s, Feff.dat file = %s' % (self.label, self.filename)] out.append(geomlabel) for atsym, iz, ipot, amass, x, y, z in self.geom: s = geomformat % (atsym, x, y, z, ipot) if ipot == 0: s = "%s (absorber)" % s out.append(s) stderrs = {} out.append(' {:7s}= {:.5f}'.format('reff', self._feffdat.reff)) for pname in ('degen', 's02', 'e0', 'r', 'deltar', 'sigma2', 'third', 'fourth', 'ei'): val = strval = getattr(self, pname, 0) parname = fix_varname(PATHPAR_FMT % (pname, self.label)) std = None if pname == 'r': parname = fix_varname(PATHPAR_FMT % ('deltar', self.label)) par = self.params.get(parname, None) val = par.value + self._feffdat.reff strval = 'reff + ' + getattr(self, 'deltar', 0) std = par.stderr else: par = self.params.get(parname, None) if par is not None: val = par.value std = par.stderr if std is None or std <= 0: svalue = "{: 5f}".format(val) else: svalue = "{: 5f} +/- {:5f}".format(val, std) if pname == 's02': pname = 'n*s02' svalue = " {:7s}= {:s}".format(pname, svalue) if isinstance(strval, six.string_types): svalue = "{:s} '{:s}'".format(svalue, strval) if val == 0 and pname in ('third', 'fourth', 'ei'): continue out.append(svalue) return '\n'.join(out) def _calc_chi(self, k=None, kmax=None, kstep=None, degen=None, s02=None, e0=None, ei=None, deltar=None, sigma2=None, third=None, fourth=None, debug=False, interp='cubic', **kws): """calculate chi(k) with the provided parameters""" fdat = self._feffdat if fdat.reff < 0.05: self._larch.writer.write('reff is too small to calculate chi(k)') return # make sure we have a k array if k is None: if kmax is None: kmax = 30.0 kmax = min(max(fdat.k), kmax) if kstep is None: kstep = 0.05 k = kstep * np.arange(int(1.01 + kmax/kstep), dtype='float64') reff = fdat.reff # get values for all the path parameters (degen, s02, e0, ei, deltar, sigma2, third, fourth) = \ self.__path_params(degen=degen, s02=s02, e0=e0, ei=ei, deltar=deltar, sigma2=sigma2, third=third, fourth=fourth) # create e0-shifted energy and k, careful to look for |e0| ~= 0. en = k*k - e0*ETOK if min(abs(en)) < SMALL: try: en[np.where(abs(en) < 2*SMALL)] = SMALL except ValueError: pass # q is the e0-shifted wavenumber q = np.sign(en)*np.sqrt(abs(en)) # lookup Feff.dat values (pha, amp, rep, lam) if interp.startswith('lin'): pha = np.interp(q, fdat.k, fdat.pha) amp = np.interp(q, fdat.k, fdat.amp) rep = np.interp(q, fdat.k, fdat.rep) lam = np.interp(q, fdat.k, fdat.lam) else: pha = self.spline_coefs['pha'](q) amp = self.spline_coefs['amp'](q) rep = self.spline_coefs['rep'](q) lam = self.spline_coefs['lam'](q) if debug: self.debug_k = q self.debug_pha = pha self.debug_amp = amp self.debug_rep = rep self.debug_lam = lam # p = complex wavenumber, and its square: pp = (rep + 1j/lam)**2 + 1j * ei * ETOK p = np.sqrt(pp) # the xafs equation: cchi = np.exp(-2*reff*p.imag - 2*pp*(sigma2 - pp*fourth/3) + 1j*(2*q*reff + pha + 2*p*(deltar - 2*sigma2/reff - 2*pp*third/3) )) cchi = degen * s02 * amp * cchi / (q*(reff + deltar)**2) cchi[0] = 2*cchi[1] - cchi[2] # outputs: self.k = k self.p = p self.chi = cchi.imag self.chi_imag = -cchi.real
params2 = Parameters() params2.add('alpha', value= 1, min=0) params2.add('power', value= 1) result2 = minimize(fcn2min, params2, args=(x, data)) # calculate final result final1 = data + result1.residual final2 = data + result2.residual # write error report report_errors(params1) pp = pprint.PrettyPrinter(indent=4) pp.pprint(params1) print params1.get('alpha') p=params1.get('alpha') print "standard error ", p.stderr total= np.sum(result1.residual) print "sum of residuals: ", total #print "xtol: ", result1.xtol print "reduced chi-square: ", result1.redchi #print "asteval", result1.asteval print "message:", result1.message print "ier:", result1.ier print "chisqr:", result1.chisqr print "redchi:", result1.redchi pp.pprint(result1)