def test_xray_line(): cuk = xray_line('Cu', 'K') assert_allclose(cuk.energy, 8039.626, rtol=0.001) assert_allclose(cuk.intensity, 0.8716833, rtol=0.001) assert cuk.final_level == 'L' cuka = xray_line('Cu', 'Ka1') assert_allclose(cuka.energy, 8046.3, rtol=0.001) assert_allclose(cuka.intensity, 0.5771, rtol=0.001) assert cuka.final_level == 'L3' ti_klines = xray_lines('Ti', 'K') for line in ('Ka1', 'Ka2', 'Ka3', 'Kb1', 'Kb3', 'Kb5'): assert ti_klines[line].initial_level == 'K' assert_allclose(ti_klines['Ka1'].energy, 4512.2, rtol=0.001) assert_allclose(ti_klines['Ka2'].energy, 4505.8, rtol=0.001) assert_allclose(ti_klines['Ka3'].energy, 4405.1, rtol=0.001) assert_allclose(ti_klines['Kb1'].energy, 4933.4, rtol=0.001) assert_allclose(ti_klines['Kb3'].energy, 4933.4, rtol=0.001) assert_allclose(ti_klines['Kb5'].energy, 4964.0, rtol=0.001) assert_allclose(ti_klines['Ka1'].intensity, 0.58455, rtol=0.001) assert_allclose(ti_klines['Ka2'].intensity, 0.29369, rtol=0.001) assert_allclose(ti_klines['Ka3'].intensity, 0.000235, rtol=0.01) assert_allclose(ti_klines['Kb1'].intensity, 0.07965, rtol=0.001) assert_allclose(ti_klines['Kb3'].intensity, 0.04126, rtol=0.001) assert_allclose(ti_klines['Kb5'].intensity, 0.000607, rtol=0.01) assert_allclose(ti_klines['Ka1'].intensity, 0.58455, rtol=0.001) assert ti_klines['Ka1'].final_level == 'L3' assert ti_klines['Ka2'].final_level == 'L2' assert ti_klines['Ka3'].final_level == 'L1' assert ti_klines['Kb1'].final_level == 'M3' assert ti_klines['Kb3'].final_level == 'M2' assert ti_klines['Kb5'].final_level == 'M4,5'
def calc_escape_scale(self, energy, thickness=None): """ calculate energy dependence of escape effect X-rays penetrate a depth 1/mu(material, energy) and the detector fluorescence escapes from that depth as exp(-mu(material, KaEnergy)*thickness) with a fluorecence yield of the material """ det = self.detector # note material_mu, xray_edge, xray_line work in eV! escape_energy_ev = xray_line(det.material, 'Ka').energy mu_emit = material_mu(det.material, escape_energy_ev) self.escape_energy = 0.001 * escape_energy_ev mu_input = material_mu(det.material, 1000 * energy) edge = xray_edge(det.material, 'K') self.escape_scale = edge.fyield * np.exp(-mu_emit / (2 * mu_input)) self.escape_scale[np.where(energy < 0.001 * edge.energy)] = 0.0
def xrf_calib_fitrois(mca): """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).energy / 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') 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 predict_escape(self, det='Si', scale=1.0): """ predict detector escape for a spectrum, save to 'escape' attribute X-rays penetrate a depth 1/mu(material, energy) and the detector fluorescence escapes from that depth as exp(-mu(material, KaEnergy)*thickness) with a fluorecence yield of the material """ fluor_en = xray_line(det, 'Ka').energy/1000.0 edge = xray_edge(det, 'K') # here we use the 1/e depth for the emission # and the 1/e depth of the incident beam: # the detector fluorescence can escape on either side mu_emit = material_mu(det, fluor_en*1000) mu_input = material_mu(det, self.energy*1000) escape = 0.5*scale*edge.fyield * np.exp(-mu_emit / (2*mu_input)) escape[np.where(self.energy*1000 < edge.energy)] = 0.0 escape *= interp(self.energy - fluor_en, self.counts*1.0, self.energy, kind='cubic') self.escape = escape
def fluo_corr(energy, mu, formula, elem, group=None, edge='K', anginp=45, angout=45, _larch=None, **pre_kws): """correct over-absorption (self-absorption) for fluorescene XAFS using the FLUO alogrithm of D. Haskel. Arguments --------- energy array of energies mu uncorrected fluorescence mu formula string for sample stoichiometry elem atomic symbol or Z of absorbing element group output group [default None] edge name of edge ('K', 'L3', ...) [default 'K'] anginp input angle in degrees [default 45] angout output angle in degrees [default 45] Additional keywords will be passed to pre_edge(), which will be used to ensure consistent normalization. Returns -------- None, writes `mu_corr` and `norm_corr` (normalized `mu_corr`) to output group. Notes ----- Support First Argument Group convention, requiring group members 'energy' and 'mu' """ energy, mu, group = parse_group_args(energy, members=('energy', 'mu'), defaults=(mu, ), group=group, fcn_name='fluo_corr') # gather pre-edge options pre_opts = { 'e0': None, 'nnorm': 1, 'nvict': 0, 'pre1': None, 'pre2': -30, 'norm1': 100, 'norm2': None } if hasattr(group, 'pre_edge_details'): uopts = getattr(group.pre_edge_details, 'call_args', {}) for attr in pre_opts: if attr in uopts: pre_opts[attr] = uopts[attr] pre_opts.update(pre_kws) pre_opts['step'] = None pre_opts['nvict'] = 0 # generate normalized mu for correction preinp = preedge(energy, mu, **pre_opts) ang_corr = (np.sin(max(1.e-7, np.deg2rad(anginp))) / np.sin(max(1.e-7, np.deg2rad(angout)))) # find edge energies and fluorescence line energy e_edge = xray_edge(elem, edge).energy e_fluor = xray_line(elem, edge).energy # calculate mu(E) for fluorescence energy, above, below edge muvals = material_mu(formula, np.array([e_fluor, e_edge - 10.0, e_edge + 10.0]), density=1) alpha = (muvals[0] * ang_corr + muvals[1]) / (muvals[2] - muvals[1]) mu_corr = mu * alpha / (alpha + 1 - preinp['norm']) preout = preedge(energy, mu_corr, **pre_opts) if group is not None: if _larch is not None: group = set_xafsGroup(group, _larch=_larch) group.mu_corr = mu_corr group.norm_corr = preout['norm']
def __init__(self, parent, mca, size=(-1, -1), callback=None): self.mca = mca self.callback = callback wx.Frame.__init__(self, parent, -1, 'Calibrate MCA', size=size, style=wx.DEFAULT_FRAME_STYLE) opanel = scrolled.ScrolledPanel(self) osizer = wx.BoxSizer(wx.VERTICAL) panel = GridPanel(opanel) self.calib_updated = False panel.AddText("Calibrate MCA Energy (Energies in eV)", colour='#880000', dcol=7) panel.AddText("ROI", newrow=True, style=CEN) panel.AddText("Predicted", style=CEN) panel.AddText("Peaks with Current Calibration", dcol=3, style=CEN) panel.AddText("Peaks with Refined Calibration", dcol=3, style=CEN) panel.AddText("Name", newrow=True, style=CEN) panel.AddText("Energy", style=CEN) panel.AddText("Center", style=CEN) panel.AddText("Difference", style=CEN) panel.AddText("FWHM", style=CEN) panel.AddText("Center", style=CEN) panel.AddText("Difference", style=CEN) panel.AddText("FWHM", style=CEN) panel.AddText("Use? ", style=CEN) panel.Add(HLine(panel, size=(700, 3)), dcol=9, newrow=True) self.wids = [] # find ROI peak positions self.init_wids = {} for roi in self.mca.rois: eknown, ecen, fwhm = 1, 1, 1 words = roi.name.split() elem = words[0].title() family = 'ka' if len(words) > 1: family = words[1] try: eknown = xray_line(elem, family).energy / 1000.0 except (AttributeError, ValueError): eknown = 0.0001 mid = (roi.right + roi.left) / 2 wid = (roi.right - roi.left) / 2 ecen = mid * mca.slope + mca.offset fwhm = 2.354820 * wid * mca.slope diff = ecen - eknown name = (' ' + roi.name + ' ' * 10)[:10] opts = {'style': CEN, 'size': (75, -1)} w_name = SimpleText(panel, name, **opts) w_pred = SimpleText(panel, "% .1f" % (1000 * eknown), **opts) w_ccen = SimpleText(panel, "% .1f" % (1000 * ecen), **opts) w_cdif = SimpleText(panel, "% .1f" % (1000 * diff), **opts) w_cwid = SimpleText(panel, "% .1f" % (1000 * fwhm), **opts) w_ncen = SimpleText(panel, "-----", **opts) w_ndif = SimpleText(panel, "-----", **opts) w_nwid = SimpleText(panel, "-----", **opts) w_use = Check(panel, label='', size=(40, -1), default=0) panel.Add(w_name, style=LEFT, newrow=True) panel.AddMany((w_pred, w_ccen, w_cdif, w_cwid, w_ncen, w_ndif, w_nwid, w_use)) self.init_wids[roi.name] = [ False, w_pred, w_ccen, w_cdif, w_cwid, w_use ] self.wids.append( (roi.name, eknown, ecen, w_ncen, w_ndif, w_nwid, w_use)) panel.Add(HLine(panel, size=(700, 3)), dcol=9, newrow=True) offset = 1000.0 * self.mca.offset slope = 1000.0 * self.mca.slope panel.AddText("Current Calibration:", dcol=2, newrow=True) panel.AddText("offset(eV):") panel.AddText("%.3f" % (offset), dcol=1, style=RIGHT) panel.AddText("slope(eV/chan):") panel.AddText("%.3f" % (slope), dcol=1, style=RIGHT) panel.AddText("Refined Calibration:", dcol=2, newrow=True) self.new_offset = FloatCtrl(panel, value=offset, precision=3, size=(80, -1)) self.new_slope = FloatCtrl(panel, value=slope, precision=3, size=(80, -1)) panel.AddText("offset(eV):") panel.Add(self.new_offset, dcol=1, style=RIGHT) panel.AddText("slope(eV/chan):") panel.Add(self.new_slope, dcol=1, style=RIGHT) self.calib_btn = Button(panel, 'Compute Calibration', size=(175, -1), action=self.onCalibrate) self.calib_btn.Disable() panel.Add(self.calib_btn, dcol=3, newrow=True) panel.Add(Button(panel, 'Use New Calibration', size=(175, -1), action=self.onUseCalib), dcol=3, style=RIGHT) panel.Add(Button(panel, 'Done', size=(125, -1), action=self.onClose), dcol=2, style=RIGHT) panel.pack() a = panel.GetBestSize() self.SetSize((a[0] + 25, a[1] + 50)) osizer.Add(panel) pack(opanel, osizer) opanel.SetupScrolling() self.Show() self.Raise() self.init_proc = False self.init_t0 = time.time() self.init_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onInitTimer, self.init_timer) self.init_timer.Start(2)