Beispiel #1
0
    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
Beispiel #2
0
    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)
Beispiel #3
0
 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)
Beispiel #4
0
    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
Beispiel #6
0
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'
Beispiel #7
0
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
Beispiel #8
0
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