Fitting a Stainless Steel XRF Spectrum

Load the necessary libraries.

using NeXLSpectrum
using Gadfly           # For plotting. I've added spectrum support.
using DataFrames, Latexify       # For tables.

Load the spectra from EMSA files.

path = joinpath(@__DIR__, "XRF Stainless")
# We use map to apply `readEMSA` to each of the files
specs = steel, fe, ni, cr, ti, si, s, sn = map(fn->loadspectrum(joinpath(path, fn)), (
  "Steel_50kv_50_ma_Rh_vac_D1.msa",
  "Fe_50kv_50_ma_Rh_vac_D1.msa",
  "Ni_50kv_50_ma_Rh_vac_D1.msa",
  "Cr_50kv_50_ma_Rh_vac_D1.msa",
  "Ti_50kv_50_ma_Rh_vac_D1.msa",
  "Si_50kv_50_ma_Rh_vac_D1.msa",
  "S_50kv_50_ma_Rh_vac_D1.msa",
  "Sn_50kv_50_ma_Rh_vac_D1.msa",));
NameBeamEnergyProbeCurrentLiveTimeRealTimeCoatingIntegralMaterial
Steel50kv50maRhvacD1missingmissing120131.9nothing6.536e+06missing
Fe50kv50maRhvacD1missingmissing120134.8nothing7.644e+06missing
Ni50kv50maRhvacD1missingmissing120137.7nothing8.487e+06missing
Cr50kv50maRhvacD1missingmissing120131.7nothing6.578e+06missing
Ti50kv50maRhvacD1missingmissing120128nothing5.087e+06missing
Si50kv50maRhvacD1missingmissing120121nothing1.862e+06missing
S50kv50maRhvacD1missingmissing120122.2nothing2.455e+06missing
Sn50kv50maRhvacD1missingmissing120121.5nothing2.099e+06missing
plot(specs..., xmax=25.0e3,klms=[n"Fe",n"Cr",n"Ni",n"Ti", n"Si",n"S", n"Mo", n"Rh"])

display(plot(steel,xmax=25.0e3, yscale=1.1,klms=[n"Fe",n"Cr",n"Ni",n"Ti", n"Si",n"S", n"Mo", n"Rh"]))
display(plot(steel,xmax=25.0e3, yscale=0.01,klms=[n"Fe",n"Cr",n"Ni",n"Ti", n"Si",n"S", n"Mo", n"Rh"]))

Build the filtered references which will be fit to the steel unknown.

# This Dict defines which is the lowest z element which can be measured for the K, L, M, N shells
firstelm = Dict(KShell=>n"Na", LShell=>n"Zn", MShell=>n"Sm", NShell=>n"Og")
# Build a detector to match the steel spectrum
det = matching(steel, steel[:FWHMMnKa], 120, firstelm)
# Build a 'VariableWidthFilter' top-hat filter to suit the detector
filt = buildfilter(VariableWidthFilter,det)
refdata = (
  # ( spectrum, element, material ), # The ordering of `refdata` allows us to splat it into `filterreference(...)`
  ( fe, n"Fe", mat"Fe" ),
  ( cr, n"Cr", mat"Cr" ),
  ( ni, n"Ni", mat"Ni" ),
  ( ti, n"Ti", mat"Ti" ),
  ( si, n"Si", mat"Si" ),
  ( s, n"S", mat"S" ),
  ( sn, n"Sn", mat"Sn" ),
)
# Some necessary properties are missing from the spectra so provide them.
xtra = Dict{Symbol,Any}(:BeamEnergy=>40.0e3, :ProbeCurrent=>1.0, :Detector=>det)
refs = mapreduce(append!, refdata) do (sp, el, mat)
  filterreference(filt, sp, el, mat, props=xtra)
end
# Merge the missing properties into the unknown too.
merge!(steel.properties, xtra)
res = fit_spectrum(steel, filt, refs, false)
# Tabulate the results
FitResult(Steel_50kv_50_ma_Rh_vac_D1)
SpectrumFeatureReferenceKdKCountsRefCountsPernAsCountsPernAs
Steel50kv50maRhvacD1k[Cr K-L3 + 5 others, Unspecified]Cr50kv50maRhvacD10.23560.00021511.23e+064.351e+041.025e+04
Steel50kv50maRhvacD1k[Fe K-L3 + 1 other, Unspecified]Fe50kv50maRhvacD10.5570.00032183.034e+064.541e+042.529e+04
Steel50kv50maRhvacD1k[Fe K-M3 + 3 others, Unspecified]Fe50kv50maRhvacD10.57120.0010384.536e+0566193781
Steel50kv50maRhvacD1k[Ni K-L3 + 1 other, Unspecified]Ni50kv50maRhvacD10.04880.00010352.995e+055.114e+042496
Steel50kv50maRhvacD1k[Ni K-M3 + 3 others, Unspecified]Ni50kv50maRhvacD10.049480.00031184.4e+047413366.8
Steel50kv50maRhvacD1k[S K-L3 + 3 others, Unspecified]S50kv50maRhvacD10.0056510.0001165796855048.31
Steel50kv50maRhvacD1k[Si K-L3 + 3 others, Unspecified]Si50kv50maRhvacD10.0023060.000114974.235218.12
Steel50kv50maRhvacD1k[Sn K-L3 + 1 other, Unspecified]Sn50kv50maRhvacD1-1.2e-050.001518-0.2306160.2-0.001922
Steel50kv50maRhvacD1k[Sn K-M3 + 9 others, Unspecified]Sn50kv50maRhvacD1-0.0019640.006831-5.94925.25-0.04959
Steel50kv50maRhvacD1k[Sn L3-M5 + 27 others, Unspecified]Sn50kv50maRhvacD1-2.336e-050.000195-15.655585-0.1305
Steel50kv50maRhvacD1k[Ti K-L3 + 3 others, Unspecified]Ti50kv50maRhvacD10.0047544.537e-051.763e+043.091e+04146.9

Plot the residual spectrum. Note that Mo and Rh were not fit and so there remain significant peaks between 16 and 20 keV.

plot(res)