-
Notifications
You must be signed in to change notification settings - Fork 5
Example: Fit two layers reflectance spectrum (demo)
Here we will demonstrate the capabilities of the fit function to perform over every layer with type ModelFit
. We will create a system with two active layers: the incident medium is air, then a porous silicon layer of 0.7 porosity and 300 nm thick, then a layer of glass with 250 nanometer thickness, and the substrate made of crystalline silicon.
As usual, the light hits the air medium first, goes through the porous layer, then the glass layer and reflect off on the substrate.
The complete code can be found here.
using Plots
pyplot()
closeall()
using Optim
using ThinFilmsTools
Since we do not have a two layer system available, and for simplicity, we create the reflectance spectrum using the TMMOptics
function.
function bilayer_reflectance(beam, incident, emergent)
layers2 = [ LayerTMMO(incident),
LayerTMMO(RI.looyenga([0.70 1.0-0.70],[incident emergent]); d=300.),
LayerTMMO(RIdb.glass(beam.λ./1e3); d=250.),
LayerTMMO(emergent) ]
sol = tmm_optics(beam, layers2)
return vec(beam.p.*sol.Spectra.Rp .+ (1.0 - beam.p).*sol.Spectra.Rs)
end
# Wavelength range [nm]
λ = 250:900
# Angle of incidence [degrees]
θ = [5.]
# Polarisation (1.0 = p, 0.0 = s, between 0.0 and 1.0 = average)
pol = 0.5
beam = PlaneWave(λ, θ; p=pol)
Rexp_norm = bilayer_reflectance(beam, incident, emergent)
We use the same configuration of layers as those created. Notice that we want to fit the porous and glass layers parameters, so we set the second and third layers as ModelFit
, using the Looyenga EMA for the porous layer and the Sellmeier equation to model the glass index of refraction. The thickness of each layer is fitted by default as well.
incident = RIdb.air(beam.λ)
emergent = RIdb.silicon(beam.λ)
layers = [
LayerTMMO(incident),
ModelFit(:looyenga; N=(ninc=incident, nhost=emergent)),
ModelFit(:sellmeier),
LayerTMMO(emergent),
]
Notice the seed
parameter wraps in an array, the two array initial guesses for the two layers of interest. The first one corresponds to the porous layer and the second to the glass layer. The Sellmeier model has six parameters plus the thickness (check RefractiveIndicesModels.jl).
options = Optim.Options(
g_abstol=1e-8, g_reltol=1e-8, iterations=10^6, show_trace=true, store_trace=false,
);
seed = [
[300, 0.7],
vcat(250.0, [1.0, 0.23, 1.0, 6e-3, 2e-2, 1e-2]),
]
solOptim = fit_tmm_optics(
Reflectance(Rexp_norm), seed, beam, layers;
options=options, alg=SAMIN(), lb=0.5.*seed, ub=1.5.*seed,
)
plot(FitSpectrum(), solOptim.beam.λ, solOptim.spectrumExp, solOptim.spectrumFit)
gui()
The results we got are:
julia> solOptim.objfunMin
6.158481787202754e-9
julia> solOptim.optParams
2-element Array{Array{Float64,1},1}:
[299.688596366821, 0.7000206437221619]
[249.54056092622545, 0.626152750665722, 0.11815261899269534, 0.5184304810189039, 0.008015953858193134, 0.010645912702937665, 0.011978284930304835]
Where the first elements of solOptim.optParams
are the thickness and porosity of the Looyenga model for the porous layer, respectively, and the second element corresponds to the thickness and Sellmeier parameters for the glass layer, respectively.