def get_config(self, dgroup=None): """custom get_config to possibly inherit from Athena settings""" if dgroup is None: dgroup = self.controller.get_group() if dgroup is None: return self.get_defaultconfig() if hasattr(dgroup, self.configname): conf = getattr(dgroup, self.configname) else: conf = self.get_defaultconfig() if hasattr(dgroup, 'bkg_params'): # from Athena for attr in ('e0', 'pre1', 'pre2', 'norm1', 'norm2', 'nnorm'): conf[attr] = getattr(dgroup.bkg_params, attr, conf[attr]) conf['auto_step'] = (float( getattr(dgroup.bkg_params, 'fixstep', 0.0)) < 0.5) conf['edge_step'] = getattr(dgroup.bkg_params, 'step', conf['edge_step']) if conf['edge_step'] is None: conf['edge_step'] = getattr(dgroup, 'edge_step', conf['edge_step']) conf['atsym'] = getattr(dgroup, 'atsym', conf['atsym']) conf['edge'] = getattr(dgroup, 'edge', conf['edge']) if hasattr(dgroup, 'e0') and conf['atsym'] == '?': atsym, edge = guess_edge(dgroup.e0) conf['atsym'] = atsym conf['edge'] = edge if hasattr(dgroup, 'mback_params'): conf['atsym'] = getattr(dgroup.mback_params, 'atsym', conf['atsym']) conf['edge'] = getattr(dgroup.mback_params, 'edge', conf['edge']) setattr(dgroup, self.configname, conf) return conf
def fill_form(self, dgroup): """fill in form from a data group""" opts = self.get_config(dgroup) self.skip_process = True if dgroup.datatype == 'xas': self.plotone_op.SetChoices(list(PlotOne_Choices.keys())) self.plotsel_op.SetChoices(list(PlotSel_Choices.keys())) self.plotone_op.SetStringSelection(opts['plotone_op']) self.plotsel_op.SetStringSelection(opts['plotsel_op']) self.wids['e0'].SetValue(opts['e0']) edge_step = opts.get('edge_step', None) if edge_step is None: edge_step = 1.0 if hasattr(dgroup, 'e0') and opts['atsym'] == '?': atsym, edge = guess_edge(dgroup.e0) opts['atsym'] = atsym opts['edge'] = edge self.wids['step'].SetValue(edge_step) autoset_fs_increment(self.wids['step'], edge_step) for attr in ('pre1', 'pre2', 'norm1', 'norm2'): val = opts.get(attr, None) if val is not None: self.wids[attr].SetValue(val) self.set_nnorm_widget(opts.get('nnorm')) self.wids['nvict'].SetSelection(opts['nvict']) self.wids['showe0'].SetValue(opts['show_e0']) self.wids['auto_e0'].SetValue(opts['auto_e0']) self.wids['auto_step'].SetValue(opts['auto_step']) self.wids['edge'].SetStringSelection(opts['edge'].title()) self.wids['atsym'].SetStringSelection(opts['atsym'].title()) self.wids['norm_method'].SetStringSelection( opts['norm_method'].lower()) for attr in ('pre1', 'pre2', 'norm1', 'norm2', 'nnorm', 'edge', 'atsym', 'step', 'norm_method'): self.wids[attr].Enable() self.wids['scale'].Disable() else: self.plotone_op.SetChoices(list(PlotOne_Choices_nonxas.keys())) self.plotsel_op.SetChoices(list(PlotSel_Choices_nonxas.keys())) self.wids['scale'].SetValue(opts['scale']) for attr in ('pre1', 'pre2', 'norm1', 'norm2', 'nnorm', 'edge', 'atsym', 'step', 'norm_method'): self.wids[attr].Disable() self.wids['scale'].Enable() frozen = opts.get('is_frozen', False) if hasattr(dgroup, 'is_frozen'): frozen = dgroup.is_frozen self.wids['is_frozen'].SetValue(frozen) self._set_frozen(frozen) wx.CallAfter(self.unset_skip_process)
def onNormMethod(self, evt=None): method = self.wids['norm_method'].GetStringSelection().lower() self.update_config({'norm_method': method}) if method.startswith('mback'): dgroup = self.controller.get_group() cur_elem = self.wids['atsym'].GetStringSelection() if hasattr(dgroup, 'e0') and cur_elem == 'H': atsym, edge = guess_edge(dgroup.e0) self.wids['edge'].SetStringSelection(edge) self.wids['atsym'].SetStringSelection(atsym) self.update_config({'edge': edge, 'atsym': atsym}) time.sleep(0.01) wx.CallAfter(self.onReprocess)
def add_group(self, group, signal=None): """add Larch group (presumably XAFS data) to Athena project""" from larch.xafs import pre_edge x = athena_array(group, 'energy') yname = None for _name in ('mu', 'mutrans', 'mufluor'): if hasattr(group, _name): yname = _name break if x is None or yname is None: raise ValueError("can only add XAFS data to Athena project") y = athena_array(group, yname) i0 = athena_array(group, 'i0') if signal is not None: signal = athena_array(group, signal) elif yname in ('mu', 'mutrans'): sname = None for _name in ('i1', 'itrans'): if hasattr(group, _name): sname = _name break if sname is not None: signal = athena_array(group, sname) hashkey = make_hashkey() while hashkey in self.groups: hashkey = make_hashkey() # fill in data from pre-edge subtraction if not (hasattr(group, 'e0') and hasattr(group, 'edge_step')): pre_edge(group, _larch=self._larch) group.args = make_athena_args(group, hashkey) # fix parameters that are incompatible with athena group.args['bkg_nnorm'] = max(1, min(3, int(group.args['bkg_nnorm']))) _elem, _edge = guess_edge(group.e0) group.args['bkg_z'] = _elem group.x = x group.y = y group.i0 = i0 group.signal = signal # add a selection flag group.sel = 1 self.groups[hashkey] = group
def guess_metadata(dgroup): """guess some metadata from data group headers and array labels""" out = { 'en_arrayname': 'None', 'i0_arrayname': 'None', 'it_arrayname': 'None', 'if_arrayname': 'None', 'ir_arrayname': 'None' } alabels = ['None'] + dgroup.array_labels alabels.reverse() e0 = None for lab in alabels: llab = lab.lower() if 'ener' in llab: out['en_arrayname'] = lab if 'i0' in llab or 'imon' in llab: out['i0_arrayname'] = lab if 'it' in llab or 'i1' in llab or 'mut' in llab: out['it_arrayname'] = lab if 'if' in llab or 'fl' in llab or 'muf' in llab: out['if_arrayname'] = lab if 'iref' in llab or 'i2' in llab or 'mur' in llab: out['ir_arrayname'] = lab out['has_reference'] = out['ir_arrayname'] is not 'None' for line in dgroup.header: line = line.strip() if len(line) < 4: continue if line[0] in '#*%;!': line = line[1:].strip() line = line.replace('||', ':').replace('=', ':').replace('#', ':') words = line.split(':', 1) if len(words) > 1: key = words[0].strip().lower() val = words[1].strip() if 'e0' in key: e0 = val.split()[0] if 'elem' in key: if 'sym' in key: out['elem_sym'] = val elif 'edge' in key: out['edge'] = val if 'mono' in key: if 'name' in key: out['mono_name'] = val elif 'spac' in key: out['d_spacing'] = val.split()[0] if 'sample' in key: if 'name' in key: out['sample_name'] = val elif 'prep' in key: out['sample_prep'] = val elif 'form' in key: out['sample_formula'] = val elif 'notes' in key: out['sample_notes'] = val elif 'desc' in key: out['sample_notes'] = val if 'scan' in key and 'start_time' in key: dtime = guess_datetime(val) if dtime is not None: out['collection_date'] = fmttime(dtime) if e0 is not None and out.get('elem_sym', None) is None: try: elem, edge = guess_edge(float(e0)) except: elem, edge = None, None if elem is not None: out['elem_sym'] = elem out['edge'] = edge return out
def test_guess_edge(): vals = ((1607.22, 'Al', 'K'), (2260.35, 'P', 'K'), (2411.97, 'S', 'K'), (2559.74, 'S', 'K'), (2867.76, 'Mo', 'L1'), (3696.58, 'K', 'K'), (3971.10, 'Ca', 'K'), (4421.73, 'Sc', 'K'), (4765.58, 'Xe', 'L3'), (6278.32, 'La', 'L1'), (6375.02, 'Mn', 'K'), (6447.24, 'Pr', 'L2'), (6745.03, 'Nd', 'L2'), (6831.54, 'Pr', 'L1'), (6897.72, 'Fe', 'K'), (7074.41, 'Fe', 'K'), (7297.04, 'Sm', 'L2'), (7981.74, 'Gd', 'L2'), (8111.00, 'Ho', 'L3'), (8289.68, 'Ni', 'K'), (8787.12, 'Cu', 'K'), (8893.41, 'Cu', 'K'), (9032.98, 'Cu', 'K'), (9690.75, 'Zn', 'K'), (9694.53, 'Zn', 'K'), (10354.47, 'Ga', 'K'), (10442.38, 'Ga', 'K'), (10455.03, 'Ga', 'K'), (11150.30, 'Ge', 'K'), (11963.31, 'Re', 'L2'), (12774.25, 'Se', 'K'), (13268.59, 'Pt', 'L2'), (13971.48, 'Po', 'L3'), (14009.96, 'Kr', 'K'), (14192.88, 'Hg', 'L2'), (14248.22, 'At', 'L3'), (14601.50, 'Rn', 'L3'), (14754.45, 'Tl', 'L2'), (15058.15, 'Fr', 'L3'), (15529.31, 'Rb', 'K'), (15846.94, 'Ac', 'L3'), (16800.91, 'At', 'L2'), (16923.31, 'Y', 'K'), (17235.13, 'Y', 'K'), (17452.53, 'Y', 'K'), (17455.89, 'Y', 'K'), (18327.81, 'Zr', 'K'), (18478.71, 'Ra', 'L2'), (18895.14, 'Nb', 'K'), (18917.70, 'Nb', 'K'), (19015.14, 'Nb', 'K'), (20259.03, 'Mo', 'K'), (20325.21, 'Pa', 'L2'), (20329.70, 'Pa', 'L2'), (20489.15, 'Th', 'L1'), (20520.48, 'Th', 'L1'), (20905.52, 'Tc', 'K'), (20997.74, 'Tc', 'K'), (21606.63, 'Np', 'L2'), (21894.47, 'Ru', 'K'), (22424.97, 'Np', 'L1'), (23101.67, 'Pu', 'L1'), (24929.69, 'Pd', 'K'), (25507.23, 'Ag', 'K'), (26291.15, 'Cd', 'K'), (26523.45, 'Cd', 'K'), (26730.45, 'Cd', 'K'), (27496.51, 'In', 'K'), (27534.86, 'In', 'K'), (28524.18, 'In', 'K'), (29220.68, 'Sn', 'K'), (29563.18, 'Sn', 'K'), (30148.46, 'Sb', 'K'), (30178.67, 'Sb', 'K'), (30692.73, 'Sb', 'K'), (31006.32, 'Sb', 'K'), (31375.27, 'Te', 'K'), (31511.43, 'Te', 'K'), (31539.46, 'Te', 'K'), (31549.18, 'Te', 'K'), (31781.82, 'Te', 'K'), (32113.33, 'Te', 'K'), (32529.16, 'I', 'K'), (32769.91, 'I', 'K'), (32945.96, 'I', 'K'), (33162.58, 'I', 'K'), (33861.76, 'I', 'K'), (34282.18, 'Xe', 'K'), (34616.82, 'Xe', 'K'), (35048.42, 'Xe', 'K'), (35451.22, 'Cs', 'K'), (35732.84, 'Cs', 'K'), (36226.88, 'Cs', 'K'), (37011.63, 'Ba', 'K'), (37119.11, 'Ba', 'K'), (37687.55, 'Ba', 'K'), (38255.21, 'La', 'K'), (38534.03, 'La', 'K'), (39383.09, 'La', 'K'), (39597.61, 'La', 'K'), (39985.10, 'Ce', 'K'), (40189.85, 'Ce', 'K'), (40299.94, 'Ce', 'K'), (40803.37, 'Ce', 'K'), (41062.46, 'Ce', 'K'), (41563.00, 'Pr', 'K'), (41604.41, 'Pr', 'K'), (41914.19, 'Pr', 'K'), (41947.64, 'Pr', 'K'), (42090.33, 'Pr', 'K'), (42995.34, 'Nd', 'K'), (43296.60, 'Nd', 'K'), (44306.16, 'Nd', 'K'), (44470.03, 'Pm', 'K'), (45936.49, 'Pm', 'K'), (45992.17, 'Pm', 'K'), (45998.94, 'Pm', 'K'), (47495.02, 'Sm', 'K'), (47674.10, 'Sm', 'K'), (47942.94, 'Eu', 'K'), (48996.75, 'Eu', 'K'), (49840.90, 'Gd', 'K'), (50129.53, 'Gd', 'K'), (50525.35, 'Gd', 'K'), (51211.59, 'Tb', 'K'), (51574.05, 'Tb', 'K'), (51872.86, 'Tb', 'K'), (52198.93, 'Tb', 'K'), (52448.10, 'Tb', 'K'), (53243.79, 'Dy', 'K'), (53409.52, 'Dy', 'K'), (53945.61, 'Dy', 'K'), (53970.83, 'Dy', 'K'), (55202.28, 'Ho', 'K'), (55573.56, 'Ho', 'K'), (56150.05, 'Ho', 'K'), (56361.18, 'Ho', 'K'), (56372.13, 'Ho', 'K'), (58278.64, 'Er', 'K'), (58712.03, 'Tm', 'K'), (58804.02, 'Tm', 'K'), (59170.93, 'Tm', 'K'), (59295.42, 'Tm', 'K'), (59777.61, 'Tm', 'K'), (59795.79, 'Tm', 'K'), (60196.31, 'Tm', 'K'), (60733.44, 'Yb', 'K'), (60829.65, 'Yb', 'K'), (60877.42, 'Yb', 'K'), (60958.45, 'Yb', 'K'), (64355.51, 'Hf', 'K'), (64528.43, 'Hf', 'K'), (64807.76, 'Hf', 'K'), (67037.59, 'Ta', 'K'), (67684.42, 'Ta', 'K'), (68153.83, 'Ta', 'K'), (68190.34, 'Ta', 'K'), (68223.59, 'Ta', 'K'), (68281.45, 'Ta', 'K'), (68454.23, 'Ta', 'K'), (68645.09, 'W', 'K'), (69098.00, 'W', 'K'), (69375.45, 'W', 'K'), (69629.24, 'W', 'K'), (69917.35, 'W', 'K'), (70279.29, 'W', 'K'), (71077.63, 'Re', 'K'), (71181.95, 'Re', 'K'), (71218.00, 'Re', 'K'), (71726.69, 'Re', 'K'), (72635.91, 'Re', 'K'), (73524.97, 'Os', 'K'), (73609.73, 'Os', 'K'), (73924.75, 'Os', 'K'), (74544.48, 'Os', 'K'), (75531.12, 'Ir', 'K'), (76370.20, 'Ir', 'K'), (76460.23, 'Ir', 'K'), (77749.62, 'Pt', 'K'), (78466.63, 'Pt', 'K'), (79382.39, 'Pt', 'K'), (79752.40, 'Au', 'K'), (80068.68, 'Au', 'K'), (81540.62, 'Au', 'K'), (82353.66, 'Hg', 'K'), (82571.70, 'Hg', 'K'), (82672.30, 'Hg', 'K'), (82932.53, 'Hg', 'K'), (83155.18, 'Hg', 'K'), (83452.92, 'Hg', 'K'), (83669.34, 'Hg', 'K'), (83823.59, 'Hg', 'K'), (84319.11, 'Tl', 'K'), (84434.41, 'Tl', 'K'), (84538.14, 'Tl', 'K'), (85183.11, 'Tl', 'K'), (85213.28, 'Tl', 'K'), (85276.74, 'Tl', 'K'), (85352.80, 'Tl', 'K'), (85377.82, 'Tl', 'K')) for en, elem, edge in vals: _elem, _edge = guess_edge(en) assert elem == _elem assert edge == _edge _elem, _edge = guess_edge(1608, edges=['K', 'L1', 'L2', 'L3', 'M5', 'M3']) assert _elem == 'Tb' assert _edge == 'M3'
def pre_edge(energy, mu=None, group=None, e0=None, step=None, nnorm=None, nvict=0, pre1=None, pre2=None, norm1=None, norm2=None, make_flat=True, _larch=None): """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. extrapolate the two curves to E0 and take their difference to determine the edge jump Arguments ---------- energy: array of x-ray energies, in eV, or group (see note 1) 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 Notes. norm1: low E range (relative to E0) for post-edge fit norm2: high E range (relative to E0) for post-edge fit nnorm: degree of polynomial (ie, nnorm+1 coefficients will be found) for post-edge normalization curve. See Notes. make_flat: boolean (Default True) to calculate flattened output. Returns ------- None: The following attributes will be written to the output group: e0 energy origin edge_step edge step norm normalized mu(E), using polynomial norm_area normalized mu(E), using integrated area flat flattened, normalized mu(E) pre_edge determined pre-edge curve post_edge determined post-edge, normalization curve dmude derivative of mu(E) (if the output group is None, _sys.xafsGroup will be written to) Notes ----- 1. Supports `First Argument Group` convention, requiring group members `energy` and `mu`. 2. Support `Set XAFS Group` convention within Larch or if `_larch` is set. 3. pre_edge: a line is fit to mu(energy)*energy**nvict over the region, energy=[e0+pre1, e0+pre2]. pre1 and pre2 default to None, which will set pre1 = e0 - 2nd energy point, rounded to 5 eV pre2 = roughly pre1/3.0, rounded to 5 eV 4. post-edge: a polynomial of order nnorm is fit to mu(energy)*energy**nvict between energy=[e0+norm1, e0+norm2]. nnorm, norm1, norm2 default to None, which will set: norm2 = max energy - e0, rounded to 5 eV norm1 = roughly min(150, norm2/3.0), rounded to 5 eV nnorm = 2 in norm2-norm1>350, 1 if norm2-norm1>50, or 0 if less. 5. flattening fits a quadratic curve (no matter nnorm) to the post-edge normalized mu(E) and subtracts that curve from it. """ energy, mu, group = parse_group_args(energy, members=('energy', 'mu'), defaults=(mu, ), group=group, fcn_name='pre_edge') if len(energy.shape) > 1: energy = energy.squeeze() if len(mu.shape) > 1: mu = mu.squeeze() pre_dat = preedge(energy, mu, e0=e0, step=step, nnorm=nnorm, nvict=nvict, pre1=pre1, pre2=pre2, norm1=norm1, norm2=norm2) group = set_xafsGroup(group, _larch=_larch) e0 = pre_dat['e0'] norm = pre_dat['norm'] norm1 = pre_dat['norm1'] norm2 = pre_dat['norm2'] # generate flattened spectra, by fitting a quadratic to .norm # and removing that. flat = norm ie0 = index_nearest(energy, e0) p1 = index_of(energy, norm1 + e0) p2 = index_nearest(energy, norm2 + e0) if p2 - p1 < 2: p2 = min(len(energy), p1 + 2) if make_flat and p2 - p1 > 4: enx, mux = remove_nans2(energy[p1:p2], norm[p1:p2]) # enx, mux = (energy[p1:p2], norm[p1:p2]) fpars = Parameters() ncoefs = len(pre_dat['norm_coefs']) fpars.add('c0', value=0, vary=True) fpars.add('c1', value=0, vary=(ncoefs > 1)) fpars.add('c2', value=0, vary=(ncoefs > 2)) fit = Minimizer(flat_resid, fpars, fcn_args=(enx, mux)) result = fit.leastsq(xtol=1.e-6, ftol=1.e-6) fc0 = result.params['c0'].value fc1 = result.params['c1'].value fc2 = result.params['c2'].value flat_diff = fc0 + energy * (fc1 + energy * fc2) flat = norm - (flat_diff - flat_diff[ie0]) flat[:ie0] = norm[:ie0] group.e0 = e0 group.norm = norm group.norm_poly = 1.0 * norm group.flat = flat group.dmude = np.gradient(mu) / np.gradient(energy) group.edge_step = pre_dat['edge_step'] group.edge_step_poly = pre_dat['edge_step'] group.pre_edge = pre_dat['pre_edge'] group.post_edge = pre_dat['post_edge'] group.pre_edge_details = Group() for attr in ('pre1', 'pre2', 'norm1', 'norm2', 'nnorm', 'nvict'): setattr(group.pre_edge_details, attr, pre_dat.get(attr, None)) group.pre_edge_details.pre_slope = pre_dat['precoefs'][0] group.pre_edge_details.pre_offset = pre_dat['precoefs'][1] for i in range(MAX_NNORM): if hasattr(group, 'norm_c%i' % i): delattr(group, 'norm_c%i' % i) for i, c in enumerate(pre_dat['norm_coefs']): setattr(group.pre_edge_details, 'norm_c%i' % i, c) # guess element and edge group.atsym = getattr(group, 'atsym', None) group.edge = getattr(group, 'edge', None) if group.atsym is None or group.edge is None: _atsym, _edge = guess_edge(group.e0) if group.atsym is None: group.atsym = _atsym if group.edge is None: group.edge = _edge return
def mback_norm(energy, mu=None, group=None, z=None, edge='K', e0=None, pre1=None, pre2=None, norm1=None, norm2=None, nnorm=None, nvict=1, _larch=None): """ simplified version of MBACK to Match mu(E) data for tabulated f''(E) for normalization Arguments: energy, mu: arrays of energy and mu(E) group: output group (and input group for e0) z: Z number of absorber e0: edge energy pre1: low E range (relative to E0) for pre-edge fit pre2: high E range (relative to E0) for pre-edge fit norm1: low E range (relative to E0) for post-edge fit norm2: high E range (relative to E0) for post-edge fit nnorm: degree of polynomial (ie, nnorm+1 coefficients will be found) for post-edge normalization curve fit to the scaled f2. Default=1 (linear) Returns: group.norm_poly: normalized mu(E) from pre_edge() group.norm: normalized mu(E) from this method group.mback_mu: tabulated f2 scaled and pre_edge added to match mu(E) group.mback_params: Group of parameters for the minimization References: * MBACK (Weng, Waldo, Penner-Hahn): http://dx.doi.org/10.1086/303711 * Chantler: http://dx.doi.org/10.1063/1.555974 """ ### implement the First Argument Group convention energy, mu, group = parse_group_args(energy, members=('energy', 'mu'), defaults=(mu,), group=group, fcn_name='mback') if len(energy.shape) > 1: energy = energy.squeeze() if len(mu.shape) > 1: mu = mu.squeeze() if _larch is not None: group = set_xafsGroup(group, _larch=_larch) group.norm_poly = group.norm*1.0 if z is not None: # need to run find_e0: e0_nominal = xray_edge(z, edge).energy if e0 is None: e0 = getattr(group, 'e0', None) if e0 is None: find_e0(energy, mu, group=group) e0 = group.e0 atsym = None if z is None or z < 2: atsym, edge = guess_edge(group.e0) z = atomic_number(atsym) if atsym is None and z is not None: atsym = atomic_symbol(z) if getattr(group, 'pre_edge_details', None) is None: # pre_edge never run preedge(energy, mu, pre1=pre1, pre2=pre2, nvict=nvict, norm1=norm1, norm2=norm2, e0=e0, nnorm=nnorm) mu_pre = mu - group.pre_edge f2 = f2_chantler(z, energy) weights = np.ones(len(energy))*1.0 if norm2 is None: norm2 = max(energy) - e0 if norm2 < 0: norm2 = max(energy) - e0 - norm2 # avoid l2 and higher edges if edge.lower().startswith('l'): if edge.lower() == 'l3': e_l2 = xray_edge(z, 'L2').energy norm2 = min(norm2, e_l2-e0) elif edge.lower() == 'l2': e_l2 = xray_edge(z, 'L1').energy norm2 = min(norm2, e_l1-e0) ipre2 = index_of(energy, e0+pre2) inor1 = index_of(energy, e0+norm1) inor2 = index_of(energy, e0+norm2) + 1 weights[ipre2:] = 0.0 weights[inor1:inor2] = np.linspace(0.1, 1.0, inor2-inor1) params = Parameters() params.add(name='slope', value=0.0, vary=True) params.add(name='offset', value=-f2[0], vary=True) params.add(name='scale', value=f2[-1], vary=True) out = minimize(f2norm, params, method='leastsq', gtol=1.e-5, ftol=1.e-5, xtol=1.e-5, epsfcn=1.e-5, kws = dict(en=energy, mu=mu_pre, f2=f2, weights=weights)) p = out.params.valuesdict() model = (p['offset'] + p['slope']*energy + f2) * p['scale'] group.mback_mu = model + group.pre_edge pre_f2 = preedge(energy, model, nnorm=nnorm, nvict=nvict, e0=e0, pre1=pre1, pre2=pre2, norm1=norm1, norm2=norm2) step_new = pre_f2['edge_step'] group.edge_step_poly = group.edge_step group.edge_step_mback = step_new group.norm_mback = mu_pre / step_new group.mback_params = Group(e0=e0, pre1=pre1, pre2=pre2, norm1=norm1, norm2=norm2, nnorm=nnorm, fit_params=p, fit_weights=weights, model=model, f2=f2, pre_f2=pre_f2, atsym=atsym, edge=edge) if (abs(step_new - group.edge_step)/(1.e-13+group.edge_step)) > 0.75: print("Warning: mback edge step failed....") else: group.edge_step = step_new group.norm = group.norm_mback