def _xrf_plot(x=None, y=None, mca=None, win=1, new=True, as_mca2=False, _larch=None, wxparent=None, size=None, side='left', force_draw=True, wintitle=None, **kws): """xrf_plot(energy, data[, win=1], options]) Show XRF trace of energy, data Parameters: -------------- energy : array of energies counts : array of counts mca: Group counting MCA data (rois, etc) as_mca2: use mca as background MCA win: index of Plot Frame (0, 1, etc). May create a new Plot Frame. new: flag (True/False, default False) for whether to start a new plot. color: color for trace (name such as 'red', or '#RRGGBB' hex string) style: trace linestyle (one of 'solid', 'dashed', 'dotted', 'dot-dash') linewidth: integer width of line marker: symbol to draw at each point ('+', 'o', 'x', 'square', etc) markersize: integer size of marker See Also: xrf_oplot, plot """ plotter = _getDisplay(wxparent=wxparent, win=win, size=size, _larch=_larch, wintitle=wintitle, xrf=True) if plotter is None: return plotter.Raise() if x is None: return if isLarchMCAGroup(x): mca = x y = x.counts x = x.energy if as_mca2: new = False if isLarchMCAGroup(mca): plotter.plotmca(mca, as_mca2=True, new=False, **kws) elif y is not None: plotter.oplot(x, y, mca=mca, as_mca2=True, **kws) elif new: if isLarchMCAGroup(mca): plotter.plotmca(mca, **kws) elif y is not None: plotter.plot(x, y, mca=mca, **kws) else: plotter.oplot(x, y, mca=mca, **kws)
def _xrf_plot(x=None, y=None, mca=None, win=1, new=True, as_mca2=False, _larch=None, wxparent=None, size=None, side='left', force_draw=True, wintitle=None, **kws): """xrf_plot(energy, data[, win=1], options]) Show XRF trace of energy, data Parameters: -------------- energy : array of energies counts : array of counts mca: Group counting MCA data (rois, etc) as_mca2: use mca as background MCA win: index of Plot Frame (0, 1, etc). May create a new Plot Frame. new: flag (True/False, default False) for whether to start a new plot. color: color for trace (name such as 'red', or '#RRGGBB' hex string) style: trace linestyle (one of 'solid', 'dashed', 'dotted', 'dot-dash') linewidth: integer width of line marker: symbol to draw at each point ('+', 'o', 'x', 'square', etc) markersize: integer size of marker See Also: xrf_oplot, plot """ plotter = _getDisplay(wxparent=wxparent, win=win, size=size, _larch=_larch, wintitle=wintitle, xrf=True) if plotter is None: _larch.raise_exception(None, msg='No Plotter defined') plotter.Raise() if x is None: return if isLarchMCAGroup(x): mca = x y = x.counts x = x.energy if as_mca2: new = False if isLarchMCAGroup(mca): plotter.plotmca(mca, as_mca2=True, new=False, **kws) elif y is not None: plotter.oplot(x, y, mca=mca, as_mca2=True, **kws) elif new: if isLarchMCAGroup(mca): plotter.plotmca(mca, **kws) elif y is not None: plotter.plot(x, y, mca=mca, **kws) else: plotter.oplot(x, y, mca=mca, **kws)
def xrf_calib_compute(mca, apply=False, _larch=None): """compute linear energy calibration from init_calib dictionary found from xrf_calib_fitrois() To exclude lines from the calibration, first run >>> xrf_calib_fitrois(mca) then remove items (by ROI name) from the mca.init_calib dictionay """ if not isLarchMCAGroup(mca): print('Not a valid MCA') return if not hasattr(mca, 'init_calib'): xrf_calib_fitrois(mca, _larch=_larch) # current calib offset, slope = mca.offset, mca.slope x = np.array([c[3] for c in mca.init_calib.values()]) y = np.array([c[0] for c in mca.init_calib.values()]) _s, _o, r, p, std = linregress(x, y) mca.new_calib = (_o, _s) mca.cal_x = x mca.cal_y = y if apply: xrf_calib_apply(mca, offset=_o, slope=_s)
def xrf_calib_compute(mca, apply=False, _larch=None): """compute linear energy calibration from init_calib dictionary found from xrf_calib_fitrois() To exclude lines from the calibration, first run >>> xrf_calib_fitrois(mca) then remove items (by ROI name) from the mca.init_calib dictionay """ if not isLarchMCAGroup(mca): print( 'Not a valid MCA') return if not hasattr(mca, 'init_calib'): xrf_calib_fitrois(mca, _larch=_larch) # current calib offset, slope = mca.offset, mca.slope x = np.array([c[3] for c in mca.init_calib.values()]) y = np.array([c[0] for c in mca.init_calib.values()]) _s, _o, r, p, std = linregress(x, y) mca.new_calib = (_o, _s) mca.cal_x = x mca.cal_y = y if apply: xrf_calib_apply(mca, offset=_o, slope=_s)
def xrf_calib_init_roi(mca, roiname): """initial calibration step for MCA: find energy locations for one ROI """ if not isLarchMCAGroup(mca): print( 'Not a valid MCA') return energy = 1.0*mca.energy chans = 1.0*np.arange(len(energy)) counts = mca.counts bgr = getattr(mca, 'bgr', None) if bgr is not None: counts = counts - bgr if not hasattr(mca, 'init_calib'): mca.init_calib = OrderedDict() roi = None for xroi in mca.rois: if xroi.name == roiname: roi = xroi break if roi is None: return words = roiname.split() elem = words[0].title() family = 'Ka' if len(words) > 1: family = words[1].title() if family == 'Lb': family = 'Lb1' eknown = xray_line(elem, family)[0]/1000.0 llim = max(0, roi.left - roi.bgr_width) hlim = min(len(chans)-1, roi.right + roi.bgr_width) segcounts = counts[llim:hlim] maxcounts = max(segcounts) ccen = llim + np.where(segcounts==maxcounts)[0] ecen = ccen * mca.slope + mca.offset bkgcounts = counts[llim] + counts[hlim] if maxcounts < 2*bkgcounts: mca.init_calib[roiname] = (eknown, ecen, 0.0, ccen, None) else: model = GaussianModel() + ConstantModel() params = model.make_params(amplitude=maxcounts, sigma=(chans[hlim]-chans[llim])/2.0, center=ccen-llim, c=0.00) params['center'].min = -10 params['center'].max = hlim - llim + 10 params['c'].min = -10 out = model.fit(counts[llim:hlim], params, x=chans[llim:hlim]) ccen = llim + out.params['center'].value ecen = ccen * mca.slope + mca.offset fwhm = out.params['fwhm'].value * mca.slope mca.init_calib[roiname] = (eknown, ecen, fwhm, ccen, out)
def xrf_background(energy, counts=None, group=None, width=4, compress=2, exponent=2, slope=None, _larch=None): """fit background for XRF spectra. Arguments: xrf_background(energy, counts=None, group=None, width=4, compress=2, exponent=2, slope=None) Arguments --------- energy array of energies OR an MCA group. If an MCA group, it will be used to give ``counts`` and ``mca`` arguments counts array of XRF counts (or MCA.counts) group group for outputs width full width (in keV) of the concave down polynomials for when its full width is 100 counts. default = 4 compress compression factor to apply to spectra. Default is 2. exponent power of polynomial used. Default is 2, should be even. slope channel to energy conversion, from energy calibration (default == None --> found from input energy array) outputs (written to group) ------- bgr background array bgr_info dictionary of parameters used to calculate background """ if isLarchMCAGroup(energy): group = energy counts = group.counts energy = group.energy if slope is None: slope = (energy[-1] - energy[0]) / len(energy) xbgr = XRFBackground(counts, width=width, compress=compress, exponent=exponent, slope=slope) if group is not None: group.bgr = xbgr.bgr group.bgr_info = xbgr.parinfo
def xrf_calib_apply(mca, offset=None, slope=None, _larch=None): """apply calibration to MCA either supply offset and slope arguments (in keV and keV/chan) or run xrf_calib_compute(mca) to estimate these from ROI peaks """ if not isLarchMCAGroup(mca): print('Not a valid MCA') return if (offset is None or slope is None) and not hasattr(mca, 'new_calib'): print('must supply offset and slope or run xrf_calib_compute()!') return if (offset is None or slope is None): offset, slope = mca.new_calib mca.offset = offset mca.slope = slope npts = len(mca.energy) mca.energy = offset + slope * np.arange(npts)
def xrf_calib_apply(mca, offset=None, slope=None, _larch=None): """apply calibration to MCA either supply offset and slope arguments (in keV and keV/chan) or run xrf_calib_compute(mca) to estimate these from ROI peaks """ if not isLarchMCAGroup(mca): print( 'Not a valid MCA') return if (offset is None or slope is None) and not hasattr(mca, 'new_calib'): print( 'must supply offset and slope or run xrf_calib_compute()!') return if (offset is None or slope is None): offset, slope = mca.new_calib mca.offset = offset mca.slope = slope npts = len(mca.energy) mca.energy = offset + slope*np.arange(npts)
def xrf_background(energy, counts=None, group=None, width=4, compress=2, exponent=2, slope=None, _larch=None): """fit background for XRF spectra. Arguments: xrf_background(energy, counts=None, group=None, width=4, compress=2, exponent=2, slope=None) Arguments --------- energy array of energies OR an MCA group. If an MCA group, it will be used to give ``counts`` and ``mca`` arguments counts array of XRF counts (or MCA.counts) group group for outputs width full width (in keV) of the concave down polynomials for when its full width is 100 counts. default = 4 compress compression factor to apply to spectra. Default is 2. exponent power of polynomial used. Default is 2, should be even. slope channel to energy conversion, from energy calibration (default == None --> found from input energy array) outputs (written to group) ------- bgr background array bgr_info dictionary of parameters used to calculate background """ if isLarchMCAGroup(energy): group = energy counts = group.counts energy = group.energy if slope is None: slope = (energy[-1] - energy[0])/len(energy) xbgr = XRFBackground(counts, width=width, compress=compress, exponent=exponent, slope=slope) if group is not None: group.bgr = xbgr.bgr group.bgr_info = xbgr.parinfo
def xrf_calib_fitrois(mca, _larch=None): """initial calibration step for MCA: find energy locations for all ROIs """ if not isLarchMCAGroup(mca): print('Not a valid MCA') return energy = 1.0 * mca.energy chans = 1.0 * np.arange(len(energy)) counts = mca.counts bgr = getattr(mca, 'bgr', None) if bgr is not None: counts = counts - bgr calib = OrderedDict() for roi in mca.rois: words = roi.name.split() elem = words[0].title() family = 'ka' if len(words) > 1: family = words[1] try: eknown = xray_line(elem, family, _larch=_larch)[0] / 1000.0 except: continue llim = max(0, roi.left - roi.bgr_width) hlim = min(len(chans) - 1, roi.right + roi.bgr_width) fit = fit_peak(chans[llim:hlim], counts[llim:hlim], 'Gaussian', background='constant', _larch=_larch) ccen = fit.params.center.value ecen = ccen * mca.slope + mca.offset fwhm = 2.354820 * fit.params.sigma.value * mca.slope calib[roi.name] = (eknown, ecen, fwhm, ccen, fit) mca.init_calib = calib
def xrf_calib_fitrois(mca, _larch=None): """initial calibration step for MCA: find energy locations for all ROIs """ if not isLarchMCAGroup(mca): print( 'Not a valid MCA') return energy = 1.0*mca.energy chans = 1.0*np.arange(len(energy)) counts = mca.counts bgr = getattr(mca, 'bgr', None) if bgr is not None: counts = counts - bgr calib = OrderedDict() for roi in mca.rois: words = roi.name.split() elem = words[0].title() family = 'ka' if len(words) > 1: family = words[1] try: eknown = xray_line(elem, family, _larch=_larch)[0]/1000.0 except: continue llim = max(0, roi.left - roi.bgr_width) hlim = min(len(chans)-1, roi.right + roi.bgr_width) fit = fit_peak(chans[llim:hlim], counts[llim:hlim], 'Gaussian', background='constant', _larch=_larch) ccen = fit.params['center'].value ecen = ccen * mca.slope + mca.offset fwhm = 2.354820 * fit.params['sigma'].value * mca.slope calib[roi.name] = (eknown, ecen, fwhm, ccen, fit) mca.init_calib = calib
def xrf_background(energy, counts=None, group=None, width=None, exponent=2, **kws): """fit background for XRF spectra. xrf_background(energy, counts=None, group=None, exponent=2) Arguments --------- energy array of energies OR an MCA group. If an MCA group, it will be used to give ``counts`` and ``mca`` arguments counts array of XRF counts (or MCA.counts) group group for outputs width full width (in keV) of the concave down polynomials when its value is ~1% of max counts. Default width is (energy range)/4.0 exponent power of polynomial used. Default is 2, should be even. Outputs (written to group) ------- bgr background array bgr_info dictionary of parameters used to calculate background """ if isLarchMCAGroup(energy): group = energy energy = group.energy if counts is None: counts = group.counts nchans = len(counts) slope = energy[1] - energy[0] if width is None: width = max(energy)/4.0 tcounts = 1.0 * counts tcounts[np.where(tcounts<0.01)] = 0.01 bgr = -1.0*np.ones(nchans) # use 1% of 99% percentile of counts as height at which # the polynomial should have full width = width max_count = np.percentile(tcounts, [99])[0] indices = np.linspace(-nchans, nchans, 2*nchans+1) * (2.0 * slope / width) polynom = 0.01 * max_count * indices**exponent polynom = np.compress((polynom <= max_count), polynom) max_index = int(len(polynom)/2 - 1) for chan in range(nchans-1): chan0 = max((chan - max_index), 0) chan1 = min((chan + max_index), (nchans-1)) chan1 = max(chan1, chan0) + 1 idx0 = chan0 - chan + max_index idx1 = chan1 - chan + max_index offset = tcounts[chan] - polynom[idx0:idx1] test = tcounts[chan0:chan1] - offset bgr[chan0:chan1] = np.maximum(bgr[chan0:chan1], min(test)+offset) bgr[np.where(bgr <= 0)] = 0.0 if group is not None: group.bgr = bgr group.bgr_info = dict(width=width, exponent=exponent)