def cal_NetYield2(eV0, Atoms, xHe=0, xAl=0, xKapton=0, WD=6.0, xsw=0, WriteFile='Y', xsect_resonant=0.0, sample=''): # incident energy, list of elements, experimental conditions # this one tries Ka, Kb, Lg, Lb, La, Lb angle0 = 45. textOut = '' if xsw != 0 and WriteFile == 'Y': print('XSW measurements') if sample == '': Include_SelfAbsorption = 'No' else: Include_SelfAbsorption = 'Yes' angle0 = sample.angle / math.pi * 180. textOut = sample.txt # substrate concentration NetYield = [] NetTrans = [] Net = [] out2 = '' net = 0.0 text0 = '' edges = ['K', 'K', 'L1', 'L1', 'L2', 'L2', 'L2', 'L3', 'L3', 'L3'] Fluo_lines = ['Ka', 'Kb', 'Lb', 'Lg', 'Ln', 'Lb', 'Lg', 'Ll', 'La', 'Lb'] outputfile = 'Elemental_Sensitivity.txt' if WriteFile == 'Y': fo = open(outputfile, 'w') desc = ' ' if xHe == 0: desc = ' not ' if xsw == -1: out1 = '# 13IDC XRM/XSW using QuadVortex, incident x-ray energy at ' + str( eV0) + ' eV at ' + str(angle0) + ' degrees \n' out1+='# Helium path'+desc+'used, '+str(xAl)+' Al attenuators, '+str(xKapton+1)+' Kapton attenuators, '\ +str(WD)+' cm working distance. \n' else: out1 = '# 13IDC XRM/XSW using QuadVortex + collimator, incident x-ray energy at ' + str( eV0) + ' eV at ' + str(angle0) + ' degrees \n' out1+='# Helium path'+desc+'used, '+str(xAl)+' Al attenuators, '+str(xKapton)+' Kapton attenuators, '\ +str(WD)+' cm working distance. \n' print(out1) fo.write(out1) if sample != '': for stuff in Atoms: text1 = '%6.3e %s/cm^3' % (stuff.Conc * sample.Nat1, stuff.AtSym) text0 = text0 + ' ' + text1 textOut = textOut + ' ' + text0 # substrate concentration + other concentrations out1 = '# ' + text0 + '\n' if print2screen: print(out1) fo.write(out1) out1 = '%s\t%s\t%s \t%s \t%s \t%s \t%s\n' % ( 'atom', 'emit', 'emit_energy', 'yield', 'transmission', 'net_sensitivity', 'sensitivity*concentration') if print2screen: print(out1) fo.write(out1) for (ii, atom) in enumerate(Atoms): atnum = f1f2.AtSym2AtNum(atom.AtSym) temp = f1f2.AtNum2f1f2Xsect(atnum, eV0) xsect = temp[ 2] # photo-electric cross-section for fluorescence yield, temp[4] is total for attenuation con = atom.Conc for (nn, edge) in enumerate(edges): emit = Fluo_lines[nn] #edge='K'; emit='Ka' # check K edge Ka emission temp = fluo_elem.get_avgElamFY( atom.AtSym, edge, emit, eV0, useAvg=1) # July2012: useAvg=1 causes problem print(emit) fy = temp[0] emit_eV = temp[1] emit_prob = temp[2] if fy == 0.0 or emit_prob == 0: continue # try next item if FY=0 else: if xsect_resonant != 0.0: # use input value near edge xsect = xsect_resonant # for cross-section near absoprtion edge #print(xsect,fy,emit_prob) net_yield = xsect * fy * emit_prob # net_yield --> cross-section, yield, emission_probability # net transmission --> transmission from surface through detector ## ------------------ [self-absorption] ------------------------------ trans_SelfAbsorp = 1.0 # for self-absorption. if Include_SelfAbsorption == 'Yes': sample.getPenetration(eV0) # incident x-ray attenuation if fy * emit_eV * emit_prob == 0: # any one of three is zero break # skip sample.getLa(emit_eV) # account for incident x-ray attenuation, emitted x-ray attenuation trans_SelfAbsorp = sample.scale # for elements that are not part of substrate if (atom in sample.ElemListFY): if atom.tag == 'substrate1': trans_SelfAbsorp = sample.scale1 # for elements that are part of top substrate if atom.tag == 'substrate2': trans_SelfAbsorp = sample.scale2 # for elements that are part of bottom substrate ## ---------------------------------------------------------------------------- net_trans = Assemble_Detector(emit_eV, xHe, xAl, xKapton, WD, xsw) net = net_yield * net_trans * trans_SelfAbsorp # elemental sensitivity inten = net * con # sensitivity * concentration if WriteFile == 'Y': out1 = '%s\t%s\t%6.1f \t%.3e \t%.3e \t%.3e \t%.3e\n' % ( atom.AtSym + '_' + edge, emit, emit_eV, net_yield, net_trans, net, inten) fo.write(out1) if print2screen: print('%s %s %6.1f net_yield=%.3e net_trans=%.3e net=%.3e\t' % (atom.AtSym + '_' + edge, emit, emit_eV, net_yield, net_trans, net)) #print(out1)+' %s, depth-dependent factor= %6.4f' % (atom.tag, trans_SelfAbsorp) if emit == 'Kb' and fy != 0: # if above K edge, don't bother trying L edges break return textOut
def sim_spectra(eV0, Atoms, xHe=0, xAl=0, xKapton=0, WD=6.0, xsw=0, sample=''): # sample=sample matrix with object attribues to add self-absorption effect # Atoms is a list with elements that have attributes AtSym, Conc, tag if xsw == -1: xKapton = xKapton - 1 # no collimator Include_SelfAbsorption = 'Yes' Print2Screen = 'No' if sample == '': Include_SelfAbsorption = 'No' if xsw != 0: print('XSW measurements') xx = [] yy = [] tag = [] intensity_max = -10.0 LoLimit = 1e-10 angle0 = '' text1 = '' if sample != '': # sample matrix option is used angle0 = str(sample.angle * 180. / math.pi) angle0 = angle0[:5] for (ii, item) in enumerate( sample.ElemListFY): # add matrix elements to the lists Atoms.append(item) outputfile = 'simSpectrum_table.txt' fo = open(outputfile, 'w') out1 = '#incident x-ray at ' + str(eV0) + ' eV and ' + angle0 + ' Deg.\n' if Print2Screen == 'Yes': print(out1) fo.write(out1) out1 = '#Emission\tenergy(eV)\tintensity \n' if Print2Screen == 'Yes': print(out1) fo.write(out1) out2 = '#' for (ix, atom) in enumerate(Atoms): atnum = f1f2.AtSym2AtNum(atom.AtSym) # con=Conc[ix] con = atom.Conc out2 = out2 + atom.AtSym + '[' + str(con) + '] ' for edge in ['K', 'L1', 'L2', 'L3']: temp = f1f2.AtNum2f1f2Xsect(atnum, eV0) xsect = temp[ 2] # photoelectric crosssection for each element at incident x-ray, fluorescence yield temp = elam.use_ElamFY(atom.AtSym, edge) edge_eV = float(temp[2]) # absorption edge if eV0 > edge_eV: fy = float(temp[3]) for EmitLine in temp[4]: EmitName = EmitLine[1] emit_eV = float(EmitLine[2]) emit_prob = float(EmitLine[3]) if emit_eV < fluo_emit_min: continue # ignore fluorescence below fluo_emit_min (global variable) name = atom.AtSym + '_' + EmitName # net transmission --> transmission from surface through detector ## ------------------ [self-absorption] ------------------------------ trans_SelfAbsorp = 1.0 # for self-absorption. if Include_SelfAbsorption == 'Yes': sample.getPenetration( eV0) # incident x-ray attenuation eV0str = str(eV0) text1 = ' absorption_length1(%seV)= %2.2e%s \ absorption_length2(%seV)= %2.2e%s' % ( eV0str, sample.la1 * 1.e4, 'microns', eV0str, sample.la2 * 1.e4, 'microns') # text1 is added to sample.txt later sample.getLa( emit_eV) # emitted fluorescence attenuation trans_SelfAbsorp = sample.scale # for elements that are not part of substrate if (atom in sample.ElemListFY): if atom.tag == 'substrate1': trans_SelfAbsorp = sample.scale1 # for elements that are part of top substrate if atom.tag == 'substrate2': trans_SelfAbsorp = sample.scale2 # for elements that are part of bottom substrate ## ---------------------------------------------------------------------------- trans = Assemble_Detector(emit_eV, xHe, xAl, xKapton, WD, xsw) intensity = con * fy * emit_prob * xsect * trans * trans_SelfAbsorp if intensity < LoLimit: continue # skip weak emission, arbtraray limit =1e-10. if intensity > intensity_max: intensity_max = intensity xx.append(emit_eV) yy.append(intensity) tag.append(name) for ix in range(len(yy)): yy[ix] = yy[ ix] / intensity_max * 100.00 # makes the strongest line to 100.0 out1 = '%s\t%f\t%f \n' % (tag[ix], xx[ix], yy[ix]) if Print2Screen == 'Yes': print(out1) fo.write(out1) if Print2Screen == 'Yes': print(out2) fo.write(out2) fo.close() out1 = sim_GaussPeaks( xx, yy, det_res, eV0 ) # det_res: detector resoultion for Gaussian width (global variable) if Include_SelfAbsorption == 'Yes': sample.txt = sample.txt + text1 # sample.txt is combined to output of cal_NetYield text = cal_NetYield2( eV0, Atoms, xHe, xAl, xKapton, WD, xsw, sample=sample) # calculate net yield with weight-averaged emission print(out2) return text # cal_NetYield2 output is str with number densities of elements
def __init__(self, composition1='Si', density1=2.33, thickness1=0.1, \ composition2='Si', density2=2.33, thickness2=0.1, angle0=45.0, option='surface'): self.composition1 = composition1 # ex) Fe2O3 out = f1f2.get_ChemName(composition1) # output from get_ChemName self.ElemList1 = out[ 0] # list of elments in matrix: 'Fe', 'O' for Fe2O3 self.ElemInd1 = out[1] # list of index: 2, 3 for Fe2O3 self.ElemFrt1 = out[2] # list of fraction: 0.4, 0.6 for Fe2O3 self.density1 = density1 # in g/cm^3 self.thickness1 = thickness1 # top layer thickness in cm self.composition2 = composition2 out = f1f2.get_ChemName(composition2) self.ElemList2 = out[0] # for the bottom substrate self.ElemInd2 = out[1] self.ElemFrt2 = out[2] self.density2 = density2 self.thickness2 = thickness2 # bottom layer thickness in cm self.angle = angle0 * math.pi / 180. # in radian, incident beam angle, surface normal =pi/2 self.option = option # option for fluorescing element location, surface/top/bottom self.la1 = 1.0 # absoprtion length in cm self.delta1 = 0.1 # index of refraction, real part correction self.beta1 = 0.1 # index of refraction, imaginary part self.Nat1 = 1.0 # atomic number density of the Fe2O3, 1e22 Fe2O3 atoms /cm^3 self.la2 = 1.0 self.delta2 = 0.1 self.beta2 = 0.1 self.Nat2 = 1.0 # atomic number density of the first element, 1e22 Fe2O3 atoms/cm^3 self.scale = 1.0 # weighted average over the depth range: sum of (trans*factors) self.scale1 = 1.0 # sum of (trans*1.0) this is for substrate element in top layer self.scale2 = 1.0 # sum of (trans*thickness2/thickness1) for substrate element in bottom layer self.ElemListFY = [] # fluorescing element self.ElemFrtFY = [] # fraction for fluorescing element self.Nat1 = 1 # atomic number density in atoms/cc, for example # of Fe2O3/cm^3 for Fe2O3 substrate self.Nat2 = 1 # atomic number density in atoms/cc substrate1_material = Material(composition1, density1) AtNumDen1 = substrate1_material.NumDen # substrate1 substrate2_material = Material(composition2, density2) AtNumDen2 = substrate2_material.NumDen # substrate2, fixed 8/12/10 self.Nat1 = AtNumDen1 self.Nat2 = AtNumDen2 self.txt = '' text1 = 'substrate1:%6.3e %s/cm^3' % (AtNumDen1, composition1) #print(text1) text2 = 'substrate2:%6.3e %s/cm^3' % (AtNumDen2, composition2) self.txt = text1 + ' ' + text2 print(self.txt) # atom.conc is normalized to the number density of substrate1 molecule for (ii, item) in enumerate(self.ElemList1): if f1f2.AtSym2AtNum(item) >= 12: # ignore elements below Mg #atom=ElemFY(item, self.ElemFrt1[ii], 'substrate1') atom = ElemFY(item, self.ElemInd1[ii], 'substrate1') # eg. for Fe2O3, Fe concentration is 2 (=2 x Fe2O3 number density) self.ElemListFY.append(atom) for (ii, item) in enumerate(self.ElemList2): if f1f2.AtSym2AtNum(item) >= 12: # ignore elements below Mg #atom=ElemFY(item, self.ElemFrt2[ii], 'substrate2') atom = ElemFY(item, self.ElemInd2[ii] * AtNumDen2 / AtNumDen1, 'substrate2') self.ElemListFY.append(atom) numLayer = 100 # each layer is sliced into 100 sublayers. self.depths = [] # depth values for calculation for ii in range(numLayer): step = 1.0 / numLayer self.depths.append(step * ii * self.thickness1) for ii in range(numLayer): step = 1.0 / numLayer self.depths.append(step * ii * self.thickness2 + self.thickness1) self.factors = [] # 1 or pre if element present at each depth pre = self.thickness2 / self.thickness1 # prefactor, if two layers have different thickness if self.option == 'surface': # fluorescecing atoms present in only on the surface for ii in range(len(self.depths)): if ii == 0: self.factors.append(1.0) else: self.factors.append(0.0) if self.option == 'all': # fluorescecing atoms present throughout for ii in range(len(self.depths)): if self.depths[ii] < self.thickness1: self.factors.append(1.0) else: self.factors.append(pre) if self.option == 'top': # fluorescecing atoms present only in the top layer for ii in range(len(self.depths)): if self.depths[ii] < self.thickness1: self.factors.append(1.0) else: self.factors.append(0.0) if self.option == 'bottom': # fluorescecing atoms present only in the bottom layer for ii in range(len(self.depths)): if self.depths[ii] < self.thickness1: self.factors.append(0.0) else: self.factors.append(pre) # note: fluorescing substrate atoms present throughout regardless of the option. self.trans = [] # transmission to surface for emitted fluorescence self.absrp = [] # absorption until surface for emitted fluorescence self.inten0 = [] # incident x-ray intensity at each depth for ii in range(len(self.depths)): self.trans.append(0.5) self.absrp.append(0.5) self.inten0.append(0.5)