Skip to content

Example: Fit single layer reflectance spectrum with SolutionSpaceEMA

Leandro Acquaroli edited this page Nov 28, 2019 · 4 revisions

Here we have a similar system as in this example, with a porous silicon layer between the incident medium, air, and a crystalline silicon substrate.

We will use the brute force search to explore the solution space in order to check the objective function minima.

The complete code can be found here.

Load modules

using Plots
pyplot()
using ThinFilmsTools
using Optim

Light source

λ = 250:900 # Wavelength range [nm]
θ = [5.] # Angle of incidence [degrees]
pol = 0.5 # Polarisation (1.0 = p, 0.0 = s, between 0.0 and 1.0 = average)
beam = PlaneWave(λ, θ; p=pol)

Layer system

Here we use the indices of refraction of air and silicon from the database and select the Looyenga model for the effective medium approximation, with air and silicon as components of the effective medium.

# Refractive indices of the incident and substrate media
incident = RIdb.air(beam.λ)
emergent = RIdb.silicon(beam.λ)

# Define the RI model to use
layers = [
   LayerTMMO(incident),
   ModelFit(:looyenga; N=(ninc=incident, nhost=emergent)),
   LayerTMMO(emergent),
]

Normalisation of reflectance spectrum

We use the information stored in the SpectraDB database and normalise the reflection measurement to obtain the absolute one.

# Raw measured spectrum stored in Utils
Rexp = SpectraDB.sl2_exp_spectrum(beam.λ)

# Reference measured spectrum stored in Utils
Rref = SpectraDB.sl2_ref_spectrum(beam.λ)

# Theoretical reflectance spectrum for the reference
Rthe = theoretical_spectrum(Reflectance(), beam, incident, emergent)

# Calculate the absolute normalised measured spectra to fit 
Rexp_norm = normalize_reflectance(beam.λ, [beam.λ Rexp], [beam.λ Rthe], [beam.λ Rref])

Solution space exploration

We explore the solution space by selecting a range of values for the optimisation parameters, thickness, and porosity. Remember that space_solution_ema only works with two parameters. We create boundaries using the BoundariesFit structure. Also, when using space_solution_ema, instead of fitting the thickness in the first parameter, we fit the optical thickness, i.e., the thickness times the average index of refraction of the layer over the wavelength. After, we plot the results.

# Boundaries for the space solution and number of grid points
b = BoundariesFit(7000.0, 7300.0, 0.5, 0.65)

# Brute force search
sol = space_solution_ema(Reflectance(Rexp_norm), b, beam, layers)

plot(SpaceSolution(),
     sol.od, sol.p, sol.solSpace,
     xaxis=("Optical thickness [nm]"),
     yaxis=("Porosity");
     num_levels=50,
)
gui()

plot(FitSpectrum(),
     sol.beam.λ, sol.spectrumExp, sol.spectrumFit,
     xaxis=("Wavelength [nm]"),
     yaxis=("Reflectance"),
)
gui()

Solution space

Comparison solution space

Optimisation using Optim

The results from solution space are not optimal but serve as a good initial guess for the optimisation algorithm.

# Take the seed as the output from the 2D search
seed = vcat(sol.optThickness, sol.optParams)

options = Optim.Options(
    g_abstol=1e-8, g_reltol=1e-8, iterations=10^5, store_trace=true, show_trace=false,
)

solOptim = fit_tmm_optics(
     Reflectance(Rexp_norm), [seed], beam, layers;
     options=options, alg=SAMIN(), lb=[0.8.*seed], ub=[1.2.*seed],
)

plot(FitSpectrum(), solOptim.beam.λ, solOptim.spectrumExp, solOptim.spectrumFit)
gui()

Fit results

Clone this wiki locally