def test_stoichiometry_found_from_theoretical_EELS(self): if True: return from atomic_eels import atomic_diff_cross_section # Make BN eels data using FEFF. atomic_numbers=[5,7] amps =[1.0,1.0] edge_label = 'K' beam_energy_keV = 200.0 convergence_angle_mrad = 1.5 collection_angle_mrad = 1.5 egrid_eV = numpy.arange(0.0, 1000.0, 0.5) # Define energy grid energyDiffSigma_total = numpy.zeros_like(egrid_eV) # Initialize total cross section. edge_onsets = [0.0, 0.0] edge_deltas = [0.0, 0.0] background_ranges = [numpy.zeros(2),numpy.zeros(2)] iEdge = 0 for atomic_number in atomic_numbers: #print(atomic_number,edge_label,beam_energy_keV,convergence_angle_mrad,collection_angle_mrad) energyDiffSigma,edge_onsets[iEdge] = atomic_diff_cross_section(atomic_number, edge_label, beam_energy_keV, convergence_angle_mrad, collection_angle_mrad, egrid_eV) energyDiffSigma_total = numpy.add(energyDiffSigma_total,energyDiffSigma*amps[iEdge]) # set background ranges and offsets, etc. background_ranges[iEdge][0] = max(edge_onsets[iEdge]-30.0,0.0) background_ranges[iEdge][1] = max(edge_onsets[iEdge]-5.0,0.0) edge_deltas[iEdge] = 30.0 iEdge += 1 #print('bgr',background_ranges,edge_onsets) bgfunc = lambda x: 1.0e-3/(x+10.0)**3 background = numpy.vectorize(bgfunc) energyDiffSigma_total = numpy.add(energyDiffSigma_total,background(egrid_eV))*10.0e12 #plt.plot(egrid_eV,energyDiffSigma_total) #plt.show() #noise = numpy.random.normal(0.0, max(energyDiffSigma_total)/100.0,energyDiffSigma_total.size) #energyDiffSigma_total = numpy.add(noise,energyDiffSigma_total) erange = numpy.zeros(2) estep = (egrid_eV[-1] - egrid_eV[0])/(egrid_eV.size - 1) print(estep) erange[0] = egrid_eV[0] erange[1] = egrid_eV[-1] + estep stoich_data = EELS_DataAnalysis.stoichiometry_from_eels(energyDiffSigma_total,erange,background_ranges,atomic_numbers,edge_onsets,edge_deltas, beam_energy_keV*1000.0, convergence_angle_mrad/1000.0, collection_angle_mrad/1000.0) stoichiometry = stoich_data[0] error_in_stoichiometry = stoich_data[1] iAtom = 0 print("Stoichiometry from theoretical EELS signal of BN:") for atomic_number in atomic_numbers: print(atomic_number, stoichiometry[iAtom][0], '+/-', error_in_stoichiometry[iAtom][0]) assert abs(stoichiometry[iAtom][0]/amps[iAtom]*amps[0]-0.5) < 0.01 # Test stoichiometry found is better than 1% iAtom += 1
def test_stoichiometry_found_from_experimental_eels(self): # Read EELS data from file (BN). This is data from Tracy, taken from a thin part of the sample, # and represents to some extent a best case scenario. test_data_dir = Path(__file__).parent / 'Test_Data' data_files = [test_data_dir / 'BN_0-0910eV_.msa', test_data_dir / 'CaCO3.msa', test_data_dir / 'CuO.msa'] labels = ['BN', 'CaCO_3','CuO'] #data_file = Path('./Test_Data/EELS_Thick.csv') #data_file = Path('./Test_Data/EELS_Thin.csv') atomic_number_arrays = [[7,5],[8,20,6],[29,8]] beam_energies = [200.0,200.0,200.0] convergence_angles = [0.0, 0.0, 0.0] collection_angles = [100.0, 100.0, 100.0] edge_onset_arrays = [[401.0, 188.0],[532.0, 346.0, 284.0], [931.0,532.0]] edge_delta_arrays = [[25.0,25.0],[40.0,25.0,25.0],[40.0,40.0]] background_arrays = [ [numpy.array([358.0,393.0]),numpy.array([167.0,183.0])], [numpy.array([474.0, 521.0]),numpy.array([308.0,339.0]),numpy.array([253.0,278.0])], [numpy.array([831.0,912.0]),numpy.array([474.0,521.0])]] DM_Stoichiometries = [[1.0, 0.83], [1.0, 0.76, 0.27],[1.0,0.09]] True_Stoichiometries = [[1.0,1.0],[1.0, 0.3333, 0.3333],[1.0,1.0]] iData = 0 for data_file in data_files: energy_grid,spectrum = numpy.loadtxt(data_file, delimiter=',',unpack=True) # Set up input to stoichiometry quantification. All settings are hard coded to # BN to match DM 2.32.888. beam_energy_keV = beam_energies[iData] atomic_numbers = atomic_number_arrays[iData] convergence_angle_mrad = convergence_angles[iData] collection_angle_mrad = collection_angles[iData] edge_onsets = edge_onset_arrays[iData] edge_deltas = edge_delta_arrays[iData] background_ranges = background_arrays[iData] erange = numpy.zeros(2) erange[0] = energy_grid[0] erange[1] = energy_grid[-1] stoich_data = EELS_DataAnalysis.stoichiometry_from_eels(spectrum,erange,background_ranges,atomic_numbers,edge_onsets,edge_deltas, beam_energy_keV*1000.0, convergence_angle_mrad/1000.0, collection_angle_mrad/1000.0) stoich = stoich_data[0] error_in_stoich = stoich_data[1] iAtom = 0 DM_Stoichometry = DM_Stoichiometries[iData] True_Stoichiometry = True_Stoichiometries[iData] print("----------------------------------------------------------------------------\n\n\n") print('Stoichiometry from experimental EELS data from the EELS Atlas:' + labels[iData]) print('atomic#, N, N from DM, True N') for atomic_number in atomic_numbers: print(atomic_number, stoich[iAtom][0],'+/-',error_in_stoich[iAtom][0],DM_Stoichometry[iAtom], True_Stoichiometry[iAtom]) iAtom += 1 print("----------------------------------------------------------------------------") iData += 1
def test_stoichiometry_from_multidimensional_EELS(self): # Make BN eels data using FEFF. atomic_numbers=[5,7] nSpectra = 100 amps =[1.0,1.0] edge_label = 'K' beam_energy_keV = 200.0 convergence_angle_mrad = 1.5 collection_angle_mrad = 1.5 egrid_eV = numpy.arange(0.0, 1000.0, 0.5) # Define energy grid energyDiffSigma_total = numpy.array([numpy.zeros_like(egrid_eV)]*nSpectra) # Initialize total cross section. energyDiffSigma = numpy.array([numpy.zeros_like(egrid_eV)]*2) # Initialize total cross section. edge_onsets = [0.0, 0.0] edge_deltas = [0.0, 0.0] background_ranges = [numpy.zeros(2),numpy.zeros(2)] iEdge = 0 for atomic_number in atomic_numbers: energyDiffSigma[iEdge],edge_onsets[iEdge] = atomic_diff_cross_section(atomic_number, edge_label, beam_energy_keV, convergence_angle_mrad, collection_angle_mrad, egrid_eV) iEdge += 1 iEdge = 0 for atomic_number in atomic_numbers: iSpectrum = 0 while iSpectrum < nSpectra: if iEdge == 0: amps[iEdge] = numpy.sin(float(iSpectrum)/float(nSpectra)*numpy.pi/2.0)**2 else: amps[iEdge] = numpy.cos(float(iSpectrum)/float(nSpectra)*numpy.pi/2.0)**2 energyDiffSigma_total[iSpectrum] = numpy.add(energyDiffSigma_total[iSpectrum],energyDiffSigma[iEdge]*amps[iEdge]) iSpectrum += 1 # set background ranges and offsets, etc. background_ranges[iEdge][0] = max(edge_onsets[iEdge]-30.0,0.0) background_ranges[iEdge][1] = max(edge_onsets[iEdge]-5.0,0.0) edge_deltas[iEdge] = 30.0 iEdge += 1 iSpectrum = 0 # Add background. bgfunc = lambda x: 1.0e-3/(x+10.0)**3 background = numpy.vectorize(bgfunc) while iSpectrum < nSpectra: energyDiffSigma_total[iSpectrum] = numpy.add(energyDiffSigma_total[iSpectrum],background(egrid_eV))*10.0e12 iSpectrum += 1 #print('bgr',background_ranges,edge_onsets) #plt.plot(egrid_eV,energyDiffSigma_total) #plt.show() #noise = numpy.random.normal(0.0, max(energyDiffSigma_total)/100.0,energyDiffSigma_total.size) #energyDiffSigma_total = numpy.add(noise,energyDiffSigma_total) erange = numpy.zeros(2) erange[0] = egrid_eV[0] erange[1] = egrid_eV[-1] stoich_data = EELS_DataAnalysis.stoichiometry_from_eels(energyDiffSigma_total,erange,background_ranges,atomic_numbers,edge_onsets,edge_deltas, beam_energy_keV*1000.0, convergence_angle_mrad/1000.0, collection_angle_mrad/1000.0) stoichiometry = stoich_data[0] error_in_stoich = stoich_data[1] iAtom = 0 print("Stoichiometry from multidimensional spectrum array.") for atomic_number in atomic_numbers: print(atomic_number) print(stoichiometry[iAtom]) iAtom += 1
def test_eels_quantification(self): if True: # For now turn this test off. I will keep the directory of all EELS Atlas data on hand for more testing. return import glob import pandas df = pandas.read_csv("EELS_Atlas_Major/files_HE.dat", delim_whitespace=True, header=None) file_names, tmp1, efin, tmp2, estart = df.to_numpy().T i_search = 0 total_extra_found = 0 total_edges_edb = 0 total_edges_matched = 0 mintol = 0.1 nfail = 0 ntot = 0 ptable = PeriodicTable.PeriodicTable() for file_name in file_names: # Load data from text file. #data_file = glob.glob('./EELS_Atlas_Major/**/' + file_name,recursive = True)[0] print('\n\n\n') data_file = glob.glob('./EELS_Atlas_Major/**/' + file_name, recursive=True)[0] #edge_file = glob.glob('./EELS_Atlas_Major/**/' + 'edges_' + os.path.splitext(file_name)[0]+'.dat', recursive=True)[0] edge_file = glob.glob('./EELS_Atlas_Major/**/' + 'edges_' + os.path.splitext(file_name)[0] + '.dat', recursive=True)[0] energies, eels_spectrum = numpy.loadtxt(data_file, delimiter=',', unpack=True) energy_step = (energies[-1] - energies[0]) / energies.size energy_range_ev = numpy.array( [energies[0], energies[-1] + energy_step]) if estart[i_search] < 0: estart[i_search] = tmp1[i_search] search_range = [estart[i_search], efin[i_search]] chem_formula = file_name.split('_')[0] elements_exp = [ elem.strip(string.digits) for elem in re.findall('[A-Z][^A-Z]*', chem_formula) if str(ptable.atomic_number(elem.strip(string.digits))) in ptable.find_elements_in_energy_interval(search_range) ] print(file_name, ':') experimental_edge_data = EELS_DataAnalysis.find_experimental_edge_energies( eels_spectrum, energy_range_ev, search_range, debug_plotting=False) df = pandas.read_csv(edge_file, delim_whitespace=True, header=None) edge_data = EELS_DataAnalysis.find_species_from_experimental_edge_data( eels_spectrum, energy_range_ev, experimental_edge_data, search_range_ev=search_range, only_major_edges=True) elements_found = [ed[0] for ed in edge_data] edge_data = EELS_DataAnalysis.find_species_from_experimental_edge_data( eels_spectrum, energy_range_ev, experimental_edge_data, search_range_ev=search_range, only_major_edges=False, element_list=elements_found) edge_energies = experimental_edge_data[0] # Print edges found, and edges found by visual inspection for comparison. # The edge finder will not find all edges necessarily, and might find extra edges. elements_found = [ptable.element_symbol(ed[0]) for ed in edge_data] ntot += 1 if (all(x in elements_found for x in elements_exp)): missing = False else: print(chem_formula, ": Failed to find all elements") i_search += 1 continue edge_data = [ ed for ed in edge_data if ptable.element_symbol(ed[0]) in elements_exp ] #print('edge_data', edge_data) i_search += 1 # We now have the elements and the edges we want to analyze, lets do the quantification # Set microscope parameters beam_energy_keV = 200.0 convergence_angle_mrad = 0.0 collection_angle_mrad = 100.0 # Set up the atomic numbers, edge onsets, and background ranges atomic_numbers = [] background_ranges = [] edge_onsets = [] edge_deltas = [] iElement = 0 for ed in edge_data: iEdge = 0 edge_onset = [] while iEdge < len(ed[1]): edge_onsets = edge_onsets + [ed[1][iEdge][2]] atomic_numbers = atomic_numbers + [ed[0]] iEdge += 1 #print('Atoms in system:', atomic_numbers) deltas = 30.0 for i, onset in enumerate(edge_onsets): if i + 1 < len(edge_onsets) and atomic_numbers[ i] == atomic_numbers[i + 1]: if edge_onsets[i + 1] - onset > deltas: edge_deltas = edge_deltas + [deltas] else: edge_deltas = edge_deltas + [ deltas ] #[edge_onsets[i+1] - onset] #print(edge_deltas[-1]) else: edge_deltas = edge_deltas + [ min(deltas, energy_range_ev[1] - onset) ] if i > 0: if atomic_numbers[i] == atomic_numbers[i - 1]: #background_ranges = background_ranges + [numpy.array([max(onset - 30.0,edge_onsets[i-1]), onset - 10.0])] background_ranges = background_ranges + [ numpy.array([ max(onset - 30.0, energy_range_ev[0]), onset - 10.0 ]) ] else: background_ranges = background_ranges + [ numpy.array([ max(onset - 30.0, energy_range_ev[0]), onset - 10.0 ]) ] else: background_ranges = background_ranges + [ numpy.array([ max(onset - 30.0, energy_range_ev[0]), onset - 10.0 ]) ] #print(edge_onsets) #print(edge_deltas) #print(background_ranges) stoich, error_in_stoich, quant_data, diff_cross, egrid_ev = EELS_DataAnalysis.stoichiometry_from_eels( eels_spectrum, energy_range_ev, background_ranges, atomic_numbers, edge_onsets, edge_deltas, beam_energy_keV * 1000.0, convergence_angle_mrad / 1000.0, collection_angle_mrad / 1000.0) for iat, atm in enumerate(atomic_numbers): erange = (edge_onsets[iat] - 50.0, edge_onsets[iat] + edge_deltas[iat] + 50) edges = ptable.find_all_edges_in_energy_interval(erange, atm) print(ptable.element_symbol(atm), ':') print('Energy Range: ', edge_onsets[iat], edge_onsets[iat] + edge_deltas[iat]) for edg in edges: edgestr = edg.get_shell_str_in_eels_notation( include_subshell=True) print('\t', edgestr) print('\t', stoich[iat], error_in_stoich[iat]) print('\n\n###############################################') if False: import matplotlib.pyplot as plt e_step = (quant_data[iat][4][1] - quant_data[iat][4][0] ) / quant_data[iat][1][0].size profile_grid = numpy.arange(quant_data[iat][4][0], quant_data[iat][4][1], e_step) plt.plot(profile_grid, quant_data[iat][1][0]) plt.plot(profile_grid, quant_data[iat][3][0]) plt.plot(energies, eels_spectrum) plt.xlim(quant_data[iat][4][0] - 50, quant_data[iat][4][1] + 50) plt.plot(egrid_ev[iat], diff_cross[iat]) plt.show()